feat: 添加配置事件和蓝牙配对模块支持多设备连接
添加了新的配置事件类型用于本地模块配置,包括事件定义和头文件, 以及蓝牙配对模块来管理多个配对设备。更新了CMakeLists.txt以包含 新的源文件,并修改prj.conf增加蓝牙配对数量限制。 - 新增config_event事件类型用于本地配置通信 - 实现ble_bond_module用于管理蓝牙配对和身份切换 - 配置蓝牙最大配对数和身份数为4 - 支持通过配置通道进行设备选择、删除等操作
This commit is contained in:
@@ -14,8 +14,10 @@ zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/events)
|
|||||||
target_sources(app PRIVATE
|
target_sources(app PRIVATE
|
||||||
src/main.c
|
src/main.c
|
||||||
src/events/battery_status_event.c
|
src/events/battery_status_event.c
|
||||||
|
src/events/config_event.c
|
||||||
src/events/mode_event.c
|
src/events/mode_event.c
|
||||||
src/modules/battery_module.c
|
src/modules/battery_module.c
|
||||||
|
src/modules/ble_bond_module.c
|
||||||
src/modules/button_map_module.c
|
src/modules/button_map_module.c
|
||||||
src/modules/mode_switch_module.c
|
src/modules/mode_switch_module.c
|
||||||
src/modules/hids_module.c
|
src/modules/hids_module.c
|
||||||
|
|||||||
2
prj.conf
2
prj.conf
@@ -9,6 +9,8 @@ CONFIG_BT_SMP=y
|
|||||||
CONFIG_BT_DEVICE_NAME="new_kbd"
|
CONFIG_BT_DEVICE_NAME="new_kbd"
|
||||||
CONFIG_BT_DEVICE_APPEARANCE=961
|
CONFIG_BT_DEVICE_APPEARANCE=961
|
||||||
CONFIG_BT_MAX_CONN=1
|
CONFIG_BT_MAX_CONN=1
|
||||||
|
CONFIG_BT_MAX_PAIRED=4
|
||||||
|
CONFIG_BT_ID_MAX=4
|
||||||
CONFIG_SETTINGS=y
|
CONFIG_SETTINGS=y
|
||||||
CONFIG_SETTINGS_NVS=y
|
CONFIG_SETTINGS_NVS=y
|
||||||
CONFIG_NVS=y
|
CONFIG_NVS=y
|
||||||
|
|||||||
18
src/events/config_event.c
Normal file
18
src/events/config_event.c
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include "config_event.h"
|
||||||
|
|
||||||
|
static void log_config_event(const struct app_event_header *aeh)
|
||||||
|
{
|
||||||
|
const struct config_event *event = cast_config_event(aeh);
|
||||||
|
|
||||||
|
APP_EVENT_MANAGER_LOG(aeh, "status:%u %s rcpt:%02x id:%02x",
|
||||||
|
event->status,
|
||||||
|
event->is_request ? "req" : "rsp",
|
||||||
|
event->recipient,
|
||||||
|
event->event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
APP_EVENT_TYPE_DEFINE(config_event,
|
||||||
|
log_config_event,
|
||||||
|
NULL,
|
||||||
|
APP_EVENT_FLAGS_CREATE(
|
||||||
|
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));
|
||||||
54
src/events/config_event.h
Normal file
54
src/events/config_event.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Lightweight config event used for local module configuration.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NEW_KBD_CONFIG_EVENT_H__
|
||||||
|
#define NEW_KBD_CONFIG_EVENT_H__
|
||||||
|
|
||||||
|
#include <app_event_manager.h>
|
||||||
|
#include <app_event_manager_profiler_tracer.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Keep the same local recipient as nRF Desktop config channel. */
|
||||||
|
#define CFG_CHAN_RECIPIENT_LOCAL 0x00
|
||||||
|
|
||||||
|
/* Event ID field layout (compatible with nRF Desktop config_event encoding). */
|
||||||
|
#define MOD_FIELD_POS 4
|
||||||
|
#define MOD_FIELD_SIZE 4
|
||||||
|
#define MOD_FIELD_MASK BIT_MASK(MOD_FIELD_SIZE)
|
||||||
|
#define MOD_FIELD_GET(id) (((id) >> MOD_FIELD_POS) & MOD_FIELD_MASK)
|
||||||
|
|
||||||
|
#define OPT_FIELD_POS 0
|
||||||
|
#define OPT_FIELD_SIZE 4
|
||||||
|
#define OPT_FIELD_MASK BIT_MASK(OPT_FIELD_SIZE)
|
||||||
|
#define OPT_FIELD_GET(id) (((id) >> OPT_FIELD_POS) & OPT_FIELD_MASK)
|
||||||
|
#define OPT_ID_GET(opt) ((opt) - 1U)
|
||||||
|
|
||||||
|
enum config_status {
|
||||||
|
CONFIG_STATUS_SET = 0,
|
||||||
|
CONFIG_STATUS_FETCH,
|
||||||
|
CONFIG_STATUS_SUCCESS,
|
||||||
|
CONFIG_STATUS_REJECT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct config_event {
|
||||||
|
struct app_event_header header;
|
||||||
|
|
||||||
|
uint16_t transport_id;
|
||||||
|
bool is_request;
|
||||||
|
uint8_t event_id;
|
||||||
|
uint8_t recipient;
|
||||||
|
uint8_t status;
|
||||||
|
struct event_dyndata dyndata;
|
||||||
|
};
|
||||||
|
|
||||||
|
APP_EVENT_TYPE_DYNDATA_DECLARE(config_event);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* NEW_KBD_CONFIG_EVENT_H__ */
|
||||||
326
src/modules/ble_bond_module.c
Normal file
326
src/modules/ble_bond_module.c
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
#include <zephyr/bluetooth/bluetooth.h>
|
||||||
|
#include <zephyr/settings/settings.h>
|
||||||
|
|
||||||
|
#include <app_event_manager.h>
|
||||||
|
|
||||||
|
#define MODULE ble_bond
|
||||||
|
#include <caf/events/module_state_event.h>
|
||||||
|
#include <caf/events/ble_common_event.h>
|
||||||
|
#include <caf/events/power_event.h>
|
||||||
|
|
||||||
|
#include "config_event.h"
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||||
|
|
||||||
|
/* Application-visible options carried by config_event. */
|
||||||
|
enum ble_bond_cfg_opt {
|
||||||
|
BLE_BOND_CFG_PEER_SELECT = 0,
|
||||||
|
BLE_BOND_CFG_PEER_ERASE,
|
||||||
|
BLE_BOND_CFG_PEER_ERASE_ALL,
|
||||||
|
BLE_BOND_CFG_OPT_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Module ID in config_event event_id[7:4]. Keep stable for host side tooling. */
|
||||||
|
#define BLE_BOND_CFG_MODULE_ID 0x01
|
||||||
|
|
||||||
|
#define PEER_ID_KEY "peer_id"
|
||||||
|
#define BT_LUT_KEY "bt_lut"
|
||||||
|
|
||||||
|
enum state {
|
||||||
|
STATE_DISABLED,
|
||||||
|
STATE_IDLE,
|
||||||
|
STATE_STANDBY,
|
||||||
|
};
|
||||||
|
|
||||||
|
BUILD_ASSERT(CONFIG_BT_ID_MAX >= 2, "Need at least one resettable identity");
|
||||||
|
|
||||||
|
#define APP_PEER_COUNT (CONFIG_BT_ID_MAX - 1)
|
||||||
|
|
||||||
|
static enum state state = STATE_DISABLED;
|
||||||
|
|
||||||
|
/* app peer id -> bt stack id mapping (default identity 0 is not used). */
|
||||||
|
static uint8_t bt_stack_id_lut[APP_PEER_COUNT];
|
||||||
|
static bool bt_stack_id_lut_valid;
|
||||||
|
|
||||||
|
static uint8_t cur_ble_peer_id;
|
||||||
|
static bool cur_peer_id_valid;
|
||||||
|
|
||||||
|
static uint8_t get_bt_stack_peer_id(uint8_t app_id)
|
||||||
|
{
|
||||||
|
__ASSERT_NO_MSG(app_id < APP_PEER_COUNT);
|
||||||
|
return bt_stack_id_lut[app_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int store_peer_id(uint8_t peer_id)
|
||||||
|
{
|
||||||
|
char key[] = MODULE_NAME "/" PEER_ID_KEY;
|
||||||
|
|
||||||
|
return settings_save_one(key, &peer_id, sizeof(peer_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int store_bt_stack_id_lut(void)
|
||||||
|
{
|
||||||
|
char key[] = MODULE_NAME "/" BT_LUT_KEY;
|
||||||
|
|
||||||
|
return settings_save_one(key, bt_stack_id_lut, sizeof(bt_stack_id_lut));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void submit_peer_op_event(enum peer_operation op, uint8_t app_id)
|
||||||
|
{
|
||||||
|
struct ble_peer_operation_event *event = new_ble_peer_operation_event();
|
||||||
|
|
||||||
|
event->op = op;
|
||||||
|
event->bt_app_id = app_id;
|
||||||
|
event->bt_stack_id = get_bt_stack_peer_id(app_id);
|
||||||
|
APP_EVENT_SUBMIT(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_bt_stack_id_lut(void)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(bt_stack_id_lut); i++) {
|
||||||
|
/* Keep id 0 (BT_ID_DEFAULT) untouched for safe reset/unpair flow. */
|
||||||
|
bt_stack_id_lut[i] = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool storage_data_is_valid(void)
|
||||||
|
{
|
||||||
|
if (!cur_peer_id_valid || !bt_stack_id_lut_valid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur_ble_peer_id >= APP_PEER_COUNT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(bt_stack_id_lut); i++) {
|
||||||
|
if ((bt_stack_id_lut[i] == BT_ID_DEFAULT) ||
|
||||||
|
(bt_stack_id_lut[i] >= CONFIG_BT_ID_MAX)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int settings_set(const char *key, size_t len_rd,
|
||||||
|
settings_read_cb read_cb, void *cb_arg)
|
||||||
|
{
|
||||||
|
ssize_t rc;
|
||||||
|
|
||||||
|
if (!strcmp(key, PEER_ID_KEY)) {
|
||||||
|
if (len_rd != sizeof(cur_ble_peer_id)) {
|
||||||
|
cur_peer_id_valid = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = read_cb(cb_arg, &cur_ble_peer_id, sizeof(cur_ble_peer_id));
|
||||||
|
cur_peer_id_valid = (rc == sizeof(cur_ble_peer_id));
|
||||||
|
} else if (!strcmp(key, BT_LUT_KEY)) {
|
||||||
|
if (len_rd != sizeof(bt_stack_id_lut)) {
|
||||||
|
bt_stack_id_lut_valid = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = read_cb(cb_arg, bt_stack_id_lut, sizeof(bt_stack_id_lut));
|
||||||
|
bt_stack_id_lut_valid = (rc == sizeof(bt_stack_id_lut));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SETTINGS_STATIC_HANDLER_DEFINE(ble_bond, MODULE_NAME, NULL, settings_set, NULL, NULL);
|
||||||
|
|
||||||
|
static void load_identities(void)
|
||||||
|
{
|
||||||
|
bt_addr_le_t addrs[CONFIG_BT_ID_MAX];
|
||||||
|
size_t count = ARRAY_SIZE(addrs);
|
||||||
|
|
||||||
|
bt_id_get(addrs, &count);
|
||||||
|
|
||||||
|
for (; count < CONFIG_BT_ID_MAX; count++) {
|
||||||
|
int err = bt_id_create(NULL, NULL);
|
||||||
|
if (err < 0) {
|
||||||
|
LOG_ERR("Cannot create identity (err:%d)", err);
|
||||||
|
module_set_state(MODULE_STATE_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int erase_peer(uint8_t app_id)
|
||||||
|
{
|
||||||
|
uint8_t stack_id = get_bt_stack_peer_id(app_id);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Tell ble_adv to restart advertising session for this identity. */
|
||||||
|
submit_peer_op_event(PEER_OPERATION_ERASE_ADV, app_id);
|
||||||
|
|
||||||
|
err = bt_unpair(stack_id, NULL);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Cannot unpair id %u (err:%d)", stack_id, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bt_id_reset(stack_id, NULL, NULL);
|
||||||
|
if (err < 0) {
|
||||||
|
LOG_ERR("Cannot reset id %u (err:%d)", stack_id, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
submit_peer_op_event(PEER_OPERATION_ERASED, app_id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int erase_all_peers(void)
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < APP_PEER_COUNT; i++) {
|
||||||
|
int err = erase_peer(i);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool handle_config_event(const struct config_event *event)
|
||||||
|
{
|
||||||
|
if (!event->is_request || (event->recipient != CFG_CHAN_RECIPIENT_LOCAL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MOD_FIELD_GET(event->event_id) != BLE_BOND_CFG_MODULE_ID) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct config_event *rsp = new_config_event(0);
|
||||||
|
rsp->transport_id = event->transport_id;
|
||||||
|
rsp->is_request = false;
|
||||||
|
rsp->event_id = event->event_id;
|
||||||
|
rsp->recipient = event->recipient;
|
||||||
|
rsp->status = CONFIG_STATUS_REJECT;
|
||||||
|
|
||||||
|
if (event->status == CONFIG_STATUS_SET) {
|
||||||
|
uint8_t opt_field = OPT_FIELD_GET(event->event_id);
|
||||||
|
uint8_t opt_id = (opt_field == 0) ? UINT8_MAX : OPT_ID_GET(opt_field);
|
||||||
|
|
||||||
|
switch (opt_id) {
|
||||||
|
case BLE_BOND_CFG_PEER_SELECT:
|
||||||
|
if (event->dyndata.size >= 1) {
|
||||||
|
uint8_t peer_id = event->dyndata.data[0];
|
||||||
|
if (peer_id < APP_PEER_COUNT) {
|
||||||
|
cur_ble_peer_id = peer_id;
|
||||||
|
if (!store_peer_id(cur_ble_peer_id)) {
|
||||||
|
submit_peer_op_event(PEER_OPERATION_SELECTED, cur_ble_peer_id);
|
||||||
|
rsp->status = CONFIG_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BLE_BOND_CFG_PEER_ERASE:
|
||||||
|
if (!erase_peer(cur_ble_peer_id)) {
|
||||||
|
rsp->status = CONFIG_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BLE_BOND_CFG_PEER_ERASE_ALL:
|
||||||
|
if (!erase_all_peers()) {
|
||||||
|
rsp->status = CONFIG_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (event->status == CONFIG_STATUS_FETCH) {
|
||||||
|
uint8_t opt_field = OPT_FIELD_GET(event->event_id);
|
||||||
|
uint8_t opt_id = (opt_field == 0) ? UINT8_MAX : OPT_ID_GET(opt_field);
|
||||||
|
|
||||||
|
if (opt_id == BLE_BOND_CFG_PEER_SELECT) {
|
||||||
|
struct config_event *rsp_data = new_config_event(1);
|
||||||
|
rsp_data->transport_id = event->transport_id;
|
||||||
|
rsp_data->is_request = false;
|
||||||
|
rsp_data->event_id = event->event_id;
|
||||||
|
rsp_data->recipient = event->recipient;
|
||||||
|
rsp_data->status = CONFIG_STATUS_SUCCESS;
|
||||||
|
rsp_data->dyndata.data[0] = cur_ble_peer_id;
|
||||||
|
APP_EVENT_SUBMIT(rsp_data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
APP_EVENT_SUBMIT(rsp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_after_settings_loaded(void)
|
||||||
|
{
|
||||||
|
load_identities();
|
||||||
|
if (state == STATE_DISABLED) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!storage_data_is_valid()) {
|
||||||
|
cur_ble_peer_id = 0;
|
||||||
|
init_bt_stack_id_lut();
|
||||||
|
|
||||||
|
if (store_peer_id(cur_ble_peer_id) || store_bt_stack_id_lut()) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = STATE_IDLE;
|
||||||
|
submit_peer_op_event(PEER_OPERATION_SELECTED, cur_ble_peer_id);
|
||||||
|
module_set_state(MODULE_STATE_READY);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool app_event_handler(const struct app_event_header *aeh)
|
||||||
|
{
|
||||||
|
if (is_module_state_event(aeh)) {
|
||||||
|
const struct module_state_event *event = cast_module_state_event(aeh);
|
||||||
|
|
||||||
|
if (check_state(event, MODULE_ID(settings_loader), MODULE_STATE_READY) &&
|
||||||
|
(state == STATE_DISABLED)) {
|
||||||
|
if (init_after_settings_loaded()) {
|
||||||
|
module_set_state(MODULE_STATE_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_power_down_event(aeh)) {
|
||||||
|
if (state == STATE_IDLE) {
|
||||||
|
state = STATE_STANDBY;
|
||||||
|
module_set_state(MODULE_STATE_OFF);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_wake_up_event(aeh)) {
|
||||||
|
if (state == STATE_STANDBY) {
|
||||||
|
state = STATE_IDLE;
|
||||||
|
module_set_state(MODULE_STATE_READY);
|
||||||
|
submit_peer_op_event(PEER_OPERATION_SELECTED, cur_ble_peer_id);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_config_event(aeh)) {
|
||||||
|
return handle_config_event(cast_config_event(aeh));
|
||||||
|
}
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
APP_EVENT_LISTENER(MODULE, app_event_handler);
|
||||||
|
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
|
||||||
|
APP_EVENT_SUBSCRIBE(MODULE, power_down_event);
|
||||||
|
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);
|
||||||
|
APP_EVENT_SUBSCRIBE_EARLY(MODULE, config_event);
|
||||||
Reference in New Issue
Block a user