/** * @file app_ble.c * @brief BLE application layer implementation, derived from the SDK rdtss * demo (app_ble.c) and trimmed to a single custom link service (CLS). * * NOTE: ke_msg / ke_timer may only run in the BLE schedule task context * (SDK constraint). rwip_schedule() is therefore only called from the * dedicated FreeRTOS task created here. * * TODO: ns_sleep() low-power mode is intentionally NOT used: it conflicts * with the FreeRTOS tick. Revisit when sleep support is required. */ #include #include #include "n32wb03x.h" #include "rwip.h" #include "gapm_task.h" // GAP Manager Task API #include "ns_ble.h" #include "ns_sec.h" #include "app_ble.h" #include "app_cls.h" #include "app_ble_proto.h" #include "app_user_config.h" #include "FreeRTOS.h" #include "task.h" #include "bsp_usart.h" /* Private define ------------------------------------------------------------*/ #define BLE_SCHED_TASK_STACK (512) /* stack in words */ #define BLE_SCHED_TASK_PRIORITY (2) /* Private variables ---------------------------------------------------------*/ static uint8_t s_ble_connected = 0; /* Private function prototypes -----------------------------------------------*/ static void app_ble_connected(void); static void app_ble_disconnected(void); static void ble_schedule_task(void *argument); /* Private functions ---------------------------------------------------------*/ /** * @brief ble message handler */ static void app_ble_msg_handler(struct ble_msg_t const *p_ble_msg) { switch (p_ble_msg->msg_id) { case APP_BLE_OS_READY: break; case APP_BLE_GAP_CONNECTED: app_ble_connected(); break; case APP_BLE_GAP_DISCONNECTED: app_ble_disconnected(); break; case APP_BLE_GATTC_MTU_IND: bsp_usart_tx_lock(); printf("[BLE] MTU updated: %u\n", (unsigned int)app_env.max_mtu); bsp_usart_tx_unlock(); break; default: break; } } /** * @brief advertising message handler */ static void app_ble_adv_msg_handler(enum app_adv_mode adv_mode) { switch (adv_mode) { case APP_ADV_MODE_DIRECTED: break; case APP_ADV_MODE_FAST: break; case APP_ADV_MODE_SLOW: break; default: break; } } /** * @brief ble GAP initialization */ static void app_ble_gap_params_init(void) { struct ns_gap_params_t dev_info = {0}; uint8_t *p_mac = SystemGetMacAddr(); //get UUID from trim stored if (p_mac != NULL) { //set the uuid as mac address memcpy(dev_info.mac_addr.addr, p_mac, BD_ADDR_LEN); } else { memcpy(dev_info.mac_addr.addr, "\x01\x02\x03\x04\x05\x06", BD_ADDR_LEN); } /* init params*/ dev_info.mac_addr_type = GAPM_STATIC_ADDR; dev_info.appearance = 0; dev_info.dev_role = GAP_ROLE_PERIPHERAL; dev_info.dev_name_len = sizeof(CUSTOM_DEVICE_NAME)-1; memcpy(dev_info.dev_name, CUSTOM_DEVICE_NAME, dev_info.dev_name_len); dev_info.dev_conn_param.intv_min = MSECS_TO_UNIT(MIN_CONN_INTERVAL, MSECS_UNIT_1_25_MS); dev_info.dev_conn_param.intv_max = MSECS_TO_UNIT(MAX_CONN_INTERVAL, MSECS_UNIT_1_25_MS); dev_info.dev_conn_param.latency = SLAVE_LATENCY; dev_info.dev_conn_param.time_out = MSECS_TO_UNIT(CONN_SUP_TIMEOUT, MSECS_UNIT_10_MS); dev_info.conn_param_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY; ns_ble_gap_init(&dev_info); } /** * @brief ble advertising initialization */ static void app_ble_adv_init(void) { struct ns_adv_params_t user_adv = {0}; //init advertising data user_adv.adv_data_len = CUSTOM_USER_ADVERTISE_DATA_LEN; memcpy(user_adv.adv_data, CUSTOM_USER_ADVERTISE_DATA, CUSTOM_USER_ADVERTISE_DATA_LEN); user_adv.scan_rsp_data_len = CUSTOM_USER_ADV_SCNRSP_DATA_LEN; memcpy(user_adv.scan_rsp_data, CUSTOM_USER_ADV_SCNRSP_DATA, CUSTOM_USER_ADV_SCNRSP_DATA_LEN); user_adv.attach_appearance = false; user_adv.attach_name = true; user_adv.ex_adv_enable = false; user_adv.adv_phy = PHY_1MBPS_VALUE; //init advertising params user_adv.directed_adv.enable = false; user_adv.fast_adv.enable = true; user_adv.fast_adv.duration = CUSTOM_ADV_FAST_DURATION; user_adv.fast_adv.adv_intv = CUSTOM_ADV_FAST_INTERVAL; user_adv.slow_adv.enable = true; user_adv.slow_adv.duration = CUSTOM_ADV_SLOW_DURATION; user_adv.slow_adv.adv_intv = CUSTOM_ADV_SLOW_INTERVAL; user_adv.ble_adv_msg_handler = app_ble_adv_msg_handler; ns_ble_adv_init(&user_adv); } /** * @brief ble security initialization */ static void app_ble_sec_init(void) { struct ns_sec_init_t sec_init = {0}; sec_init.rand_pin_enable = false; sec_init.pin_code = 123456; sec_init.pairing_feat.auth = ( SEC_PARAM_BOND | (SEC_PARAM_MITM<<2) | (SEC_PARAM_LESC<<3) | (SEC_PARAM_KEYPRESS<<4) ); sec_init.pairing_feat.iocap = SEC_PARAM_IO_CAPABILITIES; sec_init.pairing_feat.key_size = SEC_PARAM_KEY_SIZE; sec_init.pairing_feat.oob = SEC_PARAM_OOB; sec_init.pairing_feat.ikey_dist = SEC_PARAM_IKEY; sec_init.pairing_feat.rkey_dist = SEC_PARAM_RKEY; sec_init.pairing_feat.sec_req = SEC_PARAM_SEC_MODE_LEVEL; sec_init.bond_enable = BOND_STORE_ENABLE; sec_init.bond_db_addr = BOND_DATA_BASE_ADDR; sec_init.bond_max_peer = MAX_BOND_PEER; sec_init.bond_sync_delay = 2000; sec_init.ns_sec_msg_handler = NULL; ns_sec_init(&sec_init); } /** * @brief register the custom link service profile */ static void app_ble_prf_init(void) { //add custom link server (cls) ns_ble_add_prf_func_register(app_cls_add_cls); } /** * @brief BLE schedule task: the only context allowed to run ke_msg/ke_timer. */ static void ble_schedule_task(void *argument) { (void)argument; for (;;) { /*schedule all pending events*/ rwip_schedule(); /*flush protocol pending TX frames (notify busy retry)*/ app_ble_proto_poll(); vTaskDelay(pdMS_TO_TICKS(1)); } } /** * @brief ble initialization: stack, parameters, profile, advertising, task. */ void app_ble_init(void) { struct ns_stack_cfg_t app_handler = {0}; /* Debug aid: the rwip kernel heaps are described by ROM-managed cells * (absolute symbols from symbol_g15.obj, initialized via the vendor * .data init values). Dump them so a bad/zero heap descriptor is * immediately visible on the console. */ printf("[BLE] rwip heap cells: env=%08lX/%u db=%08lX/%u msg=%08lX/%u nret=%08lX/%u\n", (unsigned long)g_rwip_heap_env, (unsigned int)g_rwip_heap_env_size, (unsigned long)g_rwip_heap_db, (unsigned int)g_rwip_heap_db_size, (unsigned long)g_rwip_heap_msg, (unsigned long)g_rwip_heap_msg_size, (unsigned long)g_rwip_heap_non_ret, (unsigned long)g_rwip_heap_non_ret_size); app_handler.ble_msg_handler = app_ble_msg_handler; app_handler.user_msg_handler = NULL; app_handler.lsc_cfg = BLE_LSC_LSI_32000HZ; //initialization ble stack ns_ble_stack_init(&app_handler); app_ble_gap_params_init(); app_ble_sec_init(); app_ble_adv_init(); app_ble_prf_init(); /* frame protocol frontend: CLI-over-BLE + OTA receiver */ app_ble_proto_init(); app_ble_set_rx_callback(app_ble_proto_rx_bytes); //start adv ns_ble_adv_start(); /* Create the BLE schedule task (rwip_schedule loop) */ xTaskCreate(ble_schedule_task, "BLE", BLE_SCHED_TASK_STACK, NULL, BLE_SCHED_TASK_PRIORITY, NULL); } /** * @brief ble connected */ static void app_ble_connected(void) { s_ble_connected = 1; app_cls_notify_state_reset(); bsp_usart_tx_lock(); printf("[BLE] connected, conidx=%u\n", (unsigned int)app_env.conidx); bsp_usart_tx_unlock(); } /** * @brief ble disconnected */ static void app_ble_disconnected(void) { s_ble_connected = 0; app_cls_notify_state_reset(); app_ble_proto_on_disconnect(); /* abort OTA session, drop pending TX */ bsp_usart_tx_lock(); printf("[BLE] disconnected, restart advertising\n"); bsp_usart_tx_unlock(); // Restart Advertising ns_ble_adv_start(); } /** * @brief uplink notify send interface (single packet, busy -> -1) */ int app_ble_notify_send(const uint8_t *data, uint16_t len) { return app_cls_send_notify(data, len); } /** * @brief register downlink data callback */ void app_ble_set_rx_callback(void (*cb)(const uint8_t*, uint16_t)) { app_cls_set_rx_callback((app_cls_rx_cb_t)cb); } /** * @brief BLE connection state */ uint8_t app_ble_is_connected(void) { return s_ble_connected; } /** * @brief Max payload per BLE notification packet (att_mtu-3) */ uint16_t app_ble_max_payload(void) { if (!s_ble_connected || app_env.max_mtu < 23u) { return 20u - 3u; /* default ATT MTU 23 */ } return (uint16_t)(app_env.max_mtu - 3u); }