[技术干货]蓝牙遥控器在TuyaOS联网单品上的使用
简介
Tuya beacon 协议是基于 BLE 广播通信技术,完善配对解绑、组包拆包、群组管理、加密解密、安全策略,形成的一种轻量、安全的可接入涂鸦云的蓝牙协议。
蓝牙 beacon 是基于蓝牙 BLE 技术的一种应用,是蓝牙 BLE 的诸多应用之一。BLE 的通信包括两个主要部分:advertising(广播)和connecting(连接),Beacon 设备只使用了广播通信信道,不和任何低功耗蓝牙主机进行连接,正如 beacon(信标、灯塔)的字面意思。当你在 beacon 遥控器上按下按键后 beacon 遥控器便会发送一个对应的数据包。
优势
- 成本低,无需使用任何模组,只需要一个 16 脚的 IC 和少量的外围器件就可以实现遥控器的硬件方案。
- 遥控器无需配网,支持被控的设备已激活状态下配对,也支持未激活未配网设备处于低功耗状态下配对。配对成功后,设备离线也仍然可以被遥控器控制。
- 控制范围大,无需对准设备即可控制。红外遥控器需要对准产品上的红外接收头才能正确接收。
- 最大支持 4 个群组。
配网模式中涉及到的低功耗,是指关闭射频模组,芯片并非进入了睡眠模式。配网模式详细说明可参考:https://developer.tuya.com/cn/docs/iot- ... 7tro6mzaz0
使用流程
- 注册蓝牙 beacon 数据接收回调函数。此接口在需要使用蓝牙遥控器功能下必须调用,当用户没有注册蓝牙遥控器数据处理回调函数时,SDK将不启动蓝牙广播扫描,蓝牙遥控器功能不可用。
Code: Select all
//callback function for advertisement scanning data processing
typedef VOID (*TUYA_BLE_APP_SCAN_HANDLE)(UCHAR_T *data, UCHAR_T len, UCHAR_T type, UCHAR_T* mac);
OPERATE_RET tuya_ble_reg_app_scan_adv_cb(TUYA_BLE_APP_SCAN_HANDLE cb);
注:
- 使用蓝牙 beacon 遥控器之前需先调用 TuyaOS 联网单品设备初始化,“设备初始化"详细说明可参考:https://developer.tuya.com/cn/docs/iot- ... 7dkj0mxrne。
- 注册蓝牙遥控器绑定校验,绑定/解绑通知回调函数,通过注册的两个函数可以进行蓝牙遥控器绑定判断和得到是否成功绑定的结果。
Code: Select all
typedef struct {
BLE_SCAN_ADV_BIND_CHECK_CB bind_check;
BLE_SCAN_ADV_BIND_RSLT_NOTIFY_CB bind_notify;
} TUYA_BLE_SCAN_ADV_HANDLE_CBS;
/**
\* @brief Register callback function for advertisement scanning data processing
*
* @param[in] cbs: callback function
*
* @note This API is used for registering callback function for advertisement scanning data processing
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ble_reg_app_scan_adv_handle_cbs(TUYA_BLE_SCAN_ADV_HANDLE_CBS* cbs);
- 可在遥控器绑定校验回调中做指令校验,可通过返回
OPRT_OK
或OPRT_ERR_COMMON
允许/拒绝当前遥控器绑定。 - 在遥控器绑定/解绑通知回调函数
bind_notify
中做如指示灯状态提示,可帮助产品使用者判断当前遥控器绑定状态。
注:当前函数 tuya_ble_reg_app_scan_adv_handle_cbs() 非必须调用,如未调用该接口,可通过tuya_ble_reg_app_scan_adv_cb() 注册的数据接收回调函数中通过命令字获取绑定/解绑状态。
- 蓝牙遥控器绑定窗口时间设置函数。该函数也可以不调用,默认绑定窗口时间为 30s。
Code: Select all
/**
* @brief Set timeout window for binding remote controller
*
* @param[in] time_out: time out in second, 0: no window
*
* @note This API is used for setting timeout window for binding remote controller
*
* @return VOID
*/
VOID_T tuya_ble_set_bind_window(UINT_T time_out);
- 手工打开蓝牙遥控器绑定窗口。设备在每次上电后自动开启蓝牙绑定窗口,直至绑定窗口时间超时后关闭。此函数用于在TuyaOS关闭蓝牙遥控器绑定窗口后,手工开启设备绑定窗口,如使用按钮触发使设备再次进入蓝牙遥控器绑定窗口
Code: Select all
/**
* @brief Open window for binding remote controller
*
* @param[in] VOID
*
* @note This API is used for opening window for binding remote controller
*
* @return VOID
*/
VOID_T tuya_ble_open_bind_window(VOID_T);
注:该函数不能用于设备配网状态下,否则会导致设备蓝牙配网失败。
示例代码
遥控器命令表
Code: Select all
#define BLE_KEY_TYPE_CMD 0x01 //健值
#define BLE_BIND_CMD 0x02 //绑定
#define BLE_UNBIND_CMD 0x03 //解绑
#define BLE_SWITCH_CMD 0x04 //开关
#define BLE_FAVORITES_CMD 0x05 //喜好收藏
#define BLE_SCHEDULED_CLOSE_CMD 0x06 //倒计时
#define BLE_GROUP_CHECK_CMD 0x07 //一键群组查询
#define BLE_LIGHT_SWITCH_CMD 0x08 //照明开关
#define BLE_LIGHT_BRIGHT_STEP_CMD 0x09 //照明步进调亮度
#define BLE_LIGHT_BRIGHT_STEPLESS_CMD 0x0a //照明无极调亮度
#define BLE_LIGHT_TEMP_STEP_CMD 0x0b //照明步进调色温
#define BLE_LIGHT_TEMP_STEPLESS_CMD 0x0c //照明无极调色温
#define BLE_LIGHT_SEVEN_COLOR_CMD 0x0d //七彩调节
#define BLE_LIGHT_COLOE_H_STEP_CMD 0x0e //H 值调节
#define BLE_LIGHT_COLOE_H_DIMMING_CMD 0x0f //H 值无极调节
#define BLE_LIGHT_COLOE_S_STEP_CMD 0x10 //S 值调节
#define BLE_LIGHT_COLOE_S_DIMMING_CMD 0x11 //S 值无极调节
#define BLE_LIGHT_COLOE_V_STEP_CMD 0x12 //V 值调节
#define BLE_LIGHT_COLOE_V_DIMMING_CMD 0x13 //S 值无极调节
#define BLE_LIGHT_COLOE_HSV_CMD 0x14 //HSV 值调节
#define BLE_LIGHT_SCENE_CHANGE_CMD 0x15 //场景调节
#define BLE_LIGHT_MODE_SET_CMD 0x16 //模式设置
#define BLE_SPECIAL_MODE 0x17 //夜灯模式
#define BLE_RGBY_CONTROL_CMD 0xff //定制rgby方向调节颜色
遥控器代码实现
Code: Select all
/**
* @brief
*
* @param[in] data: data
* @param[in] len: data len
* @param[in] type: type
* @param[in] mac: device mac
* @return none
*/
STATIC VOID ble_receive_callback(UCHAR_T *data, UCHAR_T len, UCHAR_T type, UCHAR_T* mac)
{
INT_T i = 0;
if(type == 0xff){
TAL_PR_DEBUG("ble remote(old ver): data len: %d", len);
for ( i = 0; i < len; i++) {
TAL_PR_DEBUG("data[%d] : %d", i, data[i]);
}
}else if(type == 0x16){
TAL_PR_DEBUG("ble remote(new ver): data len: %d", len);
UCHAR_T *data_cmd = (data + 2);
//解析控制数据
switch(data_cmd[2]){
case BLE_BIND_CMD:
//do binding operation
TAL_PR_NOTICE("=====bind success!!!=====");
break;
case BLE_UNBIND_CMD:
//do unbinding operation
TAL_PR_NOTICE("====unbind success!!!====");
break;
default:
//do ctrl operation
TAL_PR_DEBUG("====please do ctrl operation!!!==== cmd: [%02x], cmd len: [%02d]", data_cmd[2], len - 2);
// do_handle(&(data_cmd[2]),len-2);
break;
}
}
return;
}
/**
* @brief ble scanf adv bind check callback
*
* @param[in] type: bind type for bluetooth remote controller
* @param[in] data: data
* @param[in] len: data len
* @return OPRT_OK
*/
STATIC OPERATE_RET ble_scan_adv_bind_check_callback(TUYA_BLE_BIND_TYPE type, UCHAR_T *data, UCHAR_T len)
{
INT_T i = 0;
TAL_PR_DEBUG("----------------scan adv bind check cb-----------------");
TAL_PR_DEBUG("ble bind type : %d, data len: %d", type, len);
for(i=0;i<len;i++) {
TAL_PR_DEBUG("data[%d] : %d", i, data[i]);
}
return OPRT_OK;
}
/**
* @brief ble scan adv bind notify callback
*
* @param[in] type: bind type for bluetooth remote controller
* @return none
*/
STATIC VOID_T ble_adv_scan_bind_notify_callback(TUYA_BLE_BIND_TYPE type, int rslt)
{
TAL_PR_DEBUG("------------------ble scan adv bind notify cb----------------");
TAL_PR_DEBUG("ble bind type : %s", (type == TUYA_BLE_OP_BIND) ? "bind" : "unbind");
TAL_PR_DEBUG("result : %s", (rslt == 0) ? "success" : "failed");
return ;
}
/**
* @brief ble remote init
*
* @param[in] param: none
* @return none
*/
VOID ble_remote_init(VOID)
{
OPERATE_RET rt = OPRT_OK;
TUYA_BLE_SCAN_ADV_HANDLE_CBS ble_scan_cfg = {0};
ble_scan_cfg.bind_check = ble_scan_adv_bind_check_callback;
ble_scan_cfg.bind_notify = ble_adv_scan_bind_notify_callback;
/* Register callback function for advertisement scanning data processing */
TUYA_CALL_ERR_GOTO(tuya_ble_reg_app_scan_adv_cb(ble_receive_callback), __EXIT);
/* Register callback function for advertisement scanning data processing */
TUYA_CALL_ERR_GOTO(tuya_ble_reg_app_scan_adv_handle_cbs(&ble_scan_cfg), __EXIT);
/* set bind window (s) */
tuya_ble_set_bind_window(30);
__EXIT:
return;
}
可根据具体项目情况
- 在
ble_scan_adv_bind_check_callback()
回调函数中实现对遥控器数据做判断,如判读正确返回OPRT_OK
表示允许绑定,否则返回OPRT_COM_ERROR
表示不允许绑定。 - 在
ble_adv_scan_bind_notify_callback()
回调函数中实现绑定/解绑指示。 - 在
ble_receive_callback()
回调函数中实现控制逻辑代码。
开启遥控器配对功能
将 ble_remote_config()
加入 tuyaos_demo_quickstart 项目,加入到 __soc_device_init()
函数最后。由于设备在未配网状态下同样会开启蓝牙用于配网,如在未配网状态下开启遥控器配对功能会导致蓝牙配网失败。
Code: Select all
OPERATE_RET __soc_device_init(VOID_T)
{
OPERATE_RET rt = OPRT_OK;
#if (defined(UUID) && defined(AUTHKEY))
ws_db_init_mf();
/* Set authorization information
* Note that if you use the default authorization information of the code, there may be problems of multiple users and conflicts,
* so try to use all the authorizations purchased from the tuya iot platform.
* Buying guide: https://developer.tuya.com/cn/docs/iot/lisence-management?id=Kb4qlem97idl0.
* You can also apply for two authorization codes for free in the five-step hardware development stage of the Tuya IoT platform.
* Authorization information can also be written through the production testing tool.
* When the production testing function is started and the authorization is burned with the Tuya Cloud module tool,
* please comment out this piece of code.
*/
WF_GW_PROD_INFO_S prod_info = {UUID, AUTHKEY};
TUYA_CALL_ERR_RETURN(tuya_iot_set_wf_gw_prod_info(&prod_info));
#else
// 产测初始化, 注册函数需要应用实现,其中串口驱动不需要应用提供
MF_IMPORT_INTF_S intf = {0};
intf.uart_init = mf_uart_init_callback;
intf.uart_free = mf_uart_free_callback;
intf.uart_send = mf_uart_send_callback;
intf.uart_recv = mf_uart_recv_callback;
intf.gpio_test = mf_gpio_test_cb;
intf.mf_user_product_test = mf_user_product_test_callback;
intf.user_callback = mf_user_callback;
intf.user_enter_mf_callback = mf_user_enter_mf_callback;
intf.user_pre_gpio_test = mf_pre_gpio_test_cb;
TAL_PR_ERR("mf_init APP_BIN_NAME[%s] USER_SW_VER[%s]", APP_BIN_NAME, USER_SW_VER);
TUYA_CALL_ERR_RETURN(mf_init(&intf, APP_BIN_NAME, USER_SW_VER, TRUE));
#endif
/* Initialize TuyaOS product information */
TY_IOT_CBS_S iot_cbs = {0};
iot_cbs.gw_status_cb = __soc_dev_status_changed_cb;
iot_cbs.gw_ug_cb = __soc_dev_rev_upgrade_info_cb;
iot_cbs.gw_reset_cb = __soc_dev_restart_req_cb;
iot_cbs.dev_obj_dp_cb = __soc_dev_obj_dp_cmd_cb;
iot_cbs.dev_raw_dp_cb = __soc_dev_raw_dp_cmd_cb;
iot_cbs.dev_dp_query_cb = __soc_dev_dp_query_cb;
TUYA_CALL_ERR_RETURN(tuya_iot_wf_soc_dev_init(GWCM_OLD, WF_START_AP_FIRST, &iot_cbs, PID, USER_SW_VER));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_get_wf_nw_stat_cb(__soc_dev_net_status_cb));
/* Network button, LED initialization */
app_led_init(LED_PIN);
app_key_init(KEY_PIN);
ble_remote_init();
return 0;
}
运行
设备重新上电,进入蓝牙 beacon 遥控器配对模式。
同时长按蓝牙遥控器的分组键,(如果你的蓝牙遥控器没有分组键,那么开关键就是默认的分组1),蓝牙遥控器开始和设备配对。
Code: Select all
[09-13 16:20:18 TUYA D][example_ble_remote.c:113] ----------------scan adv bind check cb-----------------
[09-13 16:20:18 TUYA D][example_ble_remote.c:114] ble bind type : 1, data len: 12
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[0] : 11
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[1] : 7
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[2] : 1
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[3] : 255
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[4] : 2
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[5] : 1
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[6] : 255
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[7] : 92
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[8] : 139
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[9] : 0
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[10] : 211
[09-13 16:20:18 TUYA D][example_ble_remote.c:116] data[11] : 206
蓝牙状态发送变化,进入蓝牙事件回调函数中。
Code: Select all
[09-13 16:20:18 TUYA D][example_ble_remote.c:130] ------------------ble scan adv bind notify cb----------------
[09-13 16:20:18 TUYA D][example_ble_remote.c:131] ble bind type : bind
[09-13 16:20:18 TUYA D][example_ble_remote.c:132] result : success
按下蓝牙遥控器的控制键。进入数据处理回调函数中。
Code: Select all
[09-13 16:20:27 TUYA D][example_ble_remote.c:75] ble remote(new ver): data len: 9
[09-13 16:20:27 TUYA D][example_ble_remote.c:92] ====please do ctrl operation!!!==== cmd: [04], cmd len: [07]
同时长按蓝牙遥控器的亮度-和配对时的分组键,开始解绑流程。
设备解绑,状态发生变化,进入蓝牙事件回调函数中。
Code: Select all
[09-13 16:24:11 TUYA D][example_ble_remote.c:75] ble remote(new ver): data len: 9
[09-13 16:24:11 TUYA N][example_ble_remote.c:87] ====unbind success!!!====
[09-13 16:24:11 TUYA D][example_ble_remote.c:130] ------------------ble scan adv bind notify cb----------------
[09-13 16:24:11 TUYA D][example_ble_remote.c:131] ble bind type : unbind
[09-13 16:24:11 TUYA D][example_ble_remote.c:132] result : success