|
|
@@ -1,3 +1,47 @@
|
|
|
+/*
|
|
|
+@module ble
|
|
|
+@summary 低功耗蓝牙
|
|
|
+@version 1.0
|
|
|
+@date 2025.6.21
|
|
|
+@usage
|
|
|
+-- 本库用于操作BLE对象, 需要搭配bluetooth.init()使用
|
|
|
+-- 详细用法请查阅demo
|
|
|
+
|
|
|
+-- 模式解释
|
|
|
+-- 从机模式(peripheral), 设备会被扫描到, 并且可以被连接
|
|
|
+-- 主机模式(central), 设备会扫描其他设备, 并且可以连接其他设备
|
|
|
+-- 广播者模式(ibeacon), 设备会周期性的广播beacon信息, 但不会被扫描到, 也不会连接其他设备
|
|
|
+-- 观察者模式(scan), 设备会扫描其他设备, 但不会连接其他设备
|
|
|
+
|
|
|
+-- 从机模式(peripheral)的基本流程(概要描述)
|
|
|
+-- 1. 初始化蓝牙框架
|
|
|
+-- 2. 创建BLE对象
|
|
|
+-- local ble_device = bluetooth_device:ble(ble_event_cb)
|
|
|
+-- 3. 创建GATT描述
|
|
|
+-- local att_db = {xxx}
|
|
|
+-- 4. 创建广播信息
|
|
|
+-- ble_device:adv_create(adv_data)
|
|
|
+-- 5. 开始广播
|
|
|
+-- ble_device:adv_start()
|
|
|
+-- 6. 等待连接
|
|
|
+-- 7. 在回调函数中处理连接事件, 如接收数据, 发送数据等
|
|
|
+
|
|
|
+-- 主机模式(central)的基本流程(概要描述)
|
|
|
+-- TODO
|
|
|
+
|
|
|
+-- 广播者模式(ibeacon)的基本流程(概要描述)
|
|
|
+-- TODO
|
|
|
+
|
|
|
+-- 观察者模式(scan)的基本流程(概要描述)
|
|
|
+-- 1. 初始化蓝牙框架
|
|
|
+-- 2. 创建BLE对象
|
|
|
+-- local ble_device = bluetooth_device:ble(ble_event_cb)
|
|
|
+-- 3. 开始扫描
|
|
|
+-- ble_device:scan_start()
|
|
|
+-- 4. 在回调函数中处理扫描事件, 如接收设备信息等
|
|
|
+-- 5. 按需停止扫描
|
|
|
+-- ble_device:scan_stop()
|
|
|
+*/
|
|
|
#include "luat_base.h"
|
|
|
#include "luat_mem.h"
|
|
|
#include "luat_rtos.h"
|
|
|
@@ -22,31 +66,22 @@ int l_ble_callback(lua_State *L, void *ptr)
|
|
|
uint8_t tmpbuff[16] = {0};
|
|
|
|
|
|
lua_geti(L, LUA_REGISTRYINDEX, g_ble_lua_cb_ref);
|
|
|
- if (lua_isfunction(L, -1))
|
|
|
- {
|
|
|
+ if (lua_isfunction(L, -1)){
|
|
|
lua_geti(L, LUA_REGISTRYINDEX, g_bt_ble_ref);
|
|
|
lua_pushinteger(L, evt);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
+ }else{
|
|
|
LLOGE("用户回调函数不存在");
|
|
|
goto exit;
|
|
|
}
|
|
|
|
|
|
- switch (evt)
|
|
|
- {
|
|
|
- case LUAT_BLE_EVENT_WRITE:
|
|
|
- {
|
|
|
+ switch (evt){
|
|
|
+ case LUAT_BLE_EVENT_WRITE:{
|
|
|
luat_ble_write_req_t *write_req = &(param->write_req);
|
|
|
|
|
|
lua_createtable(L, 0, 5);
|
|
|
lua_pushliteral(L, "handle");
|
|
|
lua_pushinteger(L, write_req->handle);
|
|
|
lua_settable(L, -3);
|
|
|
- // luat_ble_uuid_t uuid_service = {0};
|
|
|
- // luat_ble_uuid_t uuid_characteristic = {0};
|
|
|
- // luat_ble_uuid_t uuid_descriptor = {0};
|
|
|
- // luat_ble_handle2uuid(write_req->handle, &uuid_service, &uuid_characteristic, &uuid_descriptor);
|
|
|
|
|
|
lua_pushliteral(L, "uuid_service");
|
|
|
lua_pushlstring(L, (const char *)write_req->uuid_service.uuid, write_req->uuid_service.uuid_type);
|
|
|
@@ -54,8 +89,7 @@ int l_ble_callback(lua_State *L, void *ptr)
|
|
|
lua_pushliteral(L, "uuid_characteristic");
|
|
|
lua_pushlstring(L, (const char *)write_req->uuid_characteristic.uuid, write_req->uuid_characteristic.uuid_type);
|
|
|
lua_settable(L, -3);
|
|
|
- if (write_req->uuid_descriptor.uuid[0] != 0 || write_req->uuid_descriptor.uuid[1] != 0)
|
|
|
- {
|
|
|
+ if (write_req->uuid_descriptor.uuid[0] != 0 || write_req->uuid_descriptor.uuid[1] != 0){
|
|
|
lua_pushliteral(L, "uuid_descriptor");
|
|
|
lua_pushlstring(L, (const char *)write_req->uuid_descriptor.uuid, write_req->uuid_descriptor.uuid_type);
|
|
|
lua_settable(L, -3);
|
|
|
@@ -124,20 +158,17 @@ int l_ble_callback(lua_State *L, void *ptr)
|
|
|
// uint8_t evt_type; /**< Event type (see enum \ref adv_report_info and see enum \ref adv_report_type)*/
|
|
|
|
|
|
lua_call(L, 3, 0);
|
|
|
- if (adv_req->data)
|
|
|
- {
|
|
|
+ if (adv_req->data){
|
|
|
luat_heap_free(adv_req->data);
|
|
|
adv_req->data = NULL;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
- case LUAT_BLE_EVENT_GATT_DONE:
|
|
|
- {
|
|
|
- luat_ble_gatt_service_t **gatt_services = ¶m->gatt_done_ind.gatt_service;
|
|
|
+ case LUAT_BLE_EVENT_GATT_DONE:{
|
|
|
+ luat_ble_gatt_service_t **gatt_services = param->gatt_done_ind.gatt_service;
|
|
|
uint8_t gatt_service_num = param->gatt_done_ind.gatt_service_num;
|
|
|
lua_createtable(L, gatt_service_num, 0);
|
|
|
- for (size_t i = 0; i < gatt_service_num; i++)
|
|
|
- {
|
|
|
+ for (size_t i = 0; i < gatt_service_num; i++){
|
|
|
luat_ble_gatt_service_t *gatt_service = gatt_services[i];
|
|
|
lua_newtable(L);
|
|
|
// servise uuid
|
|
|
@@ -145,14 +176,14 @@ int l_ble_callback(lua_State *L, void *ptr)
|
|
|
lua_rawseti(L, -2, 1);
|
|
|
// characteristics
|
|
|
uint8_t characteristics_num = gatt_service->characteristics_num;
|
|
|
- for (size_t m = 0; m < characteristics_num; m++)
|
|
|
- {
|
|
|
+ for (size_t m = 0; m < characteristics_num; m++){
|
|
|
luat_ble_gatt_chara_t *gatt_chara = &gatt_service->characteristics[m];
|
|
|
lua_newtable(L);
|
|
|
lua_pushlstring(L, (const char *)gatt_chara->uuid, gatt_service->uuid_type);
|
|
|
lua_seti(L, -2, 1);
|
|
|
// Properties
|
|
|
- // lua_seti(L, -2, 2);
|
|
|
+ lua_pushnumber(L, gatt_chara->perm);
|
|
|
+ lua_seti(L, -2, 2);
|
|
|
|
|
|
lua_seti(L, -2, m + 2);
|
|
|
}
|
|
|
@@ -230,6 +261,26 @@ void luat_ble_cb(luat_ble_t *args, luat_ble_event_t ble_event, luat_ble_param_t
|
|
|
luat_msgbus_put(&msg, 0);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+创建一个BLE GATT服务
|
|
|
+@api ble.gatt_create(opts)
|
|
|
+@table GATT服务的描述信息
|
|
|
+@return boolean 是否创建成功
|
|
|
+@usage
|
|
|
+local att_db = { -- Service
|
|
|
+ string.fromHex("FA00"), -- Service UUID, 服务的UUID, 可以是16位、32位或128位
|
|
|
+ -- Characteristic
|
|
|
+ { -- Characteristic 1
|
|
|
+ string.fromHex("EA01"), -- Characteristic UUID Value, 特征的UUID值, 可以是16位、32位或128位
|
|
|
+ ble.NOTIFY | ble.READ | ble.WRITE -- Properties, 对应蓝牙特征的属性, 可以是以下值的组合:
|
|
|
+ -- ble.READ: 可读
|
|
|
+ -- ble.WRITE: 可写
|
|
|
+ -- ble.NOTIFY: 可通知
|
|
|
+ -- ble.INDICATE: 可指示
|
|
|
+ }
|
|
|
+}
|
|
|
+ble_device:gatt_create(att_db)
|
|
|
+*/
|
|
|
static int l_ble_gatt_create(lua_State *L){
|
|
|
if (!lua_isuserdata(L, 1)){
|
|
|
return 0;
|
|
|
@@ -357,6 +408,7 @@ static int l_ble_gatt_create(lua_State *L){
|
|
|
if (LUA_TSTRING == lua_type(L, -1)){
|
|
|
const char *value = luaL_checklstring(L, -1, &len);
|
|
|
characteristics[characteristics_num].value = luat_heap_malloc(len);
|
|
|
+ memcpy(characteristics[characteristics_num].value, value, len);
|
|
|
characteristics[characteristics_num].value_len = len;
|
|
|
}
|
|
|
lua_pop(L, 1);
|
|
|
@@ -381,6 +433,26 @@ error_exit:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+创建一个BLE广播
|
|
|
+@api ble.adv_create(opts)
|
|
|
+@table 广播的描述信息
|
|
|
+@return boolean 是否创建成功
|
|
|
+@usage
|
|
|
+-- 创建广播信息
|
|
|
+ble_device:adv_create({
|
|
|
+ addr_mode = ble.PUBLIC, -- 广播地址模式, 可选值: ble.PUBLIC, ble.RANDOM, ble.RPA, ble.NRPA
|
|
|
+ channel_map = ble.CHNLS_ALL, -- 广播的通道, 可选值: ble.CHNLS_37, ble.CHNLS_38, ble.CHNLS_39, ble.CHNLS_ALL
|
|
|
+ intv_min = 120, -- 广播间隔最小值, 单位为0.625ms, 最小值为20, 最大值为10240
|
|
|
+ intv_max = 120, -- 广播间隔最大值, 单位为0.625ms, 最小值为20, 最大值为10240
|
|
|
+ adv_data = {
|
|
|
+ {ble.FLAGS, string.char(0x06)},
|
|
|
+ {ble.COMPLETE_LOCAL_NAME, "LuatOS123"}, -- 广播的设备名
|
|
|
+ {ble.SERVICE_DATA, string.fromHex("FE01")}, -- 广播的服务数据
|
|
|
+ {ble.MANUFACTURER_SPECIFIC_DATA, string.fromHex("05F0")}
|
|
|
+ }
|
|
|
+})
|
|
|
+*/
|
|
|
static int l_ble_advertising_create(lua_State *L){
|
|
|
if (!lua_isuserdata(L, 1)){
|
|
|
return 0;
|
|
|
@@ -491,16 +563,48 @@ end:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+开始广播
|
|
|
+@api ble.adv_start()
|
|
|
+@return boolean 是否成功
|
|
|
+@usage
|
|
|
+-- 开始广播
|
|
|
+ble_device:adv_start()
|
|
|
+
|
|
|
+-- 提醒, 对于从机模式, 如果被断开了连接, 则需要重新开始广播, 才能被重新搜索到
|
|
|
+*/
|
|
|
static int l_ble_advertising_start(lua_State *L){
|
|
|
lua_pushboolean(L, luat_ble_start_advertising(NULL) ? 0 : 1);
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+主动停止广播
|
|
|
+@api ble.adv_stop()
|
|
|
+@return boolean 是否成功
|
|
|
+@usage
|
|
|
+-- 停止广播
|
|
|
+ble_device:adv_stop()
|
|
|
+*/
|
|
|
static int l_ble_advertising_stop(lua_State *L){
|
|
|
lua_pushboolean(L, luat_ble_stop_advertising(NULL) ? 0 : 1);
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+写入带通知的特征值
|
|
|
+@api ble.write_notify(opts, value)
|
|
|
+@table 特征值的描述信息
|
|
|
+@string value 要写入的值
|
|
|
+@return boolean 是否成功
|
|
|
+@usage
|
|
|
+-- 写入带通知的特征值
|
|
|
+ble_device:write_notify({
|
|
|
+ uuid_service = "FA00", -- 服务的UUID, 可以是16位、32位或128位
|
|
|
+ uuid_characteristic = "EA01", -- 特征的UUID值, 可以是16位、32位或128位
|
|
|
+ uuid_descriptor = "2902" -- 可选, 描述符的UUID值, 可以是16位、32位或128位
|
|
|
+}, "Hello BLE") -- 要写入的值
|
|
|
+*/
|
|
|
static int l_ble_write_notify(lua_State *L){
|
|
|
uint16_t ret = 0;
|
|
|
const char *service_uuid = NULL;
|
|
|
@@ -561,6 +665,20 @@ end_error:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+写入带指示的特征值
|
|
|
+@api ble.write_indicate(opts, value)
|
|
|
+@table 特征值的描述信息
|
|
|
+@string value 要写入的值
|
|
|
+@return boolean 是否成功
|
|
|
+@usage
|
|
|
+-- 写入带指示的特征值
|
|
|
+ble_device:write_indicate({
|
|
|
+ uuid_service = "FA00", -- 服务的UUID, 可以是16位、32位或128位
|
|
|
+ uuid_characteristic = "EA01", -- 特征的UUID值, 可以是16位、32位或128位
|
|
|
+ uuid_descriptor = "2902" -- 可选, 描述符的UUID值, 可以是16位、32位或128位
|
|
|
+}, "Hello BLE") -- 要写入的值
|
|
|
+*/
|
|
|
static int l_ble_write_indicate(lua_State *L){
|
|
|
uint16_t ret = 0;
|
|
|
const char *service_uuid = NULL;
|
|
|
@@ -621,6 +739,20 @@ end_error:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+写入特征值
|
|
|
+@api ble.write_value(opts, value)
|
|
|
+@table 特征值的描述信息
|
|
|
+@string value 要写入的值
|
|
|
+@return boolean 是否成功
|
|
|
+@usage
|
|
|
+-- 写入特征值,填充预设值,被动读取
|
|
|
+ble_device:write_value({
|
|
|
+ uuid_service = "FA00", -- 服务的UUID, 可以是16位、32位或128位
|
|
|
+ uuid_characteristic = "EA01", -- 特征的UUID值, 可以是16位、32位或128位
|
|
|
+ uuid_descriptor = "2902" -- 可选, 描述符的UUID值, 可以是16位、32位或128位
|
|
|
+}, "Hello BLE") -- 要写入的值
|
|
|
+*/
|
|
|
static int l_ble_write_value(lua_State *L){
|
|
|
uint16_t ret = 0;
|
|
|
const char *service_uuid = NULL;
|
|
|
@@ -727,15 +859,18 @@ static int l_ble_read_value(lua_State *L){
|
|
|
descriptor.uuid_type = tmp;
|
|
|
memcpy(descriptor.uuid, descriptor_uuid, descriptor.uuid_type);
|
|
|
LLOGD("uuid_descriptor: %02X %02X", descriptor.uuid[0], descriptor.uuid[1]);
|
|
|
- ret = luat_ble_read_value(&service, &characteristic, &descriptor, value, &len);
|
|
|
+ ret = luat_ble_read_value(&service, &characteristic, &descriptor, &value, &len);
|
|
|
|
|
|
}else{
|
|
|
- ret = luat_ble_read_value(&service, &characteristic, NULL, value, &len);
|
|
|
+ ret = luat_ble_read_value(&service, &characteristic, NULL, &value, &len);
|
|
|
}
|
|
|
lua_pop(L, 1);
|
|
|
if (ret == 0){
|
|
|
lua_pushlstring(L, (const char *)value, len);
|
|
|
return 1;
|
|
|
+ }else{
|
|
|
+ LLOGE("ble read value error");
|
|
|
+ return 0;
|
|
|
}
|
|
|
}
|
|
|
end_error:
|
|
|
@@ -743,6 +878,16 @@ end_error:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*创建一个BLE扫描
|
|
|
+@api ble.scan_create(addr_mode, scan_interval, scan_window)
|
|
|
+@number addr_mode 广播地址模式, 可选值: ble.PUBLIC, ble.RANDOM, ble.RPA, ble.NRPA
|
|
|
+@number scan_interval 扫描间隔, 单位为0.625ms, 最小值为20, 最大值为10240
|
|
|
+@number scan_window 扫描窗口, 单位为0.625ms, 最小值为20, 最大值为10240
|
|
|
+@return boolean 是否创建成功
|
|
|
+@usage
|
|
|
+-- 创建BLE扫描
|
|
|
+ble_device:scan_create(ble.PUBLIC, 100, 100)
|
|
|
+*/
|
|
|
static int l_ble_scanning_create(lua_State *L){
|
|
|
if (!lua_isuserdata(L, 1)){
|
|
|
return 0;
|
|
|
@@ -770,11 +915,31 @@ static int l_ble_scanning_create(lua_State *L){
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+/*
|
|
|
+开始BLE扫描
|
|
|
+@api ble.scan_start()
|
|
|
+@return boolean 是否成功
|
|
|
+@usage
|
|
|
+-- 开始BLE扫描
|
|
|
+ble_device:scan_start()
|
|
|
+-- 提醒, 扫描会一直进行, 直到调用ble.scan_stop()停止扫描
|
|
|
+-- 扫描结果会立即执行回调, 同一个设备不会去重, 扫描到数据就会执行回调
|
|
|
+*/
|
|
|
static int l_ble_scanning_start(lua_State *L){
|
|
|
lua_pushboolean(L, luat_ble_start_scanning(NULL) ? 0 : 1);
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+停止BLE扫描
|
|
|
+@api ble.scan_stop()
|
|
|
+@return boolean 是否成功
|
|
|
+@usage
|
|
|
+-- 停止BLE扫描
|
|
|
+ble_device:scan_stop()
|
|
|
+-- 提醒, 扫描会一直进行, 直到调用ble.scan_stop()停止扫描
|
|
|
+*/
|
|
|
static int l_ble_scanning_stop(lua_State *L){
|
|
|
lua_pushboolean(L, luat_ble_stop_scanning(NULL) ? 0 : 1);
|
|
|
return 1;
|