更新时间:2024-06-25 06:16:42下载pdf
本文介绍 SDK 中的 蓝牙配网 功能。首先,将阐述其工作原理,随后详细说明如何实施蓝牙配网,以便实现将网关设备接入涂鸦开发者平台的目标。
蓝牙配网是指网关设备与手机 App 通过蓝牙通道传输路由器 SSID、密码以及激活 Token,网关设备接收这些信息后,使用相应的 SSID 和密码连接路由器,并利用激活 Token 绑定到涂鸦开发者平台。
SDK 已内置配网流程的业务逻辑,通过定义一套 TuyaOS Kernel Layer(简称 TKL)接口来屏蔽硬件与系统的差异,您只需适配 TKL 接口即可。
蓝牙配网的工作原理如下:
蓝牙配网过程中,设备与 App 传输的数据均由 SDK 处理,您无需关心数据内容,只负责接收和发送即可。
蓝牙配网的流程图如下图所示:
tkl_ble_stack_init
OPERATE_RET tkl_ble_stack_init(UCHAR_T role);
初始化 SDK 时调用此接口。您可以在接口中初始化蓝牙协议栈,可以空实现。
tkl_ble_gap_callback_register
OPERATE_RET tkl_ble_gap_callback_register(CONST TKL_BLE_GAP_EVT_FUNC_CB gap_evt);
注册蓝牙 Generic Access Profile(GAP)事件回调接口,您应将 gap_evt
函数指针保存至全局变量。当监听到 Central 建立或断开连接时,通过此函数指针,使用 TKL_BLE_GAP_EVT_CONNECT
或 TKL_BLE_GAP_EVT_DISCONNECT
通知 SDK。
tkl_ble_gatt_callback_register
OPERATE_RET tkl_ble_gatt_callback_register(CONST TKL_BLE_GATT_EVT_FUNC_CB gatt_evt);
注册蓝牙 Generic Attribute Profile(GATT)事件回调接口,您应将 gatt_evt
函数指针保存至全局变量。当接收到 GATT 数据时,通过此函数指针,使用 TKL_BLE_GATT_EVT_WRITE_REQ
事件将数据传递给 SDK 进行处理。
tkl_ble_gap_adv_rsp_data_set
OPERATE_RET tkl_ble_gap_adv_rsp_data_set(TKL_BLE_DATA_T CONST *p_adv, TKL_BLE_DATA_T CONST *p_scan_rsp)
设置广播和扫描响应数据接口。
tkl_ble_gap_adv_start
OPERATE_RET tkl_ble_gap_adv_start(TKL_BLE_GAP_ADV_PARAMS_T CONST *p_adv_params);
开启广播接口,广播的数据采用 tkl_ble_gap_adv_rsp_data_set
接口提供的数据。
tkl_ble_gap_adv_stop
OPERATE_RET tkl_ble_gap_adv_stop(VOID);
关闭广播接口。
tkl_ble_gatts_value_notify
OPERATE_RET tkl_ble_gatts_value_notify(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length);
发送 Characteristic 值变化通知接口。SDK 通过蓝牙通道给 App 发送数据时调用此接口。
tkl_ble_gatts_service_add
OPERATE_RET tkl_ble_gatts_service_add(TKL_BLE_GATTS_PARAMS_T *p_service);
添加 GATT Service 接口。
Characteristic Handle 需要自行处理,涉及以下两处:
tkl_ble_gatts_service_add
接口中的 p_service
参数,需要把 Characteristic Handle 赋值给 p_service
里的 p_service[i].p_char[j].handle
,其中 i
表示 Service 的索引,j
表示 Characteristic 的索引。TKL_BLE_GATT_EVT_WRITE_REQ
事件,需要把 Characteristic Handle 赋值给 event.gatt_event.write_report.char_handle
。如果蓝牙的实现不关心 Characteristic Handle,可以直接把 Characteristic 的 UUID 赋值给 Handle。更多信息,参考使用示例的代码。
蓝牙广播和扫描响应的数据格式:
蓝牙广播和扫描响应数据遵循蓝牙规范,SDK 已经把广播数据和扫描响应数据封装成 AD Structure 格式,您无需关注数据内容,直接透传即可。
App 和网关设备建立蓝牙连接后,采用蓝牙 The Attribute Protocol(ATT)协议来传输数据,发送数据和接收数据均由 SDK 处理,您只需适配 TKL 接口即可。
Service 和 Characteristic 定义如下:
Service UUID: 0x1910
。
Characteristic | UUID | 属性 | 说明 |
---|---|---|---|
Write Characteristic | 0x2B11 | Write Without Response | App 向蓝牙低功耗设备发送数据 |
Notify Characteristic | 0x2B10 | Notify | 蓝牙低功耗设备向 App 发送通知信息 |
以下示例展示了使用如何使用 BlueZ 来实现蓝牙配网功能。代码仅提供实现思路,实际生产代码应考虑内存管理和性能。
#include "tkl_bluetooth.h"
#include "tuya_cloud_types.h"
#include "uni_log.h"
#include "mem_pool.h"
#include "uni_queue.h"
#include "tuya_bluez_api.h"
#define BLE_CONN_HANDLE 0x0001
typedef struct {
UINT16_T uuid;
USHORT_T length;
UCHAR_T data[0];
} BLE_CACHE_DATA_T;
STATIC TKL_BLE_GAP_EVT_FUNC_CB __gap_evt_cb = NULL;
STATIC TKL_BLE_GATT_EVT_FUNC_CB __gatt_evt_cb = NULL;
STATIC BOOL_T g_connected = FALSE;
STATIC P_QUEUE_CLASS g_cache_queue = NULL;
STATIC VOID __gatt_write_request_event_cb(UINT16_T uuid, UINT8_T *data, UINT8_T len)
{
INT_T i = 0;
BLE_CACHE_DATA_T *cache_data = NULL;
PR_DEBUG("recv write request event, uuid: 0x%04x", uuid);
for (i = 0; i < len; i++) {
PR_DEBUG_RAW(" %02x", data[i]);
}
PR_DEBUG_RAW("\r\n");
/**
* FIXME: BlueZ uses D-BUS for inter-process communication. In our
* testing, we faced a problem where data was received before
* the connection event was detected. To address this, we are
* introducing a caching mechanism.
*/
if (!g_connected) {
cache_data = (BLE_CACHE_DATA_T *)Malloc(SIZEOF(BLE_CACHE_DATA_T) + len);
if (!cache_data) {
PR_ERR("Malloc err");
return;
}
cache_data->uuid = uuid;
cache_data->length = len;
memcpy(cache_data->data, data, len);
InQueue(g_cache_queue, (unsigned char *)&cache_data, 1);
return;
}
TKL_BLE_GATT_PARAMS_EVT_T event;
memset(&event, 0, SIZEOF(TKL_BLE_GATT_PARAMS_EVT_T));
event.result = 0;
event.type = TKL_BLE_GATT_EVT_WRITE_REQ;
event.conn_handle = BLE_CONN_HANDLE;
event.gatt_event.write_report.char_handle = uuid;
event.gatt_event.write_report.report.p_data = data;
event.gatt_event.write_report.report.length = len;
if (__gatt_evt_cb)
__gatt_evt_cb(&event);
}
STATIC VOID __gap_connect_event_cb(INT_T status)
{
PR_DEBUG("recv connect event, status: %d", status);
g_connected = status;
TKL_BLE_GAP_PARAMS_EVT_T event;
memset(&event, 0, SIZEOF(TKL_BLE_GAP_PARAMS_EVT_T));
event.result = 0;
if (status) {
event.type = TKL_BLE_GAP_EVT_CONNECT;
} else {
event.type = TKL_BLE_GAP_EVT_DISCONNECT;
}
event.conn_handle = BLE_CONN_HANDLE;
event.gap_event.connect.role = TKL_BLE_ROLE_SERVER;
if (__gap_evt_cb) {
__gap_evt_cb(&event);
}
/**
* FIXME: BlueZ uses D-BUS for inter-process communication. In our
* testing, we faced a problem where data was received before
* the connection event was detected. To address this, we are
* introducing a caching mechanism.
*/
while (GetCurQueNum(g_cache_queue)) {
BLE_CACHE_DATA_T *cache_data = NULL;
if (!OutQueue(g_cache_queue, (unsigned char *)&cache_data, 1)) {
break;
}
__gatt_write_request_event_cb(cache_data->uuid, cache_data->data, cache_data->length);
Free(cache_data);
}
}
OPERATE_RET tkl_ble_stack_init(UCHAR_T role)
{
PR_INFO("tkl_ble_stack_init");
g_cache_queue = CreateQueueObj(32, SIZEOF(BLE_CACHE_DATA_T));
if (!g_cache_queue) {
PR_ERR("CreateQueueObj error");
return OPRT_COM_ERROR;
}
tuya_bluez_init();
tuya_bluez_le_register_connect_event(__gap_connect_event_cb);
tuya_bluez_le_register_write_req_event(__gatt_write_request_event_cb);
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_callback_register(CONST TKL_BLE_GAP_EVT_FUNC_CB gap_evt)
{
PR_INFO("tkl_ble_gap_callback_register");
__gap_evt_cb = gap_evt;
return OPRT_OK;
}
OPERATE_RET tkl_ble_gatt_callback_register(CONST TKL_BLE_GATT_EVT_FUNC_CB gatt_evt)
{
PR_INFO("tkl_ble_gatt_callback_register");
__gatt_evt_cb = gatt_evt;
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_adv_start(TKL_BLE_GAP_ADV_PARAMS_T CONST *p_adv_params)
{
PR_INFO("tkl_ble_gap_adv_start");
le_set_adv_params_t adv_param = {
.advtype = p_adv_params->adv_type,
.min_interval = p_adv_params->adv_interval_min,
.max_interval = p_adv_params->adv_interval_max,
};
tuya_bluez_le_set_adv_params(&adv_param);
tuya_bluez_le_set_adv_enable(1);
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_adv_stop(VOID)
{
tuya_bluez_le_set_adv_enable(0);
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_adv_rsp_data_set(TKL_BLE_DATA_T CONST *p_adv, TKL_BLE_DATA_T CONST *p_scan_rsp)
{
PR_INFO("tkl_ble_gap_adv_rsp_data_set");
tuya_bluez_le_set_adv_data(p_adv->p_data, p_adv->length);
tuya_bluez_le_set_scan_rsp_data(p_scan_rsp->p_data, p_scan_rsp->length);
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_adv_rsp_data_update(TKL_BLE_DATA_T CONST *p_adv, TKL_BLE_DATA_T CONST *p_scan_rsp)
{
PR_INFO("tkl_ble_gap_adv_rsp_data_update");
return tkl_ble_gap_adv_rsp_data_set(p_adv, p_scan_rsp);
}
OPERATE_RET tkl_ble_gatts_service_add(TKL_BLE_GATTS_PARAMS_T *p_service)
{
INT_T i = 0, j = 0;
UINT8_T svc_num = 0;
le_gatt_service_t *gatt_svc = NULL;
PR_INFO("tkl_ble_gatts_service_add");
svc_num = p_service->svc_num;
gatt_svc = (le_gatt_service_t *)Malloc(svc_num * SIZEOF(le_gatt_service_t));
if (!gatt_svc) {
return OPRT_MALLOC_FAILED;
}
memset(gatt_svc, 0, svc_num * SIZEOF(le_gatt_service_t));
for (i = 0; i < svc_num; i++) {
/* Service Information */
TKL_BLE_SERVICE_PARAMS_T *p_service_param = &p_service->p_service[i];
gatt_svc[i].uuid = p_service_param->svc_uuid.uuid.uuid16;
gatt_svc[i].type = p_service_param->type;
gatt_svc[i].chr_num = p_service_param->char_num;
/* Characteristic information*/
UINT8_T chr_num = p_service_param->char_num;
le_gatt_characteristic_t *gatt_chr = (le_gatt_characteristic_t *)Malloc(chr_num * SIZEOF(le_gatt_characteristic_t));
if (!gatt_chr) {
Free(gatt_svc);
return OPRT_MALLOC_FAILED;
}
memset(gatt_chr, 0, chr_num * SIZEOF(le_gatt_characteristic_t));
for (j = 0; j < gatt_svc[i].chr_num; j++) {
gatt_chr[j].uuid = p_service_param->p_char[j].char_uuid.uuid.uuid16;
gatt_chr[j].property = p_service_param->p_char[j].property;
/**
* FIXME: BlueZ does not focus on the handle, so in this
* case, we're using the uuid as
* the handle value to differentiate the specific
* characteristic.
*/
p_service_param->p_char[j].handle = gatt_chr[j].uuid;
}
gatt_svc[i].chr = gatt_chr;
}
tuya_bluez_le_add_gatt_service(gatt_svc, svc_num);
for (i = 0; i < svc_num; i++) {
if (gatt_svc[i].chr) {
Free(gatt_svc[i].chr);
}
}
Free(gatt_svc);
return OPRT_OK;
}
OPERATE_RET tkl_ble_gatts_value_notify(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
PR_INFO("tkl_ble_gatts_value_notify, chr_handle: 0x%04x", char_handle);
tuya_bluez_le_gatts_value_notify(char_handle, p_data, length);
return OPRT_OK;
}
OPERATE_RET tkl_ble_stack_deinit(UCHAR_T role)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_stack_gatt_link(USHORT_T *p_link)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_addr_set(TKL_BLE_GAP_ADDR_T CONST *p_peer_addr)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_address_get(TKL_BLE_GAP_ADDR_T *p_peer_addr)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_scan_start(TKL_BLE_GAP_SCAN_PARAMS_T CONST *p_scan_params)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_scan_stop(VOID)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_connect(TKL_BLE_GAP_ADDR_T CONST *p_peer_addr, TKL_BLE_GAP_SCAN_PARAMS_T CONST *p_scan_params, TKL_BLE_GAP_CONN_PARAMS_T CONST *p_conn_params)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_disconnect(USHORT_T conn_handle, UCHAR_T hci_reason)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_conn_param_update(USHORT_T conn_handle, TKL_BLE_GAP_CONN_PARAMS_T CONST *p_conn_params)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_tx_power_set(UCHAR_T role, INT_T tx_power)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_rssi_get(USHORT_T conn_handle)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gap_name_set(CHAR_T *p_name)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gatts_value_set(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gatts_value_get(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gatts_value_indicate(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gatts_exchange_mtu_reply(USHORT_T conn_handle, USHORT_T server_rx_mtu)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gattc_all_service_discovery(USHORT_T conn_handle)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gattc_all_char_discovery(USHORT_T conn_handle, USHORT_T start_handle, USHORT_T end_handle)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gattc_char_desc_discovery(USHORT_T conn_handle, USHORT_T start_handle, USHORT_T end_handle)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gattc_write_without_rsp(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gattc_write(USHORT_T conn_handle, USHORT_T char_handle, UCHAR_T *p_data, USHORT_T length)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gattc_read(USHORT_T conn_handle, USHORT_T char_handle)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_gattc_exchange_mtu_request(USHORT_T conn_handle, USHORT_T client_rx_mtu)
{
return OPRT_OK;
}
OPERATE_RET tkl_ble_vendor_command_control(USHORT_T opcode, VOID_T *user_data, USHORT_T data_len)
{
return OPRT_NOT_SUPPORTED;
}
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include "uni_log.h"
#include "tuya_hci.h"
int tuya_hci_le_set_adv_params(uint16_t min_interval, uint16_t max_interval, uint8_t advtype)
{
int device = 0;
uint8_t status = 0;
device = hci_open_dev(hci_get_route(NULL));
if (device < 0) {
return LE_OPEN_ERROR;
}
le_set_advertising_parameters_cp adv_params_cp;
memset(&adv_params_cp, 0, sizeof(adv_params_cp));
adv_params_cp.advtype = advtype;
adv_params_cp.min_interval = htobs(min_interval);
adv_params_cp.max_interval = htobs(max_interval);
adv_params_cp.chan_map = 7;
struct hci_request req;
memset(&req, 0, sizeof(req));
req.ogf = OGF_LE_CTL;
req.ocf = OCF_LE_SET_ADVERTISING_PARAMETERS;
req.cparam = &adv_params_cp;
req.clen = LE_SET_ADVERTISING_PARAMETERS_CP_SIZE;
req.rparam = &status;
req.rlen = 1;
if (hci_send_req(device, &req, 1000) != 0) {
hci_close_dev(device);
return LE_READ_ERROR;
}
PR_DEBUG("Set Advertisement Parameters, status 0x%02x", status);
hci_close_dev(device);
return LE_SUCCESS;
}
int tuya_hci_le_set_adv_enable(bool enable)
{
int device = 0;
uint8_t status = 0;
device = hci_open_dev(hci_get_route(NULL));
if (device < 0) {
return LE_OPEN_ERROR;
}
le_set_advertise_enable_cp advertise_cp;
memset(&advertise_cp, 0, sizeof(advertise_cp));
advertise_cp.enable = enable;
struct hci_request req;
memset(&req, 0, sizeof(req));
req.ogf = OGF_LE_CTL;
req.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
req.cparam = &advertise_cp;
req.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
req.rparam = &status;
req.rlen = 1;
if (hci_send_req(device, &req, 1000) != 0) {
hci_close_dev(device);
return LE_READ_ERROR;
}
PR_DEBUG("Set Advertisement %s, status 0x%02x", enable ? "Enable" : "Disable", status);
hci_close_dev(device);
return LE_SUCCESS;
}
int tuya_hci_le_set_adv_data(uint8_t *data, uint8_t len)
{
int device = 0;
uint8_t status = 0;
if ((data == NULL) || (len == 0)) {
return LE_INVALID_PARAM;
}
device = hci_open_dev(hci_get_route(NULL));
if (device < 0) {
return LE_OPEN_ERROR;
}
le_set_advertising_data_cp adv_data_cp;
memset(&adv_data_cp, 0, sizeof(adv_data_cp));
memcpy(adv_data_cp.data, data, len);
adv_data_cp.length = len;
struct hci_request req;
memset(&req, 0, sizeof(req));
req.ogf = OGF_LE_CTL;
req.ocf = OCF_LE_SET_ADVERTISING_DATA;
req.cparam = &adv_data_cp;
req.clen = LE_SET_ADVERTISING_DATA_CP_SIZE;
req.rparam = &status;
req.rlen = 1;
if (hci_send_req(device, &req, 1000) != 0) {
hci_close_dev(device);
return LE_READ_ERROR;
}
PR_DEBUG("Set Advertisement Data, status 0x%02x", status);
hci_close_dev(device);
return LE_SUCCESS;
}
int tuya_hci_le_set_scan_rsp_data(uint8_t *data, uint8_t len)
{
int device = 0;
uint8_t status = 0;
if ((data == NULL) || (len == 0)) {
return LE_INVALID_PARAM;
}
device = hci_open_dev(hci_get_route(NULL));
if (device < 0) {
return LE_OPEN_ERROR;
}
le_set_scan_response_data_cp scan_rsp_data_cp;
memset(&scan_rsp_data_cp, 0, sizeof(scan_rsp_data_cp));
memcpy(scan_rsp_data_cp.data, data, len);
scan_rsp_data_cp.length = len;
struct hci_request req;
memset(&req, 0, sizeof(req));
req.ogf = OGF_LE_CTL;
req.ocf = OCF_LE_SET_SCAN_RESPONSE_DATA;
req.cparam = &scan_rsp_data_cp;
req.clen = LE_SET_SCAN_RESPONSE_DATA_CP_SIZE;
req.rparam = &status;
req.rlen = 1;
if (hci_send_req(device, &req, 1000) != 0) {
hci_close_dev(device);
return LE_READ_ERROR;
}
PR_DEBUG("Set Scan Response Data, status 0x%02x", status);
hci_close_dev(device);
return LE_SUCCESS;
}
/**
* Copy from gatt-service.c
*/
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <dbus/dbus.h>
#include "uni_log.h"
#include "gdbus/gdbus.h"
#include "tuya_gatt.h"
#define ERROR_INTERFACE "org.bluez.Error"
#define DEVICE_INFACE "org.bluez.Device1"
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
#define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1"
#define PATH_PREFIX "/com/tuya"
struct characteristic
{
char *service;
char *uuid;
char *path;
uint8_t *value;
int vlen;
uint8_t props;
};
struct descriptor
{
struct characteristic *chr;
char *uuid;
char *path;
uint8_t *value;
int vlen;
uint8_t props;
};
static DBusConnection *connection = NULL;
static GDBusClient *client = NULL;
static GSList *chr_list;
static void (*__gatt_connect_event)(int status) = NULL;
static void (*__gatt_write_request_event)(uint16_t uuid, uint8_t *data, uint8_t len) = NULL;
static gboolean desc_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct descriptor *desc = user_data;
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &desc->uuid);
return TRUE;
}
static gboolean desc_get_characteristic(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct descriptor *desc = user_data;
dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
&desc->chr->path);
return TRUE;
}
static bool desc_read(struct descriptor *desc, DBusMessageIter *iter)
{
DBusMessageIter array;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_BYTE_AS_STRING, &array);
if (desc->vlen && desc->value)
dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
&desc->value, desc->vlen);
dbus_message_iter_close_container(iter, &array);
return true;
}
static gboolean desc_get_value(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct descriptor *desc = user_data;
PR_DEBUG("Descriptor(%s): Get(\"Value\")", desc->uuid);
return desc_read(desc, iter);
}
static void desc_write(struct descriptor *desc, const uint8_t *value, int len)
{
g_free(desc->value);
desc->value = g_memdup(value, len);
desc->vlen = len;
g_dbus_emit_property_changed(connection, desc->path,
GATT_DESCRIPTOR_IFACE, "Value");
}
static int parse_value(DBusMessageIter *iter, const uint8_t **value, int *len)
{
DBusMessageIter array;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return -EINVAL;
dbus_message_iter_recurse(iter, &array);
dbus_message_iter_get_fixed_array(&array, value, len);
return 0;
}
static void desc_set_value(const GDBusPropertyTable *property,
DBusMessageIter *iter,
GDBusPendingPropertySet id, void *user_data)
{
struct descriptor *desc = user_data;
const uint8_t *value;
int len;
PR_DEBUG("Descriptor(%s): Set(\"Value\", ...)", desc->uuid);
if (parse_value(iter, &value, &len))
{
PR_ERR("Invalid value for Set('Value'...)");
g_dbus_pending_property_error(id,
ERROR_INTERFACE ".InvalidArguments",
"Invalid arguments in method call");
return;
}
desc_write(desc, value, len);
g_dbus_pending_property_success(id);
}
static gboolean desc_get_props(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
struct descriptor *desc = data;
DBusMessageIter array;
int i;
char *prop = NULL;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING, &array);
if (desc->props & LE_GATT_CHR_PROP_WRITE_NO_RSP) {
prop = "write-without-response";
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &prop);
}
if (desc->props & LE_GATT_CHR_PROP_WRITE) {
prop = "write";
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &prop);
}
if (desc->props & LE_GATT_CHR_PROP_NOTIFY) {
prop = "notify";
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &prop);
}
if (desc->props & LE_GATT_CHR_PROP_INDICATE) {
prop = "indicate";
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &prop);
}
if (desc->props & LE_GATT_CHR_PROP_READ) {
prop = "read";
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &prop);
}
dbus_message_iter_close_container(iter, &array);
return TRUE;
}
static const GDBusPropertyTable desc_properties[] = {
{"UUID", "s", desc_get_uuid},
{"Characteristic", "o", desc_get_characteristic},
{"Value", "ay", desc_get_value, desc_set_value, NULL},
{"Flags", "as", desc_get_props, NULL, NULL},
{}};
static gboolean chr_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct characteristic *chr = user_data;
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
return TRUE;
}
static gboolean chr_get_service(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct characteristic *chr = user_data;
dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
&chr->service);
return TRUE;
}
static bool chr_read(struct characteristic *chr, DBusMessageIter *iter)
{
DBusMessageIter array;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_BYTE_AS_STRING, &array);
dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
&chr->value, chr->vlen);
dbus_message_iter_close_container(iter, &array);
return true;
}
static gboolean chr_get_value(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct characteristic *chr = user_data;
PR_DEBUG("Characteristic(%s): Get(\"Value\")", chr->uuid);
return chr_read(chr, iter);
}
static gboolean chr_get_props(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
struct characteristic *chr = data;
DBusMessageIter array;
int i;
char *prop = NULL;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING, &array);
if (chr->props & LE_GATT_CHR_PROP_WRITE_NO_RSP) {
prop = "write-without-response";
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &prop);
}
if (chr->props & LE_GATT_CHR_PROP_WRITE) {
prop = "write";
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &prop);
}
if (chr->props & LE_GATT_CHR_PROP_NOTIFY) {
prop = "notify";
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &prop);
}
if (chr->props & LE_GATT_CHR_PROP_INDICATE) {
prop = "indicate";
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &prop);
}
if (chr->props & LE_GATT_CHR_PROP_READ) {
prop = "read";
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &prop);
}
dbus_message_iter_close_container(iter, &array);
return TRUE;
}
static void chr_write(struct characteristic *chr, const uint8_t *value, int len)
{
g_free(chr->value);
chr->value = g_memdup(value, len);
chr->vlen = len;
g_dbus_emit_property_changed(connection, chr->path, GATT_CHR_IFACE,
"Value");
}
static void chr_set_value(const GDBusPropertyTable *property,
DBusMessageIter *iter,
GDBusPendingPropertySet id, void *user_data)
{
struct characteristic *chr = user_data;
const uint8_t *value;
int len;
PR_DEBUG("Characteristic(%s): Set('Value', ...)", chr->uuid);
if (!parse_value(iter, &value, &len))
{
PR_ERR("Invalid value for Set('Value'...)");
g_dbus_pending_property_error(id,
ERROR_INTERFACE ".InvalidArguments",
"Invalid arguments in method call");
return;
}
chr_write(chr, value, len);
g_dbus_pending_property_success(id);
}
static const GDBusPropertyTable chr_properties[] = {
{"UUID", "s", chr_get_uuid},
{"Service", "o", chr_get_service},
{"Value", "ay", chr_get_value, chr_set_value, NULL},
{"Flags", "as", chr_get_props, NULL, NULL},
{}};
static gboolean service_get_primary(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
dbus_bool_t primary = TRUE;
dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &primary);
return TRUE;
}
static gboolean service_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
const char *uuid = user_data;
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
return TRUE;
}
static gboolean service_get_includes(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
#if 0
const char *uuid = user_data;
char service_path[100] = {
0,
};
DBusMessageIter array;
char *p = NULL;
snprintf(service_path, 100, "/service3");
printf("Get Includes: %s\n", uuid);
p = service_path;
printf("Includes path: %s\n", p);
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_OBJECT_PATH_AS_STRING, &array);
dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
&p);
snprintf(service_path, 100, "/service2");
p = service_path;
printf("Get Includes: %s\n", p);
dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
&p);
dbus_message_iter_close_container(iter, &array);
#endif
return TRUE;
}
static gboolean service_exist_includes(const GDBusPropertyTable *property,
void *user_data)
{
return FALSE;
}
static const GDBusPropertyTable service_properties[] = {
{"Primary", "b", service_get_primary},
{"UUID", "s", service_get_uuid},
{"Includes", "ao", service_get_includes, NULL,
service_exist_includes},
{}};
static void chr_iface_destroy(gpointer user_data)
{
struct characteristic *chr = user_data;
g_free(chr->uuid);
g_free(chr->service);
g_free(chr->value);
g_free(chr->path);
g_free(chr);
}
static void desc_iface_destroy(gpointer user_data)
{
struct descriptor *desc = user_data;
g_free(desc->uuid);
g_free(desc->value);
g_free(desc->path);
g_free(desc);
}
static int parse_options(DBusMessageIter *iter, const char **device)
{
DBusMessageIter dict;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return -EINVAL;
dbus_message_iter_recurse(iter, &dict);
while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY)
{
const char *key;
DBusMessageIter value, entry;
int var;
dbus_message_iter_recurse(&dict, &entry);
dbus_message_iter_get_basic(&entry, &key);
dbus_message_iter_next(&entry);
dbus_message_iter_recurse(&entry, &value);
var = dbus_message_iter_get_arg_type(&value);
if (strcasecmp(key, "device") == 0)
{
if (var != DBUS_TYPE_OBJECT_PATH)
return -EINVAL;
dbus_message_iter_get_basic(&value, device);
PR_DEBUG("Device: %s", *device);
}
dbus_message_iter_next(&dict);
}
return 0;
}
static DBusMessage *chr_read_value(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
struct characteristic *chr = user_data;
DBusMessage *reply;
DBusMessageIter iter;
const char *device;
if (!dbus_message_iter_init(msg, &iter))
return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
if (parse_options(&iter, &device))
return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
reply = dbus_message_new_method_return(msg);
if (!reply)
return g_dbus_create_error(msg, DBUS_ERROR_NO_MEMORY,
"No Memory");
dbus_message_iter_init_append(reply, &iter);
chr_read(chr, &iter);
return reply;
}
static DBusMessage *chr_write_value(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
struct characteristic *chr = user_data;
DBusMessageIter iter;
const uint8_t *value;
int len;
const char *device;
dbus_message_iter_init(msg, &iter);
if (parse_value(&iter, &value, &len))
return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
if (parse_options(&iter, &device))
return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
chr_write(chr, value, len);
if (__gatt_write_request_event) {
__gatt_write_request_event((uint16_t)strtol(chr->uuid, NULL, 16), (uint8_t *)value, len);
}
return dbus_message_new_method_return(msg);
}
static DBusMessage *chr_start_notify(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
return g_dbus_create_error(msg, DBUS_ERROR_NOT_SUPPORTED,
"Not Supported");
}
static DBusMessage *chr_stop_notify(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
return g_dbus_create_error(msg, DBUS_ERROR_NOT_SUPPORTED,
"Not Supported");
}
static const GDBusMethodTable chr_methods[] = {
{GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({"options", "a{sv}"}),
GDBUS_ARGS({"value", "ay"}),
chr_read_value)},
{GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({"value", "ay"}, {"options", "a{sv}"}),
NULL, chr_write_value)},
{GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chr_start_notify)},
{GDBUS_METHOD("StopNotify", NULL, NULL, chr_stop_notify)},
{}};
static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
struct descriptor *desc = user_data;
DBusMessage *reply;
DBusMessageIter iter;
const char *device;
if (!dbus_message_iter_init(msg, &iter))
return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
if (parse_options(&iter, &device))
return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
reply = dbus_message_new_method_return(msg);
if (!reply)
return g_dbus_create_error(msg, DBUS_ERROR_NO_MEMORY,
"No Memory");
dbus_message_iter_init_append(reply, &iter);
desc_read(desc, &iter);
return reply;
}
static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
struct descriptor *desc = user_data;
DBusMessageIter iter;
const char *device;
const uint8_t *value;
int len;
if (!dbus_message_iter_init(msg, &iter))
return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
if (parse_value(&iter, &value, &len))
return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
if (parse_options(&iter, &device))
return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
desc_write(desc, value, len);
return dbus_message_new_method_return(msg);
}
static const GDBusMethodTable desc_methods[] = {
{GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({"options", "a{sv}"}),
GDBUS_ARGS({"value", "ay"}),
desc_read_value)},
{GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({"value", "ay"}, {"options", "a{sv}"}),
NULL, desc_write_value)},
{}};
static void register_app_reply(DBusMessage *reply, void *user_data)
{
DBusError derr;
dbus_error_init(&derr);
dbus_set_error_from_message(&derr, reply);
if (dbus_error_is_set(&derr))
PR_DEBUG("RegisterApplication: %s", derr.message);
else
PR_DEBUG("RegisterApplication: OK");
dbus_error_free(&derr);
}
static void register_app_setup(DBusMessageIter *iter, void *user_data)
{
const char *path = "/";
DBusMessageIter dict;
dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
/* TODO: Add options dictionary */
dbus_message_iter_close_container(iter, &dict);
}
static void register_app(GDBusProxy *proxy)
{
if (!g_dbus_proxy_method_call(proxy, "RegisterApplication",
register_app_setup, register_app_reply,
NULL, NULL))
{
PR_ERR("Unable to call RegisterApplication");
return;
}
}
static void proxy_added_cb(GDBusProxy *proxy, void *user_data)
{
const char *iface;
iface = g_dbus_proxy_get_interface(proxy);
if (g_strcmp0(iface, GATT_MGR_IFACE))
return;
register_app(proxy);
}
static void property_changed_cb(GDBusProxy *proxy, const char *name,
DBusMessageIter *iter, void *user_data)
{
dbus_bool_t conn_status = FALSE;
const char *interface = g_dbus_proxy_get_interface(proxy);
const char *path = g_dbus_proxy_get_path(proxy);
PR_DEBUG("property_changed, path: %s, iface: %s, name: %s", path, interface, name);
if (!g_strcmp0(interface, DEVICE_INFACE)) {
if (!g_strcmp0(name, "ServicesResolved")) {
dbus_message_iter_get_basic(iter, &conn_status);
if (__gatt_connect_event) {
__gatt_connect_event(conn_status);
}
}
}
}
int tuya_gatt_init(void)
{
connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
if (!g_dbus_attach_object_manager(connection)) {
PR_ERR("g_dbus_attach_object_manager error");
return LE_COM_ERROR;
}
client = g_dbus_client_new(connection, "org.bluez", "/");
g_dbus_client_set_proxy_handlers(client, proxy_added_cb, NULL, property_changed_cb,
NULL);
return LE_SUCCESS;
}
int tuya_gatt_register_service(uint16_t uuid)
{
if (!connection) {
PR_WARN("Connection not initialized");
return LE_COM_ERROR;
}
char path[64] = {0};
char *uuid_str = NULL;
uuid_str = g_strdup_printf("%04x", uuid);
g_snprintf(path, sizeof(path), "%s/service%s", PATH_PREFIX, uuid_str);
if (!g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
NULL, NULL, service_properties,
uuid_str, g_free))
{
PR_ERR("Couldn't register service interface");
g_free(uuid_str);
return LE_COM_ERROR;
}
return LE_SUCCESS;
}
int tuya_gatt_register_characteristic(uint16_t svc_uuid, uint16_t chr_uuid, uint8_t props,
uint16_t desc_uuid, uint8_t desc_props)
{
if (!connection) {
PR_WARN("Connection not initialized");
return LE_COM_ERROR;
}
struct characteristic *chr = NULL;
struct descriptor *desc = NULL;
char chr_path[128] = {0};
char svc_path[128] = {0};
char desc_path[128] = {0};
char svc_uuid_str[32] = {0};
char chr_uuid_str[32] = {0};
char desc_uuid_str[32] = {0};
g_snprintf(svc_uuid_str, sizeof(svc_uuid_str), "%04x", svc_uuid);
g_snprintf(chr_uuid_str, sizeof(chr_uuid_str), "%04x", chr_uuid);
g_snprintf(svc_path, sizeof(svc_path), "%s/service%s", PATH_PREFIX, svc_uuid_str);
g_snprintf(chr_path, sizeof(chr_path), "%s/service%s/characteristic%s", PATH_PREFIX, svc_uuid_str, chr_uuid_str);
chr = g_new0(struct characteristic, 1);
chr->uuid = g_strdup(chr_uuid_str);
chr->props = props;
chr->service = g_strdup(svc_path);
chr->path = g_strdup(chr_path);
if (!g_dbus_register_interface(connection, chr->path, GATT_CHR_IFACE,
chr_methods, NULL, chr_properties,
chr, chr_iface_destroy))
{
PR_ERR("Couldn't register characteristic interface");
chr_iface_destroy(chr);
return LE_COM_ERROR;
}
chr_list = g_slist_append(chr_list, chr);
if (!desc_uuid)
return LE_SUCCESS;
g_snprintf(desc_uuid_str, sizeof(desc_uuid_str), "%04x", desc_uuid);
g_snprintf(desc_path, sizeof(desc_path), "%s/service%s/characteristic%s/descriptor%s", PATH_PREFIX, svc_uuid_str, chr_uuid_str, desc_uuid_str);
desc = g_new0(struct descriptor, 1);
desc->uuid = g_strdup(desc_uuid_str);
desc->chr = chr;
desc->props = desc_props;
desc->path = g_strdup(desc_path);
if (!g_dbus_register_interface(connection, desc->path,
GATT_DESCRIPTOR_IFACE,
desc_methods, NULL, desc_properties,
desc, desc_iface_destroy))
{
PR_ERR("Couldn't register descriptor interface");
g_dbus_unregister_interface(connection, chr->path,
GATT_CHR_IFACE);
desc_iface_destroy(desc);
return LE_COM_ERROR;
}
return LE_SUCCESS;
}
int tuya_gatt_server_send_characteristic_notification(uint16_t uuid, uint8_t *data, uint8_t len)
{
struct characteristic *chr = NULL;
GSList *c = chr_list;
while (c != NULL) {
chr = (struct characteristic *)c->data;
if (uuid == strtol(chr->uuid, NULL, 16)) {
break;
}
c = c->next;
}
if (c) {
chr_write(chr, data, len);
}
return LE_SUCCESS;
}
void tuya_gatt_register_connect_event(void(*cb)(int status))
{
__gatt_connect_event = cb;
}
void tuya_gatt_register_write_req_event(void(*cb)(uint16_t uuid, uint8_t *data, uint8_t len))
{
__gatt_write_request_event = cb;
}
#include <glib.h>
#include <pthread.h>
#include "uni_log.h"
#include "tuya_bluez_api.h"
#include "tuya_hci.h"
#include "tuya_gatt.h"
static int g_bluez_inited = FALSE;
static GMainLoop *main_loop;
static void *__loop_run(void *arg)
{
g_main_loop_run((GMainLoop *)arg);
return NULL;
}
int tuya_bluez_init(void)
{
int ret = 0;
pthread_t tid;
if (g_bluez_inited) {
PR_WARN("tuya bluez had been initialized");
return LE_SUCCESS;
}
main_loop = g_main_loop_new(NULL, FALSE);
ret = tuya_gatt_init();
if (ret != 0) {
PR_ERR("tuya_gatt_init error");
return ret;
}
pthread_create(&tid, NULL, __loop_run, main_loop);
g_bluez_inited = TRUE;
return LE_SUCCESS;
}
int tuya_bluez_deinit(void)
{
return 0;
}
int tuya_bluez_le_set_adv_params(le_set_adv_params_t *params)
{
if (params == NULL) {
return LE_INVALID_PARAM;
}
return tuya_hci_le_set_adv_params(params->min_interval, params->max_interval, params->advtype);
}
int tuya_bluez_le_set_adv_enable(bool enable)
{
return tuya_hci_le_set_adv_enable(enable);
}
int tuya_bluez_le_set_adv_data(uint8_t *data, uint8_t len)
{
if ((data == NULL) || (len == 0)) {
return LE_INVALID_PARAM;
}
return tuya_hci_le_set_adv_data(data, len);
}
int tuya_bluez_le_set_scan_rsp_data(uint8_t *data, uint8_t len)
{
if ((data == NULL) || (len == 0)) {
return LE_INVALID_PARAM;
}
return tuya_hci_le_set_scan_rsp_data(data, len);
}
int tuya_bluez_le_add_gatt_service(le_gatt_service_t *service, uint8_t service_num)
{
PR_DEBUG("register gatt service, num: %d", service_num);
if ((service == NULL) || (service_num == 0)) {
return LE_INVALID_PARAM;
}
int i = 0, j = 0;
uint16_t svc_uuid = 0x0000;
le_gatt_characteristic_t *p_chr = NULL;
for (i = 0; i < service_num; i++) {
svc_uuid = service[i].uuid;
if (tuya_gatt_register_service(svc_uuid) != LE_SUCCESS) {
PR_ERR("tuya_gatt_register_service error");
continue;
}
p_chr = service[i].chr;
for (j = 0; j < service[i].chr_num; j++) {
if (tuya_gatt_register_characteristic(svc_uuid, p_chr[j].uuid, p_chr[j].property, 0, 0) != LE_SUCCESS) {
PR_ERR("tuya_gatt_register_characteristic error");
break;
}
}
}
return LE_SUCCESS;
}
int tuya_bluez_le_gatts_value_notify(uint16_t uuid, uint8_t *value, uint16_t len)
{
return tuya_gatt_server_send_characteristic_notification(uuid, value, len);
}
void tuya_bluez_le_register_connect_event(void(*cb)(int status))
{
tuya_gatt_register_connect_event(cb);
}
/**
* @brief Register write request event callback
*/
void tuya_bluez_le_register_write_req_event(void(*cb)(uint16_t uuid, uint8_t *data, uint8_t len))
{
tuya_gatt_register_write_req_event(cb);
}
tkl_bluetooth.h
接口,还需要适配 tkl_wifi.h
接口。关于 tkl_wifi.h
接口适配,参考 热点配网。该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈