/********************************************************************************************* * Filename : tuya_demo.c * Description : * Author : * Email : zh-jieli.com * Last modifiled : 2017-01-17 11:14 * Copyright:(c)JIELI 2011-2016 @ , All Rights Reserved. *********************************************************************************************/ // ***************************************************************************** /* EXAMPLE_START(le_counter): LE Peripheral - Heartbeat Counter over GATT * * @text All newer operating systems provide GATT Client functionality. * The LE Counter examples demonstrates how to specify a minimal GATT Database * with a custom GATT Service and a custom Characteristic that sends periodic * notifications. */ // ***************************************************************************** #include "system/app_core.h" #include "system/includes.h" #include "app_config.h" #include "app_action.h" #include "btstack/btstack_task.h" #include "btstack/bluetooth.h" #include "user_cfg.h" #include "vm.h" #include "btcontroller_modules.h" #include "bt_common.h" #include "3th_profile_api.h" #include "le_common.h" #include "rcsp_bluetooth.h" #include "JL_rcsp_api.h" #include "custom_cfg.h" #include "tuya_demo.h" #include "tuya_ble_api.h" #if (TCFG_BLE_DEMO_SELECT == DEF_BLE_DEMO_TUYA) //TRANS ANCS #define TRANS_ANCS_EN 0 #if TRANS_ANCS_EN #include "btstack/btstack_event.h" #endif #define TEST_SEND_HANDLE_VAL ATT_CHARACTERISTIC_ae02_01_VALUE_HANDLE /* #define TEST_SEND_HANDLE_VAL ATT_CHARACTERISTIC_ae05_01_VALUE_HANDLE */ #define EXT_ADV_MODE_EN 0 #define TEST_AUDIO_DATA_UPLOAD 0 //测试文件上传 #if 1//LE_DEBUG_PRINT_EN extern void printf_buf(u8 *buf, u32 len); /* #define log_info printf */ #define log_info(x, ...) printf("[tuya_demo]" x " ", ## __VA_ARGS__) #define log_info_hexdump printf_buf #else #define log_info(...) #define log_info_hexdump(...) #endif /* #define LOG_TAG_CONST BT_BLE */ /* #define LOG_TAG "[LE_S_DEMO]" */ /* #define LOG_ERROR_ENABLE */ /* #define LOG_DEBUG_ENABLE */ /* #define LOG_INFO_ENABLE */ /* #define LOG_DUMP_ENABLE */ /* #define LOG_CLI_ENABLE */ /* #include "debug.h" */ //------ //ATT发送的包长, note: 20 <=need >= MTU #if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x04) #define ATT_LOCAL_MTU_SIZE (246) // //ATT缓存的buffer大小, note: need >= 20,可修改 #define ATT_SEND_CBUF_SIZE (246) // #else #define ATT_LOCAL_MTU_SIZE (23) // //ATT缓存的buffer大小, note: need >= 20,可修改 #define ATT_SEND_CBUF_SIZE (246) // #endif //共配置的RAM #define ATT_RAM_BUFSIZE 2 * (ATT_CTRL_BLOCK_SIZE + ATT_LOCAL_MTU_SIZE + ATT_SEND_CBUF_SIZE) //note: static u8 att_ram_buffer[ATT_RAM_BUFSIZE] __attribute__((aligned(4))); //--------------- /* 打开流控使能后,确定使能接口 att_server_flow_enable 被调用 然后使用过程 通过接口 att_server_flow_hold 来控制流控开关 注意:流控只能控制对方使用带响应READ/WRITE等命令方式 例如:ATT_WRITE_REQUEST = 0x12 */ #define ATT_DATA_RECIEVT_FLOW 0//流控功能使能 //--------------- // 广播周期 (unit:0.625ms) #define ADV_INTERVAL_MIN (160) #define HOLD_LATENCY_CNT_MIN (3) //(0~0xffff) #define HOLD_LATENCY_CNT_MAX (15) //(0~0xffff) #define HOLD_LATENCY_CNT_ALL (0xffff) static volatile hci_con_handle_t con_handle; //加密设置 /* static const uint8_t sm_min_key_size = 7; */ //连接参数更新请求设置 //是否使能参数请求更新,0--disable, 1--enable static const uint8_t connection_update_enable = 1; ///0--disable, 1--enable //当前请求的参数表index static uint8_t connection_update_cnt = 0; // //参数表 static const struct conn_update_param_t connection_param_table[] = { {16, 24, 10, 600},//11 {12, 28, 10, 600},//3.7 {8, 20, 10, 600}, /* {12, 28, 4, 600},//3.7 */ /* {12, 24, 30, 600},//3.05 */ }; //共可用的参数组数 #define CONN_PARAM_TABLE_CNT (sizeof(connection_param_table)/sizeof(struct conn_update_param_t)) #if (ATT_RAM_BUFSIZE < 64) #error "adv_data & rsp_data buffer error!!!!!!!!!!!!" #endif //用户可配对的,这是样机跟客户开发的app配对的秘钥 /* const u8 link_key_data[16] = {0x06, 0x77, 0x5f, 0x87, 0x91, 0x8d, 0xd4, 0x23, 0x00, 0x5d, 0xf1, 0xd8, 0xcf, 0x0c, 0x14, 0x2b}; */ #define EIR_TAG_STRING 0xd6, 0x05, 0x08, 0x00, 'J', 'L', 'A', 'I', 'S', 'D','K' static const char user_tag_string[] = {EIR_TAG_STRING}; static u8 adv_data_len; static u8 adv_data[ADV_RSP_PACKET_MAX];//max is 31 static u8 scan_rsp_data_len; static u8 scan_rsp_data[ADV_RSP_PACKET_MAX];//max is 31 /* #define adv_data &att_ram_buffer[0] */ /* #define scan_rsp_data &att_ram_buffer[32] */ static char gap_device_name[BT_NAME_LEN_MAX] = "jl_ble_test"; static u8 gap_device_name_len = 0; //名字长度,不包含结束符 static u8 ble_work_state = 0; //ble 状态变化 static u8 adv_ctrl_en; //广播控制 static u8 test_read_write_buf[4]; static void (*app_recieve_callback)(void *priv, void *buf, u16 len) = NULL; static void (*app_ble_state_callback)(void *priv, ble_state_e state) = NULL; static void (*ble_resume_send_wakeup)(void) = NULL; static u32 channel_priv; static int app_send_user_data_check(u16 len); int tuya_app_send_user_data_do(void *priv, u8 *data, u16 len); static int app_send_user_data(u16 handle, u8 *data, u16 len, u8 handle_type); // Complete Local Name 默认的蓝牙名字 //------------------------------------------------------ //广播参数设置 static void advertisements_setup_init(); static int set_adv_enable(void *priv, u32 en); static int get_buffer_vaild_len(void *priv); extern const char *bt_get_local_name(); extern void clr_wdt(void); extern void sys_auto_shut_down_disable(void); extern void sys_auto_shut_down_enable(void); extern u8 get_total_connect_dev(void); extern u8 JL_tuya_ble_gatt_receive_data(u8 *p_data, u16 len); extern const uint8_t tuya_mac_addr[6]; //------------------------------------------------------ //NACS #if TRANS_ANCS_EN #define ANCS_SUBEVENT_CLIENT_NOTIFICATION 0xF1 void ancs_client_init(void); void ancs_client_register_callback(btstack_packet_handler_t callback); const char *ancs_client_attribute_name_for_id(int id); void ancs_set_notification_buffer(u8 *buffer, u16 buffer_size); //ancs info buffer #define ANCS_INFO_BUFFER_SIZE (1024) static u8 ancs_info_buffer[ANCS_INFO_BUFFER_SIZE]; #endif //------------------------------------------------------ static void send_request_connect_parameter(u8 table_index) { struct conn_update_param_t *param = (void *)&connection_param_table[table_index];//static ram log_info("update_request:-%d-%d-%d-%d-\n", param->interval_min, param->interval_max, param->latency, param->timeout); if (con_handle) { ble_op_conn_param_request(con_handle, param); } } static void check_connetion_updata_deal(void) { if (connection_update_enable) { if (connection_update_cnt < CONN_PARAM_TABLE_CNT) { send_request_connect_parameter(connection_update_cnt); } } } static void connection_update_complete_success(u8 *packet) { int con_handle, conn_interval, conn_latency, conn_timeout; con_handle = hci_subevent_le_connection_update_complete_get_connection_handle(packet); conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet); conn_latency = hci_subevent_le_connection_update_complete_get_conn_latency(packet); conn_timeout = hci_subevent_le_connection_update_complete_get_supervision_timeout(packet); log_info("conn_interval = %d\n", conn_interval); log_info("conn_latency = %d\n", conn_latency); log_info("conn_timeout = %d\n", conn_timeout); } static void set_ble_work_state(ble_state_e state) { if (state != ble_work_state) { log_info("ble_work_st:%x->%x\n", ble_work_state, state); ble_work_state = state; if (app_ble_state_callback) { app_ble_state_callback((void *)channel_priv, state); } } } static ble_state_e get_ble_work_state(void) { return ble_work_state; } static void cbk_sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { sm_just_event_t *event = (void *)packet; u32 tmp32; switch (packet_type) { case HCI_EVENT_PACKET: switch (hci_event_packet_get_type(packet)) { case SM_EVENT_JUST_WORKS_REQUEST: sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); log_info("Just Works Confirmed.\n"); break; case SM_EVENT_PASSKEY_DISPLAY_NUMBER: log_info_hexdump(packet, size); memcpy(&tmp32, event->data, 4); log_info("Passkey display: %06u.\n", tmp32); break; } break; } } static void can_send_now_wakeup(void) { /* putchar('E'); */ if (ble_resume_send_wakeup) { ble_resume_send_wakeup(); } } static void ble_auto_shut_down_enable(u8 enable) { #if TCFG_AUTO_SHUT_DOWN_TIME if (enable) { if (get_total_connect_dev() == 0) { //已经没有设备连接 sys_auto_shut_down_enable(); } } else { sys_auto_shut_down_disable(); } #endif } const char *const phy_result[] = { "None", "1M", "2M", "Coded", }; static void set_connection_data_length(u16 tx_octets, u16 tx_time) { if (con_handle) { ble_op_set_data_length(con_handle, tx_octets, tx_time); } } static void set_connection_data_phy(u8 tx_phy, u8 rx_phy) { if (0 == con_handle) { return; } u8 all_phys = 0; u16 phy_options = CONN_SET_PHY_OPTIONS_S8; ble_op_set_ext_phy(con_handle, all_phys, tx_phy, rx_phy, phy_options); } static void server_profile_start(u16 con_handle) { #if BT_FOR_APP_EN set_app_connect_type(TYPE_BLE); #endif ble_op_att_send_init(con_handle, att_ram_buffer, ATT_RAM_BUFSIZE, ATT_LOCAL_MTU_SIZE); set_ble_work_state(BLE_ST_CONNECT); ble_auto_shut_down_enable(0); /* set_connection_data_phy(CONN_SET_CODED_PHY, CONN_SET_CODED_PHY); */ } _WEAK_ u8 ble_update_get_ready_jump_flag(void) { return 0; } /* * @section Packet Handler * * @text The packet handler is used to: * - stop the counter after a disconnect * - send a notification when the requested ATT_EVENT_CAN_SEND_NOW is received */ /* LISTING_START(packetHandler): Packet Handler */ static void cbk_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { int mtu; u32 tmp; u8 status; const char *attribute_name; switch (packet_type) { case HCI_EVENT_PACKET: switch (hci_event_packet_get_type(packet)) { /* case DAEMON_EVENT_HCI_PACKET_SENT: */ /* break; */ case ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE: log_info("ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE\n"); case ATT_EVENT_CAN_SEND_NOW: can_send_now_wakeup(); break; case HCI_EVENT_LE_META: switch (hci_event_le_meta_get_subevent_code(packet)) { case HCI_SUBEVENT_LE_ENHANCED_CONNECTION_COMPLETE: status = hci_subevent_le_enhanced_connection_complete_get_status(packet); if (status) { log_info("LE_SLAVE CONNECTION FAIL!!! %0x\n", status); set_ble_work_state(BLE_ST_DISCONN); break; } con_handle = hci_subevent_le_enhanced_connection_complete_get_connection_handle(packet); log_info("HCI_SUBEVENT_LE_ENHANCED_CONNECTION_COMPLETE : %0x\n", con_handle); log_info("conn_interval = %d\n", hci_subevent_le_enhanced_connection_complete_get_conn_interval(packet)); log_info("conn_latency = %d\n", hci_subevent_le_enhanced_connection_complete_get_conn_latency(packet)); log_info("conn_timeout = %d\n", hci_subevent_le_enhanced_connection_complete_get_supervision_timeout(packet)); server_profile_start(con_handle); break; case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: con_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); log_info("HCI_SUBEVENT_LE_CONNECTION_COMPLETE: %0x\n", con_handle); connection_update_complete_success(packet + 8); server_profile_start(con_handle); log_info("ble remote rssi= %d\n", ble_vendor_get_peer_rssi(con_handle)); log_info("tuya_ble_connection_handler\n"); tuya_ble_connected_handler(); break; case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE: printf("HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE"); connection_update_complete_success(packet); break; case HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE: log_info("APP HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE\n"); /* set_connection_data_phy(CONN_SET_CODED_PHY, CONN_SET_CODED_PHY); */ break; case HCI_SUBEVENT_LE_PHY_UPDATE_COMPLETE: log_info("APP HCI_SUBEVENT_LE_PHY_UPDATE %s\n", hci_event_le_meta_get_phy_update_complete_status(packet) ? "Fail" : "Succ"); log_info("Tx PHY: %s\n", phy_result[hci_event_le_meta_get_phy_update_complete_tx_phy(packet)]); log_info("Rx PHY: %s\n", phy_result[hci_event_le_meta_get_phy_update_complete_rx_phy(packet)]); break; } break; case HCI_EVENT_DISCONNECTION_COMPLETE: log_info("HCI_EVENT_DISCONNECTION_COMPLETE: %0x\n", packet[5]); con_handle = 0; ble_op_att_send_init(con_handle, 0, 0, 0); set_ble_work_state(BLE_ST_DISCONN); connection_update_cnt = 0; #if BT_FOR_APP_EN set_app_connect_type(TYPE_NULL); #endif ble_auto_shut_down_enable(1); log_info("tuya_ble_disconnection_handler\n"); tuya_ble_disconnected_handler(); extern void tuya_ble_adv_change(void); tuya_ble_adv_change(); break; case ATT_EVENT_MTU_EXCHANGE_COMPLETE: mtu = att_event_mtu_exchange_complete_get_MTU(packet) - 3; log_info("ATT MTU = %u\n", mtu); ble_op_att_set_send_mtu(mtu); /* set_connection_data_length(251, 2120); */ break; case HCI_EVENT_VENDOR_REMOTE_TEST: log_info("--- HCI_EVENT_VENDOR_REMOTE_TEST\n"); break; case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE: tmp = little_endian_read_16(packet, 4); log_info("-update_rsp: %02x\n", tmp); if (tmp) { connection_update_cnt++; log_info("remoter reject!!!\n"); check_connetion_updata_deal(); } else { connection_update_cnt = CONN_PARAM_TABLE_CNT; } break; case HCI_EVENT_ENCRYPTION_CHANGE: log_info("HCI_EVENT_ENCRYPTION_CHANGE= %d\n", packet[2]); break; #if TRANS_ANCS_EN case HCI_EVENT_ANCS_META: switch (hci_event_ancs_meta_get_subevent_code(packet)) { case ANCS_SUBEVENT_CLIENT_NOTIFICATION: printf("ANCS_SUBEVENT_CLIENT_NOTIFICATION \n"); attribute_name = ancs_client_attribute_name_for_id(ancs_subevent_client_notification_get_attribute_id(packet)); if (!attribute_name) { printf("ancs unknow attribute_id :%d \n", ancs_subevent_client_notification_get_attribute_id(packet)); break; } else { u16 attribute_strlen = little_endian_read_16(packet, 7); u8 *attribute_str = (void *)little_endian_read_32(packet, 9); printf("Notification: %s - %s \n", attribute_name, attribute_str); } break; default: break; } break; #endif } break; } } /* LISTING_END */ /* * @section ATT Read * * @text The ATT Server handles all reads to constant data. For dynamic data like the custom characteristic, the registered * att_read_callback is called. To handle long characteristics and long reads, the att_read_callback is first called * with buffer == NULL, to request the total value length. Then it will be called again requesting a chunk of the value. * See Listing attRead. */ /* LISTING_END */ /* * @section ATT Write * * @text The only valid ATT write in this example is to the Client Characteristic Configuration, which configures notification * and indication. If the ATT handle matches the client configuration handle, the new configuration value is stored and used * in the heartbeat handler to decide if a new value should be sent. See Listing attWrite. */ /* LISTING_START(attWrite): ATT Write */ static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { int result = 0; u16 tmp16; u8 *tmp_buf = NULL; u16 handle = att_handle; //log_info("write_callback, handle= 0x%04x,size = %d\n", handle, buffer_size); //put_buf(buffer, buffer_size); switch (handle) { #if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x03) case ATT_CHARACTERISTIC_2b10_01_CLIENT_CONFIGURATION_HANDLE: set_ble_work_state(BLE_ST_NOTIFY_IDICATE); check_connetion_updata_deal(); printf("\n------write ccc:%04x,%02x\n", handle, buffer[0]); att_set_ccc_config(handle, buffer[0]); break; case ATT_CHARACTERISTIC_2b11_01_VALUE_HANDLE: printf("TUYA msg receive, handle = 0x%x, size = %d\n", handle, buffer_size); JL_tuya_ble_gatt_receive_data(buffer, buffer_size); break; #endif #if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x04) case ATT_CHARACTERISTIC_00000002_0000_1001_8001_00805F9B07D0_01_CLIENT_CONFIGURATION_HANDLE: set_ble_work_state(BLE_ST_NOTIFY_IDICATE); check_connetion_updata_deal(); printf("\n------write ccc:%04x,%02x\n", handle, buffer[0]); att_set_ccc_config(handle, buffer[0]); break; case ATT_CHARACTERISTIC_00000001_0000_1001_8001_00805F9B07D0_01_VALUE_HANDLE: printf("\nTUYA msg receive, handle = 0x%x, size = %d\n", handle, buffer_size); JL_tuya_ble_gatt_receive_data(buffer, buffer_size); break; case ATT_CHARACTERISTIC_00000003_0000_1001_8001_00805F9B07D0_01_VALUE_HANDLE: printf("\nTUYA read msg, handle = 0x%x, size = %d\n", handle, buffer_size); put_buf(buffer, buffer_size); break; #endif default: printf("\n\nunknow handle:%d\n\n", handle); break; } return 0; } static int app_send_user_data(u16 handle, u8 *data, u16 len, u8 handle_type) { u32 ret = APP_BLE_NO_ERROR; if (!con_handle) { return APP_BLE_OPERATION_ERROR; } if (!att_get_ccc_config(handle + 1)) { log_info("fail,no write ccc!!!,%04x\n", handle + 1); return APP_BLE_NO_WRITE_CCC; } ret = ble_op_att_send_data(handle, data, len, handle_type); if (ret == BLE_BUFFER_FULL) { ret = APP_BLE_BUFF_FULL; } if (ret) { log_info("app_send_fail:%d !!!!!!\n", ret); } return ret; } //------------------------------------------------------ static int make_set_adv_data(void) { u8 offset = 0; u8 *buf = adv_data; #if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x03) u8 service_data[11] = {0x01, 0xA2, 0x00, 0x65, 0x36, 0x6A, 0x6C, 0x6C, 0x70, 0x79, 0x37}; offset += make_eir_packet_val(&buf[offset], offset, HCI_EIR_DATATYPE_FLAGS, 0x06, 1); offset += make_eir_packet_val(&buf[offset], offset, HCI_EIR_DATATYPE_MORE_16BIT_SERVICE_UUIDS, 0xA201, 2); offset += make_eir_packet_data(&buf[offset], offset, HCI_EIR_DATATYPE_SERVICE_DATA, (void *)service_data, 11); #endif #if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x04) u8 service_data[22] = {0x50, 0XFD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; offset += make_eir_packet_val(&buf[offset], offset, HCI_EIR_DATATYPE_FLAGS, 0x06, 1); offset += make_eir_packet_val(&buf[offset], offset, HCI_EIR_DATATYPE_MORE_16BIT_SERVICE_UUIDS, 0x50FD, 2); offset += make_eir_packet_data(&buf[offset], offset, HCI_EIR_DATATYPE_SERVICE_DATA, (void *)service_data, 22); #endif if (offset > ADV_RSP_PACKET_MAX) { puts("***adv_data overflow!!!!!!\n"); return -1; } log_info("adv_data(%d):", offset); log_info_hexdump(buf, offset); adv_data_len = offset; ble_op_set_adv_data(offset, buf); return 0; } static int make_set_rsp_data(void) { u8 offset = 0; u8 *buf = scan_rsp_data; u8 name_len = gap_device_name_len; u8 vaild_len = ADV_RSP_PACKET_MAX - (offset + 2); if (name_len > vaild_len) { name_len = vaild_len; } #if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x03) u8 ble_specific_data[24] = {0xD0, 0x07, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x38, 0xD1, 0x4B, 0xE7, 0xF3, 0x61, 0xB8, 0xB3, 0xFA, 0x4E, 0x8C, 0x70, 0xD5, 0xEB, 0x25, 0x7E}; offset += make_eir_packet_val(&buf[offset], offset, HCI_EIR_DATATYPE_COMPLETE_LOCAL_NAME, 0x5954, 2); offset += make_eir_packet_data(&buf[offset], offset, HCI_EIR_DATATYPE_MANUFACTURER_SPECIFIC_DATA, (void *)ble_specific_data, 24); #endif #if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x04) u8 ble_specific_data[22] = {0xD0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; offset += make_eir_packet_val(&buf[offset], offset, HCI_EIR_DATATYPE_COMPLETE_LOCAL_NAME, 0x5954, 2); offset += make_eir_packet_data(&buf[offset], offset, HCI_EIR_DATATYPE_MANUFACTURER_SPECIFIC_DATA, (void *)ble_specific_data, 24); #endif if (offset > ADV_RSP_PACKET_MAX) { puts("***rsp_data overflow!!!!!!\n"); return -1; } log_info("rsp_data(%d):", offset); log_info_hexdump(buf, offset); scan_rsp_data_len = offset; ble_op_set_rsp_data(offset, buf); return 0; } //广播参数设置 static void advertisements_setup_init() { uint8_t adv_type = ADV_IND; uint8_t adv_channel = ADV_CHANNEL_ALL; int ret = 0; ble_op_set_adv_param(ADV_INTERVAL_MIN, adv_type, adv_channel); ret |= make_set_adv_data(); ret |= make_set_rsp_data(); if (ret) { puts("advertisements_setup_init fail !!!!!!\n"); return; } } #define PASSKEY_ENTER_ENABLE 0 //输入passkey使能,可修改passkey //重设passkey回调函数,在这里可以重新设置passkey //passkey为6个数字组成,十万位、万位。。。。个位 各表示一个数字 高位不够为0 static void reset_passkey_cb(u32 *key) { #if 1 u32 newkey = rand32();//获取随机数 newkey &= 0xfffff; if (newkey > 999999) { newkey = newkey - 999999; //不能大于999999 } *key = newkey; //小于或等于六位数 printf("set new_key= %06u\n", *key); #else *key = 123456; //for debug #endif } void ble_sm_setup_init(io_capability_t io_type, u8 auth_req, uint8_t min_key_size, u8 security_en) { //setup SM: Display only sm_init(); sm_set_io_capabilities(io_type); sm_set_authentication_requirements(auth_req); sm_set_encryption_key_size_range(min_key_size, 16); sm_set_request_security(security_en); sm_event_callback_set(&cbk_sm_packet_handler); if (io_type == IO_CAPABILITY_DISPLAY_ONLY) { reset_PK_cb_register(reset_passkey_cb); } } void ble_profile_init(void) { printf("TUYA ble profile init\n"); le_device_db_init(); #if PASSKEY_ENTER_ENABLE ble_sm_setup_init(IO_CAPABILITY_DISPLAY_ONLY, SM_AUTHREQ_MITM_PROTECTION, 7, TCFG_BLE_SECURITY_EN); #else /* ble_sm_setup_init(IO_CAPABILITY_NO_INPUT_NO_OUTPUT, SM_AUTHREQ_BONDING, 7, TCFG_BLE_SECURITY_EN); */ ble_sm_setup_init(IO_CAPABILITY_NO_INPUT_NO_OUTPUT, SM_AUTHREQ_BONDING, 7, 0); #endif /* setup ATT server */ att_server_init(profile_data, NULL, att_write_callback); att_server_register_packet_handler(cbk_packet_handler); /* gatt_client_register_packet_handler(packet_cbk); */ // register for HCI events hci_event_callback_set(&cbk_packet_handler); /* ble_l2cap_register_packet_handler(packet_cbk); */ /* sm_event_packet_handler_register(packet_cbk); */ le_l2cap_register_packet_handler(&cbk_packet_handler); #if TRANS_ANCS_EN //setup GATT client gatt_client_init(); //setup ANCS clent ancs_client_init(); ancs_set_notification_buffer(ancs_info_buffer, sizeof(ancs_info_buffer)); ancs_client_register_callback(&cbk_packet_handler); #endif extern u16 ble_vendor_set_default_att_mtu(u16 mtu_size); ble_vendor_set_default_att_mtu(ATT_LOCAL_MTU_SIZE); } #if EXT_ADV_MODE_EN #define EXT_ADV_NAME 'J', 'L', '_', 'E', 'X', 'T', '_', 'A', 'D', 'V' /* #define EXT_ADV_NAME "JL_EXT_ADV" */ #define BYTE_LEN(x...) sizeof((u8 []) {x}) #define EXT_ADV_DATA \ 0x02, 0x01, 0x06, \ 0x03, 0x02, 0xF0, 0xFF, \ BYTE_LEN(EXT_ADV_NAME) + 1, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, EXT_ADV_NAME struct ext_advertising_param { u8 Advertising_Handle; u16 Advertising_Event_Properties; u8 Primary_Advertising_Interval_Min[3]; u8 Primary_Advertising_Interval_Max[3]; u8 Primary_Advertising_Channel_Map; u8 Own_Address_Type; u8 Peer_Address_Type; u8 Peer_Address[6]; u8 Advertising_Filter_Policy; u8 Advertising_Tx_Power; u8 Primary_Advertising_PHY; u8 Secondary_Advertising_Max_Skip; u8 Secondary_Advertising_PHY; u8 Advertising_SID; u8 Scan_Request_Notification_Enable; } _GNU_PACKED_; struct ext_advertising_data { u8 Advertising_Handle; u8 Operation; u8 Fragment_Preference; u8 Advertising_Data_Length; u8 Advertising_Data[BYTE_LEN(EXT_ADV_DATA)]; } _GNU_PACKED_; struct ext_advertising_enable { u8 Enable; u8 Number_of_Sets; u8 Advertising_Handle; u16 Duration; u8 Max_Extended_Advertising_Events; } _GNU_PACKED_; const struct ext_advertising_param ext_adv_param = { .Advertising_Handle = 0, .Advertising_Event_Properties = 1, .Primary_Advertising_Interval_Min = {30, 0, 0}, .Primary_Advertising_Interval_Max = {30, 0, 0}, .Primary_Advertising_Channel_Map = 7, .Primary_Advertising_PHY = ADV_SET_1M_PHY, .Secondary_Advertising_PHY = ADV_SET_1M_PHY, }; const struct ext_advertising_data ext_adv_data = { .Advertising_Handle = 0, .Operation = 3, .Fragment_Preference = 0, .Advertising_Data_Length = BYTE_LEN(EXT_ADV_DATA), .Advertising_Data = EXT_ADV_DATA, }; const struct ext_advertising_enable ext_adv_enable = { .Enable = 1, .Number_of_Sets = 1, .Advertising_Handle = 0, .Duration = 0, .Max_Extended_Advertising_Events = 0, }; const struct ext_advertising_enable ext_adv_disable = { .Enable = 0, .Number_of_Sets = 1, .Advertising_Handle = 0, .Duration = 0, .Max_Extended_Advertising_Events = 0, }; #endif /* EXT_ADV_MODE_EN */ void tuya_set_adv_enable() { if (con_handle) { printf("connect is exist return\n"); return; } if (!adv_ctrl_en) { printf("!adv_ctrl_en return\n"); return; } ble_op_adv_enable(1); } static int set_adv_enable(void *priv, u32 en) { ble_state_e next_state, cur_state; if (!adv_ctrl_en) { return APP_BLE_OPERATION_ERROR; } if (con_handle) { return APP_BLE_OPERATION_ERROR; } if (en) { next_state = BLE_ST_ADV; } else { next_state = BLE_ST_IDLE; } cur_state = get_ble_work_state(); switch (cur_state) { case BLE_ST_ADV: case BLE_ST_IDLE: case BLE_ST_INIT_OK: case BLE_ST_NULL: case BLE_ST_DISCONN: break; default: return APP_BLE_OPERATION_ERROR; break; } if (cur_state == next_state) { return APP_BLE_NO_ERROR; } log_info("adv_en:%d\n", en); set_ble_work_state(next_state); #if EXT_ADV_MODE_EN if (en) { ble_op_set_ext_adv_param(&ext_adv_param, sizeof(ext_adv_param)); log_info_hexdump(&ext_adv_data, sizeof(ext_adv_data)); ble_op_set_ext_adv_data(&ext_adv_data, sizeof(ext_adv_data)); ble_op_set_ext_adv_enable(&ext_adv_enable, sizeof(ext_adv_enable)); } else { ble_op_set_ext_adv_enable(&ext_adv_disable, sizeof(ext_adv_disable)); } #else if (en) { advertisements_setup_init(); } ble_op_adv_enable(en); #endif /* EXT_ADV_MODE_EN */ return APP_BLE_NO_ERROR; } static int ble_disconnect(void *priv) { if (con_handle) { if (BLE_ST_SEND_DISCONN != get_ble_work_state()) { log_info(">>>tuya ble send disconnect\n"); set_ble_work_state(BLE_ST_SEND_DISCONN); ble_op_disconnect(con_handle); } else { log_info(">>>tuya ble wait disconnect...\n"); } return APP_BLE_NO_ERROR; } else { return APP_BLE_OPERATION_ERROR; } } static int get_buffer_vaild_len(void *priv) { u32 vaild_len = 0; ble_op_att_get_remain(&vaild_len); return vaild_len; } int tuya_app_send_user_data_do(void *priv, u8 *data, u16 len) { #if PRINT_DMA_DATA_EN if (len < 128) { log_info("-le_tx(%d):"); log_info_hexdump(data, len); } else { putchar('L'); } #endif att_set_ccc_config(ATT_CHARACTERISTIC_00000002_0000_1001_8001_00805F9B07D0_01_VALUE_HANDLE + 1, ATT_OP_NOTIFY); return app_send_user_data(ATT_CHARACTERISTIC_00000002_0000_1001_8001_00805F9B07D0_01_VALUE_HANDLE, data, len, ATT_OP_AUTO_READ_CCC); return 0; //return app_send_user_data(ATT_CHARACTERISTIC_2b10_01_VALUE_HANDLE, data, len, ATT_OP_AUTO_READ_CCC); } static int app_send_user_data_check(u16 len) { u32 buf_space = get_buffer_vaild_len(0); if (len <= buf_space) { return 1; } return 0; } static int regiest_wakeup_send(void *priv, void *cbk) { ble_resume_send_wakeup = cbk; return APP_BLE_NO_ERROR; } static int regiest_recieve_cbk(void *priv, void *cbk) { channel_priv = (u32)priv; app_recieve_callback = cbk; return APP_BLE_NO_ERROR; } static int regiest_state_cbk(void *priv, void *cbk) { channel_priv = (u32)priv; app_ble_state_callback = cbk; return APP_BLE_NO_ERROR; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ u8 *ble_get_scan_rsp_ptr(u16 *len) { if (len) { *len = scan_rsp_data_len; } return scan_rsp_data; } u8 *ble_get_adv_data_ptr(u16 *len) { if (len) { *len = adv_data_len; } return adv_data; } u8 *ble_get_gatt_profile_data(u16 *len) { *len = sizeof(profile_data); return (u8 *)profile_data; } void bt_ble_adv_enable(u8 enable) { set_adv_enable(0, enable); } u16 bt_ble_is_connected(void) { return con_handle; } void ble_module_enable(u8 en) { if (en) { adv_ctrl_en = 1; bt_ble_adv_enable(1); } else { if (con_handle) { adv_ctrl_en = 0; ble_disconnect(NULL); } else { bt_ble_adv_enable(0); adv_ctrl_en = 0; } } } //流控使能 EN: 1-停止收数 or 0-继续收数 int ble_trans_flow_enable(u8 en) { int ret = -1; #if ATT_DATA_RECIEVT_FLOW if (con_handle) { att_server_flow_hold(con_handle, en); ret = 0; } #endif log_info("ble_trans_flow_enable:%d,%d\n", en, ret); return ret; } //for test static void timer_trans_flow_test(void) { static u8 sw = 0; if (con_handle) { sw = !sw; ble_trans_flow_enable(sw); } } static const char ble_ext_name[] = "(BLE)"; int tuya_app_send_user_data(u8 *data, u16 len) { int ret = 0; #if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x03) att_set_ccc_config(ATT_CHARACTERISTIC_2b10_01_VALUE_HANDLE + 1, ATT_OP_NOTIFY); ret = app_send_user_data(ATT_CHARACTERISTIC_2b10_01_VALUE_HANDLE, data, len, ATT_OP_NOTIFY); #endif #if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x04) att_set_ccc_config(ATT_CHARACTERISTIC_00000002_0000_1001_8001_00805F9B07D0_01_VALUE_HANDLE + 1, ATT_OP_NOTIFY); ret = app_send_user_data(ATT_CHARACTERISTIC_00000002_0000_1001_8001_00805F9B07D0_01_VALUE_HANDLE, data, len, ATT_OP_NOTIFY); #endif printf("tuya_send_user_data: %d, len = %d\n", ret, len); put_buf(data, len); return ret ; } void bt_ble_init(void) { le_controller_set_mac((void *)tuya_mac_addr); log_info("***** ble_init******\n"); char *name_p; #if DOUBLE_BT_SAME_NAME u8 ext_name_len = 0; #else u8 ext_name_len = sizeof(ble_ext_name) - 1; #endif name_p = bt_get_local_name(); gap_device_name_len = strlen(name_p); if (gap_device_name_len > BT_NAME_LEN_MAX - ext_name_len) { gap_device_name_len = BT_NAME_LEN_MAX - ext_name_len; } memcpy(gap_device_name, name_p, gap_device_name_len); #if DOUBLE_BT_SAME_NAME == 0 //增加后缀,区分名字 memcpy(&gap_device_name[gap_device_name_len], "(BLE)", ext_name_len); gap_device_name_len += ext_name_len; #endif log_info("ble name(%d): %s \n", gap_device_name_len, gap_device_name); #if ATT_DATA_RECIEVT_FLOW log_info("att_server_flow_enable\n"); att_server_flow_enable(1); /* sys_timer_add(0, timer_trans_flow_test, 3000); */ #endif set_ble_work_state(BLE_ST_INIT_OK); ble_module_enable(1); extern void tuya_ble_app_init(); tuya_ble_app_init(); } void bt_ble_exit(void) { log_info("***** ble_exit******\n"); } void ble_app_disconnect(void) { ble_disconnect(NULL); } static const struct ble_server_operation_t mi_ble_operation = { .adv_enable = set_adv_enable, .disconnect = ble_disconnect, .get_buffer_vaild = get_buffer_vaild_len, .send_data = (void *)tuya_app_send_user_data_do, .regist_wakeup_send = regiest_wakeup_send, .regist_recieve_cbk = regiest_recieve_cbk, .regist_state_cbk = regiest_state_cbk, }; void ble_get_server_operation_table(struct ble_server_operation_t **interface_pt) { *interface_pt = (void *)&mi_ble_operation; } void ble_server_send_test_key_num(u8 key_num) { ; } #endif