更新时间:2024-11-20 08:51:30下载pdf
本文主要介绍蓝牙配网的关键流程,通过本文您将会了解如何实现蓝牙配网功能,完成综合 SDK 蓝牙配网的适配。
蓝牙配网,顾名思义,通过蓝牙通道传输配网信息(激活令牌、路由器的 SSID 和密码等),完成设备配网的过程。
蓝牙配网的流程是,网关设备通过蓝牙广播发送设备相关的信息,App 接收并解析蓝牙广播包,如果广播包中包含涂鸦服务,则发起扫描响应请求进一步获取设备的详细信息,获取到设备完整的信息后在 App 显示待配网设备。当用户选择激活设备,App 与网关设备建立蓝牙连接,把配网信息通过蓝牙通道发送给网关设备,网关设备获取到配网信息,完成激活流程。
说明:涂鸦蓝牙配网用的是私有服务和私有数据,所有数据均由 SDK 解析和封装,开发者无需关心数据内容。
如果使用涂鸦网关蓝牙模组,蓝牙配网的适配已经在 SDK 内部实现,应用无需任何代码适配,详见 涂鸦蓝牙业务指南。
如果使用自定义模组,开发者需要完成接口的适配。SDK 已经实现蓝牙配网的底层逻辑,定义了一套蓝牙配网适配接口,本小节将会详细介绍如何适配。
蓝牙配网的交互流程示意图:
蓝牙配网的流程如下所示:
蓝牙广播和扫描响应的数据格式:
蓝牙广播和扫描响应在蓝牙规范中有定义,SDK 把广播数据和扫描响应数据封装成 AD Structure 格式,应用无需额外解析处理,直接在广播和扫描响应中使用即可。
蓝牙传输的数据格式:
App 和网关建立蓝牙连接后,通过蓝牙 The Attribute Protocol(ATT) 协议来传输数据,SDK 封装和解析的数据都属于 ATT Payload。
Service UUID: 0x1910。
Characteristic | UUID | 属性 | 说明 |
---|---|---|---|
Write Characteristic | 0x2B11 | Write Without Response | App 向 BLE 设备发送数据 |
Notify Characteristic | 0x2B10 | Notify | BLE 设备向 App 发送通知信息 |
接下来,我们一步步来完成蓝牙配网的适配工作。
在 SDK 初始化之前初始化蓝牙适配的注册。
// ...
#if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
#include "tuya_os_adapt_bt.h"
#endif
int main(int argc, char **argv)
{
/**
* 必须在 SDK 其他接口之前调用
*/
#if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
tuya_os_adapt_reg_bt_intf();
#endif
tuya_os_intf_init();
// ...
}
适配蓝牙初始化接口,在接口中实现初始化蓝牙协议栈,把蓝牙配置成 BLE Master 模式,并且把数据处理的函数指针保存起来,通过该函数指针把接收到的蓝牙数据传给 SDK 处理。
TY_BT_MSG_CB bt_msg_cb;
OPERATE_RET tuya_adapter_bt_port_init(ty_bt_param_t *p)
{
/**
* 函数中需要实现以下功能:
* a) 初始化蓝牙协议栈,保证蓝牙能正常通讯。
* b) 保存函数指针变量,把接收到的蓝牙数据通过该函数指针传递给 SDK。
*/
bt_msg_cb = p->cb;
return OPRT_OK;
}
适配蓝牙广播数据重置接口,在接口中把广播和扫描响应数据内容保存,并且开启广播。
tuya_ble_data_buf_t adv_data = {0};
tuya_ble_data_buf_t rsp_data = {0};
OPERATE_RET tuya_adapter_bt_adv_reset(tuya_ble_data_buf_t *adv, tuya_ble_data_buf_t *scan_resp)
{
if ((adv == NULL) || (adv->len == 0) || (scan_resp == NULL) || (scan_resp->len == 0)) {
return OPRT_INVALID_PARM;
}
if (adv_data.data != NULL) {
Free(adv_data.data);
adv_data.data = NULL;
}
adv_data.len = adv->len;
adv_data.data = Malloc(adv_data.len);
if (adv_data.data != NULL) {
memcpy(adv_data.data, adv->data, adv->len);
}
if (rsp_data.data != NULL) {
Free(rsp_data.data);
rsp_data.data = NULL;
}
rsp_data.len = scan_resp->len;
rsp_data.data = Malloc(rsp_data.len);
if (rsp_data.data != NULL) {
memcpy(rsp_data.data, scan_resp->data, scan_resp->len);
}
/**
* 需要实现的逻辑:
* a) 开启广播(数据:adv_data.data,数据长度:adv_data.len)。
* b) 保存扫描响应数据,在收到扫描响应请求时返回该数据(数据:rsp_data.data,数据长度:rsp_data.len)。
*/
return OPRT_OK;
}
适配数据发送接口,在接口中把数据发送给 App。
OPERATE_RET tuya_adapter_bt_send(BYTE_T *data, UINT8_T len)
{
/**
* 需要实现的逻辑:把数据通过 GATT Server Send Characteristic Notification 发给 App。
*/
return OPRT_OK;
}
把接收到的蓝牙数据传给 SDK 解析。
蓝牙连接建立时,发送事件给 SDK:
VOID example_ble_connected(VOID)
{
tuya_ble_data_buf_t data = {0};
if (bt_msg_cb == NULL) {
return;
}
data.len = 0;
data.data = NULL;
bt_msg_cb(0, TY_BT_EVENT_CONNECTED, &data);
}
蓝牙连接断开时,发送事件给 SDK:
VOID example_ble_disconnected(VOID)
{
tuya_ble_data_buf_t data = {0};
if (bt_msg_cb == NULL) {
return;
}
data.len = 0;
data.data = NULL;
bt_msg_cb(0, TY_BT_EVENT_DISCONNECTED, &data);
bt_msg_cb(0, TY_BT_EVENT_ADV_READY, &data);
}
蓝牙接收数据时,发送事件给 SDK:
/**
* 需要实现的逻辑:应用接收到蓝牙数据,把数据中的 GATT Server Attribute Value 传给 SDK 处理。
*/
VOID example_ble_rx(BYTE_T *rx_data, UINT8_T rx_len)
{
tuya_ble_data_buf_t data = {0};
if (bt_msg_cb == NULL) {
return;
}
data.len = rx_len;
data.data = rx_data;
bt_msg_cb(0, TY_BT_EVENT_RX_DATA, &data);
}
#include <unistd.h>
#include "uni_log.h"
#include "base_os_adapter.h"
#include "tuya_iot_base_api.h"
#include "tuya_iot_com_api.h"
#include "tuya_iot_sdk_api.h"
#include "tuya_iot_sdk_defs.h"
#if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
#include "tuya_os_adapt_bt.h"
#endif
#define PID "fljmamufiym5fktz" // 替换成自己的产品 ID
#define UUID "tuya461dbc63aeeb991f" // 替换成自己的 UUID
#define AUTHKEY "c8X4PR4wx1gMFaQlaZu5dfgVvVRwB8Ug" // 替换成自己的 AUTHKEY
STATIC VOID __gw_reset_cb(GW_RESET_TYPE_E type)
{
PR_DEBUG("gw reset callback, type: %d", type);
if (GW_RESET_DATA_FACTORY != type) {
exit(0);
}
return;
}
STATIC VOID __gw_upgrade_cb(CONST FW_UG_S *fw)
{
PR_DEBUG("gw upgrade callback");
if (fw == NULL) {
PR_ERR("invalid param");
return;
}
PR_DEBUG(" tp: %d", fw->tp);
PR_DEBUG(" fw_url: %s", fw->fw_url);
PR_DEBUG(" sw_ver: %s", fw->sw_ver);
PR_DEBUG(" fw_hmac: %s", fw->fw_hmac);
PR_DEBUG(" file_size: %u", fw->file_size);
return;
}
STATIC VOID __gw_active_stat_cb(GW_STATUS_E status)
{
PR_DEBUG("gw active stat callback, status: %d", status);
return;
}
STATIC VOID __gw_reboot_cb(VOID)
{
PR_DEBUG("gw reboot callback");
exit(0);
return;
}
STATIC VOID __nw_stat_cb(IN CONST SDK_NW_STAT_T stat)
{
PR_DEBUG("network stat: %d", stat);
return;
}
STATIC VOID __wired_stat_cb(IN CONST SDK_WIRED_NW_STAT_T stat)
{
PR_DEBUG("wired stat: %d", stat);
return;
}
int main(int argc, char **argv)
{
OPERATE_RET rt = OPRT_OK;
GW_PROD_INFO_S prod_info = {0};
/* gw base callback */
TY_GW_INFRA_CBS_S gw_cbs = {
.gw_reset_cb = __gw_reset_cb,
.gw_upgrade_cb = __gw_upgrade_cb,
.gw_active_stat_cb = __gw_active_stat_cb,
.gw_reboot_cb = __gw_reboot_cb,
};
#if defined(TY_BT_MOD) && (TY_BT_MOD == 1)
tuya_os_adapt_reg_bt_intf();
#endif
/* initiate os-layer service*/
tuya_os_intf_init();
/* initiate iot-layer service */
TUYA_CALL_ERR_RETURN(tuya_iot_init("./"));
/* set the logging level to debug */
SET_PR_DEBUG_LEVEL(TY_LOG_LEVEL_DEBUG);
PR_DEBUG("SDK INFO: %s", tuya_iot_get_sdk_info());
/* set uuid and authkey */
prod_info.uuid = UUID;
prod_info.auth_key = AUTHKEY;
TUYA_CALL_ERR_RETURN(tuya_iot_set_gw_prod_info(&prod_info));
/* pre-initiate sdk service */
TUYA_CALL_ERR_RETURN(tuya_iot_sdk_pre_init(TRUE));
/* initiate application service, more service in here */
TUYA_CALL_ERR_RETURN(tuya_user_svc_init(&gw_cbs));
/* initiate sdk service */
#if defined(GW_SUPPORT_WIRED_WIFI) && (GW_SUPPORT_WIRED_WIFI==1)
TUYA_CALL_ERR_RETURN(tuya_iot_wired_wf_sdk_init(IOT_GW_NET_WIRED_WIFI, GWCM_OLD, WF_START_AP_ONLY, PID, USER_SW_VER, NULL, 0));
#elif defined(WIFI_GW) && (WIFI_GW==1)
TUYA_CALL_ERR_RETURN(tuya_iot_wf_sdk_init(GWCM_OLD, WF_START_AP_ONLY, PID, USER_SW_VER, NULL, 0));
#else
TUYA_CALL_ERR_RETURN(tuya_iot_sdk_init(PID, USER_SW_VER, NULL, 0));
#endif
/* register net stat notification callback */
TUYA_CALL_ERR_RETURN(tuya_iot_sdk_reg_netstat_cb(__nw_stat_cb, __wired_stat_cb, NULL));
/* start application service, more service in here */
TUYA_CALL_ERR_RETURN(tuya_user_svc_start(NULL));
while (1) {
sleep(10);
}
return 0;
}
#include "tuya_os_adapt_bt.h"
#include "mem_pool.h"
TY_BT_MSG_CB bt_msg_cb = NULL;
tuya_ble_data_buf_t adv_data = {0};
tuya_ble_data_buf_t rsp_data = {0};
OPERATE_RET tuya_adapter_bt_port_init(ty_bt_param_t *p)
{
/**
* 函数中需要实现以下功能:
* a) 初始化蓝牙协议栈,保证蓝牙能正常通讯。
* b) 保存函数指针变量,把接收到的蓝牙数据通过该函数指针传递给 SDK。
*/
bt_msg_cb = p->cb;
return OPRT_OK;
}
OPERATE_RET tuya_adapter_bt_port_deinit(VOID)
{
return OPRT_OK;
}
OPERATE_RET tuya_adapter_bt_gap_disconnect(VOID)
{
return OPRT_OK;
}
OPERATE_RET tuya_adapter_bt_send(BYTE_T *data, UINT8_T len)
{
/**
* 需要实现的逻辑:把数据通过 GATT Server Send Characteristic Notification 发给 App。
*/
return OPRT_OK;
}
OPERATE_RET tuya_adapter_bt_adv_reset(tuya_ble_data_buf_t *adv, tuya_ble_data_buf_t *scan_resp)
{
if ((adv == NULL) || (adv->len == 0) || (scan_resp == NULL) || (scan_resp->len == 0)) {
return OPRT_INVALID_PARM;
}
if (adv_data.data != NULL) {
Free(adv_data.data);
adv_data.data = NULL;
}
adv_data.len = adv->len;
adv_data.data = Malloc(adv_data.len);
if (adv_data.data != NULL) {
memcpy(adv_data.data, adv->data, adv->len);
}
if (rsp_data.data != NULL) {
Free(rsp_data.data);
rsp_data.data = NULL;
}
rsp_data.len = scan_resp->len;
rsp_data.data = Malloc(rsp_data.len);
if (rsp_data.data != NULL) {
memcpy(rsp_data.data, scan_resp->data, scan_resp->len);
}
/**
* 需要实现的逻辑:
* a) 开启广播(数据:adv_data.data,数据长度:adv_data.len)。
* b) 保存扫描响应数据,在收到扫描响应请求时返回该数据(数据:rsp_data.data,数据长度:rsp_data.len)。
*/
return OPRT_OK;
}
OPERATE_RET tuya_adapter_bt_adv_start(VOID)
{
return OPRT_OK;
}
OPERATE_RET tuya_adapter_bt_adv_stop(VOID)
{
return OPRT_OK;
}
OPERATE_RET tuya_adapter_bt_get_rssi(SCHAR_T *rssi)
{
return OPRT_OK;
}
OPERATE_RET tuya_adapter_bt_scan_assign(INOUT ty_bt_scan_info_t *info)
{
return OPRT_OK;
}
OPERATE_RET tuya_adapter_bt_scan_init(IN TY_BT_SCAN_ADV_CB scan_adv_cb)
{
return OPRT_OK;
}
OPERATE_RET tuya_adapter_bt_scan_start(VOID_T)
{
return OPRT_OK;
}
OPERATE_RET tuya_adapter_bt_scan_stop(VOID_T)
{
return OPRT_OK;
}
VOID example_ble_connected(VOID)
{
tuya_ble_data_buf_t data = {0};
if (bt_msg_cb == NULL) {
return;
}
data.len = 0;
data.data = NULL;
bt_msg_cb(0, TY_BT_EVENT_CONNECTED, &data);
}
VOID example_ble_disconnected(VOID)
{
tuya_ble_data_buf_t data = {0};
if (bt_msg_cb == NULL) {
return;
}
data.len = 0;
data.data = NULL;
bt_msg_cb(0, TY_BT_EVENT_DISCONNECTED, &data);
bt_msg_cb(0, TY_BT_EVENT_ADV_READY, &data);
}
/**
* 需要实现的逻辑:应用接收到蓝牙数据,把数据中的 GATT Server Attribute Value 传给 SDK 处理。
*/
VOID example_ble_rx(BYTE_T *rx_data, UINT8_T rx_len)
{
tuya_ble_data_buf_t data = {0};
if (bt_msg_cb == NULL) {
return;
}
data.len = rx_len;
data.data = rx_data;
bt_msg_cb(0, TY_BT_EVENT_RX_DATA, &data);
}
/**
* 需要实现的逻辑:应用接收到蓝牙数据,把数据中的 GATT Server Attribute Value 传给 SDK 处理。
*/
VOID example_ble_rx(BYTE_T *rx_data, UINT8_T rx_len)
{
tuya_ble_data_buf_t data = {0};
if (bt_msg_cb == NULL) {
return;
}
data.len = rx_len;
data.data = rx_data;
bt_msg_cb(0, TY_BT_EVENT_RX_DATA, &data);
}
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈