Przeglądaj źródła

update: 更新Air8000,Air8101的ble central demo

wangshihao 7 miesięcy temu
rodzic
commit
db205890e2

+ 228 - 0
module/Air8000/demo/ble/central/ble_client_main.lua

@@ -0,0 +1,228 @@
+--[[
+@module  ble_client_main
+@summary ble client 主应用功能模块
+@version 1.0
+@date    2025.08.20
+@author  王世豪
+@usage
+本文件为ble client 主应用功能模块,核心业务逻辑为:
+1. 初始化BLE功能
+2. 扫描目标BLE设备(默认名称为"LuatOS")
+3. 建立与目标设备的连接
+4. 处理各类BLE事件(连接、断开连接、扫描报告、GATT操作完成等)
+5. 接收并处理来自其他模块的请求(如READ_REQ读取请求)
+6. 异常处理与自动重连机制
+7. 依赖模块
+    - ble_client_receiver: 用于处理接收到的BLE数据
+    - ble_client_sender: 用于发送BLE数据
+8. 事件处理
+    通过ble_event_cb函数处理以下BLE事件:
+    - EVENT_CONN: 连接成功
+    - EVENT_DISCONN: 断开连接
+    - EVENT_SCAN_REPORT: 扫描报告
+    - EVENT_GATT_DONE: GATT操作完成
+    - EVENT_READ_VALUE: 读取特征值完成
+
+本文件没有对外接口,直接在main.lua中require "ble_client_main"就可以加载运行;
+]]
+local ble_client_receiver = require "ble_client_receiver"
+local ble_client_sender = require "ble_client_sender"
+
+-- ble_client_main的任务名
+local TASK_NAME = ble_client_sender.TASK_NAME_PREFIX.."main"
+
+-- 配置参数
+config = {
+    target_device_name = "LuatOS", -- 目标设备名称
+    target_service_uuid = "FA00",  -- 目标服务UUID
+    target_notify_char = "EA01",   -- 目标通知特征值UUID
+    target_write_char = "EA02",    -- 目标写入特征值UUID
+    target_read_char = "EA03",     -- 目标读取特征值UUID
+    scan_timeout = 10000,          -- 等待SCAN_REPORT超时时间(ms)
+    connect_timeout = 5000,        -- 等待CONNECT超时时间(ms)
+}
+
+local bluetooth_device = nil
+local ble_device = nil
+local scan_create = false
+local scan_count = 0
+local last_operation = nil
+
+-- 设备过滤函数
+local function is_target_device(scan_param)
+    log.info("scan_param", scan_param.data:toHex())
+    -- 检查设备名称是否匹配
+    if scan_param.data and scan_param.data:find(config.target_device_name) then
+        log.info("BLE", "发现目标设备: " .. config.target_device_name)
+        return true
+    end
+    return false
+end
+
+-- 事件回调函数
+local function ble_event_cb(ble_device, ble_event, ble_param)
+    -- 仅表示连接成功,后续读/写/订阅 需等待GATT_DONE事件
+    if ble_event == ble.EVENT_CONN then
+        sysplus.sendMsg(TASK_NAME, "BLE_EVENT", "CONNECT", ble_param)
+    -- 连接断开
+    elseif ble_event == ble.EVENT_DISCONN then
+        sysplus.sendMsg(TASK_NAME, "BLE_EVENT", "DISCONNECTED", ble_param.reason)
+    -- 扫描报告
+    elseif ble_event == ble.EVENT_SCAN_REPORT then
+        sysplus.sendMsg(TASK_NAME, "BLE_EVENT", "SCAN_REPORT", ble_param)
+    -- GATT项处理
+    elseif ble_event == ble.EVENT_GATT_ITEM then
+        log.info("ble", "gatt item", ble_param)
+    -- GATT操作完成,可进行读/写/订阅操作
+    elseif ble_event == ble.EVENT_GATT_DONE then
+        sysplus.sendMsg(TASK_NAME, "BLE_EVENT", "GATT_DONE", ble_param)
+
+        -- 开启Notify监听,监听外围设备指定服务和特征值的通知,默认打开
+        local notify_params = {
+            uuid_service = string.fromHex(config.target_service_uuid),
+            uuid_characteristic = string.fromHex(config.target_notify_char)
+        }
+        ble_device:notify_enable(notify_params, true)
+    -- 读取特征值完成
+    elseif ble_event == ble.EVENT_READ_VALUE then
+        -- 通知receiver处理数据
+        ble_client_receiver.proc(ble_param.uuid_service:toHex(), ble_param.uuid_characteristic:toHex(), ble_param.data)
+    end
+end
+
+-- 初始化BLE
+local function ble_init()
+    -- 初始化蓝牙核心
+    bluetooth_device = bluetooth_device or bluetooth.init()
+    if not bluetooth_device then
+        log.error("BLE", "蓝牙初始化失败")
+        return false
+    end
+
+    -- 初始化BLE功能
+    ble_device = ble_device or bluetooth_device:ble(ble_event_cb)
+    if not ble_device then
+        log.error("BLE", "当前固件不支持完整的BLE")
+        return false
+    end
+
+    -- 创建BLE扫描
+    scan_create = scan_create or ble_device:scan_create({})
+    if not scan_create then -- 默认参数:addr_mode=ble.PUBLIC, scan_interval=100, scan_window=100
+        log.error("BLE", "BLE创建扫描失败")
+        return false
+    end
+
+    -- 启动BLE扫描
+    if not ble_device:scan_start() then
+        log.error("BLE", "BLE扫描启动失败")
+        return false
+    end
+
+    last_operation = "scan"
+    return true
+end
+
+-- 主任务处理函数
+local function ble_client_main_task_func()
+    local result,msg
+
+    while true do
+        result = ble_init()
+        if not result then
+            log.error("ble_client_main_task_func", "ble_init error")
+            goto EXCEPTION_PROC
+        end
+
+        -- 扫描上报、连接、断开连接、异常等各种事件的处理调度逻辑
+        while true do
+            -- 根据最后操作设置超时时间
+            if last_operation == "scan" then
+                timeout = config.scan_timeout
+            elseif last_operation == "connect" then
+                timeout = config.connect_timeout
+            else
+                timeout = nil
+            end
+
+            msg = sysplus.waitMsg(TASK_NAME, "BLE_EVENT", timeout)
+
+            if not msg then
+                log.error("ble_client_main_task_func", "waitMsg timeout")
+                goto EXCEPTION_PROC
+            end
+    
+            if msg[2] == "CONNECT" then
+                -- 仅表示连接成功,后续读/写/订阅 需等待GATT_DONE事件
+                local conn_param = msg[3]
+                log.info("BLE", "设备连接成功: " .. conn_param.addr:toHex())
+                last_operation = nil
+            elseif msg[2] == "GATT_DONE" then
+                -- 连接成功且服务发现完成,后续可执行业务操作(读/写/订阅)
+                -- 通知sender模块连接成功
+                log.info("BLE", "GATT服务发现完成")
+                sysplus.sendMsg(ble_client_sender.TASK_NAME, "BLE_EVENT", "CONNECT_OK", ble_device)
+                last_operation = nil
+            elseif msg[2] == "DISCONNECTED" then
+                log.info("BLE", "设备断开连接,原因: " .. msg[3])
+                break
+            elseif msg[2] == "SCAN_REPORT" then
+                local ble_param = msg[3]
+                log.info("BLE", string.format("扫描报告 | Type: %d | MAC: %s | RSSI: %d | Data: %s ",
+                    ble_param.addr_type,        -- 类型(整数)
+                    ble_param.adv_addr:toHex(), -- MAC地址(字符串)
+                    ble_param.rssi,             -- 信号强度(整数)
+                    ble_param.data:toHex()      -- 广播数据(字符串)
+                ))
+                -- 检查是否为目标设备
+                if is_target_device(ble_param) then
+                    log.info("ble", "停止扫描, 连接设备", ble_param.adv_addr:toHex(), ble_param.addr_type)
+                    ble_device:scan_stop()
+                    scan_count = 0
+                    ble_device:connect(ble_param.adv_addr, ble_param.addr_type)
+                    last_operation = "connect"
+                end
+
+                scan_count = scan_count + 1
+                if scan_count > 100 then
+                    log.info("ble", "扫描次数超过100次, 停止扫描, 10秒后重新开始")
+                    scan_count = 0
+                    ble_device:scan_stop()
+                    sysplus.sendMsg(TASK_NAME, "BLE_EVENT", "RESTART_SCAN")
+                end
+            elseif msg[2] == "RESTART_SCAN" then
+                -- 5s后重新开始扫描
+                sys.wait(5000)
+                log.info("ble", "重新开始扫描")
+                ble_device:scan_start()
+                last_operation = "scan"
+            elseif msg[2] == "READ_REQ" then
+                -- 从消息中获取传入的UUID参数,若没有则使用默认配置
+                local service_uuid = msg[3] or config.target_service_uuid
+                local char_uuid = msg[4] or config.target_read_char
+
+                local read_params = {
+                    uuid_service = string.fromHex(service_uuid),
+                    uuid_characteristic = string.fromHex(char_uuid)
+                }
+                ble_device:read_value(read_params)
+            end
+        end
+
+        -- 出现异常
+        ::EXCEPTION_PROC::
+        log.error("ble_client_main_task_func", "异常退出, 5秒后重新扫描连接")
+
+        -- 清空此task绑定的消息队列中的未处理的消息
+        sysplus.cleanMsg(TASK_NAME)
+
+        -- 通知ble sender数据发送应用模块的task,ble连接已经断开
+        sysplus.sendMsg(ble_client_sender.TASK_NAME, "BLE_EVENT", "DISCONNECTED")
+
+        -- 5秒后跳转到循环体开始位置,自动发起重连
+        sys.wait(5000)
+    end
+end
+
+-- 启动主任务
+sysplus.taskInitEx(ble_client_main_task_func, TASK_NAME)

+ 36 - 0
module/Air8000/demo/ble/central/ble_client_receiver.lua

@@ -0,0 +1,36 @@
+-- --[[
+-- @module  ble_client_receiver
+-- @summary BLE client 数据接收应用功能模块
+-- @version 1.0
+-- @date    2025.08.20
+-- @author  王世豪
+-- @usage
+-- 本文件为BLE client 数据接收应用功能模块,核心业务逻辑为:
+-- 1. 处理接收到的BLE通知数据和主动读取到的数据,根据特征值类型(Notify或Read)进行分类处理。
+
+-- 本文件的对外接口有3个:
+-- 1. ble_client_receiver.proc(service_uuid, char_uuid, data): 处理接收到的BLE通知数据和主动读取数据。
+-- 2. sys.publish("RECV_BLE_NOTIFY_DATA", service_uuid, char_uuid, data): 发布收到的通知数据给其他模块处理。
+-- 3. sys.publish("RECV_BLE_READ_DATA", service_uuid, char_uuid, data): 发布读取到的数据给其他模块处理。
+-- ]]
+
+local ble_client_receiver = {}
+
+-- 处理接收到的BLE通知数据和主动读取数据,根据特征值类型(Notify或Read)进行分类处理。
+function ble_client_receiver.proc(service_uuid, char_uuid, data)
+    -- 判断数据类型(主动读取或通知)
+    -- 通知数据
+    if char_uuid == config.target_notify_char then
+        log.info("ble_client_receiver", "收到通知数据", service_uuid, char_uuid, data)
+        -- 发布数据给其他模块
+        sys.publish("RECV_BLE_NOTIFY_DATA", service_uuid, char_uuid, data)
+        
+    -- 主动读取数据
+    elseif char_uuid == config.target_read_char then
+        log.info("ble_client_receiver", "处理主动读取的数据", service_uuid, char_uuid, data)
+        -- 发布数据给其他模块
+        sys.publish("RECV_BLE_READ_DATA", service_uuid, char_uuid, data)
+    end
+end
+
+return ble_client_receiver

+ 156 - 0
module/Air8000/demo/ble/central/ble_client_sender.lua

@@ -0,0 +1,156 @@
+--[[
+@module  ble_client_sender
+@summary BLE client 数据发送应用功能模块
+@version 1.0
+@date    2025.08.20
+@author  王世豪
+@usage
+本文件为BLE client 数据发送应用功能模块,核心业务逻辑为:
+1、订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
+2、BLE_client_sender task接收"CONNECT_OK"、"SEND_REQ"、两种类型的"BLE_EVENT"消息,处理队列中的数据;
+3、接收"DISCONNECTED"类型的"BLE_EVENT"消息,清空发送队列;
+4、数据发送完成后通过回调函数通知发送方。
+
+本文件的对外接口有1个:
+1. sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func); 订阅"SEND_DATA_REQ"消息;
+    其他应用模块如果需要发送数据,直接sys.publish这个消息即可,将需要发送的服务UUID、特征值UUID、数据、回调函数和回调参数一起publish出去;
+    本demo项目中ble_timer_app.lua中publish了这个消息;
+]]
+
+local ble_client_sender = {}
+
+--[[
+数据发送队列,数据结构为:
+{
+    [1] = {service_uuid="service1", char_uuid="char1", data="data1", cb={func=callback_function1, para=callback_para1}},
+    [2] = {service_uuid="service2", char_uuid="char2", data="data2", cb={func=callback_function2, para=callback_para2}},
+}
+service_uuid: BLE服务UUID,string类型,必须存在;
+char_uuid: BLE特征值UUID,string类型,必须存在;
+data: 要发送的数据,string类型,必须存在;
+cb.func: 数据发送结果的用户回调函数,可以不存在;
+cb.para: 数据发送结果的用户回调函数参数,可以不存在;
+]]
+
+local send_queue = {}
+
+-- BLE client的任务名前缀
+ble_client_sender.TASK_NAME_PREFIX = "ble_client_"
+
+-- ble_client_sender的任务名
+ble_client_sender.TASK_NAME = ble_client_sender.TASK_NAME_PREFIX.."sender"
+
+-- "SEND_DATA_REQ"消息的处理函数
+local function send_data_req_proc_func(tag, service_uuid, char_uuid, data, cb)
+    -- 将数据插入到发送队列send_queue中
+    table.insert(send_queue, {service_uuid=service_uuid, char_uuid=char_uuid, data="send from " .. tag .. ": " .. data, cb=cb})
+    -- 发送消息通知 BLE sender task,有新数据等待发送
+    sysplus.sendMsg(ble_client_sender.TASK_NAME, "BLE_EVENT", "SEND_REQ")
+end
+
+-- 按照顺序发送send_queue中的数据
+-- 如果发送成功,则返回当前正在发送的数据项
+-- 如果发送失败,通知回调函数发送失败后,继续发送下一条数据
+local function send_item_func(ble_device)
+    local item
+    -- 如果发送队列中有数据等待发送
+    while #send_queue>0 do
+        -- 取出来第一条数据赋值给item
+        -- 同时从队列send_queue中删除这一条数据
+        item = table.remove(send_queue, 1)
+
+        -- 发送数据
+        local write_params = {
+            uuid_service = string.fromHex(item.service_uuid),
+            uuid_characteristic = string.fromHex(item.char_uuid)
+        }
+        local data = item.data
+        local result = ble_device:write_value(write_params, data)
+
+        -- 发送接口调用成功
+        if result then
+            -- 保存当前发送项,等待写入完成通知
+            return item
+        -- 发送接口调用失败
+        else
+            -- 如果当前发送的数据有用户回调函数,则执行用户回调函数
+            if item.cb and item.cb.func then
+                item.cb.func(false, item.cb.para)
+            end
+        end
+    end
+end
+
+-- 处理发送结果的回调函数
+local function send_item_cbfunc(item, result)
+    if item then
+        -- 如果当前发送的数据有用户回调函数,则执行用户回调函数
+        if item.cb and item.cb.func then
+            item.cb.func(result, item.cb.para)
+        end
+    end
+end
+
+-- BLE client sender的任务处理函数
+local function ble_client_sender_task_func()
+    local ble_device
+    local send_item
+    local result, msg
+
+    while true do
+        -- 等待"BLE_EVENT"消息
+        msg = sysplus.waitMsg(ble_client_sender.TASK_NAME, "BLE_EVENT")
+
+        -- BLE连接成功
+        -- msg[3]表示ble_device对象
+        if msg[2] == "CONNECT_OK" then
+            ble_device = msg[3]
+            -- 发送send_queue中的数据
+            send_item = send_item_func(ble_device)
+
+        -- BLE发送数据请求
+        elseif msg[2] == "SEND_REQ" then
+            -- 如果ble_device对象存在,发送send_queue中的数据
+            if ble_device then
+                send_item_cbfunc(send_item, true)
+                send_item = send_item_func(ble_device)
+            end
+
+        -- -- BLE发送完成(待完善......)
+        -- elseif msg[2] == "SEND_OK" then
+        --     -- send完成,执行回调并继续发送
+        --     send_item_cbfunc(send_item, true)
+        --     -- 发送send_queue中的下一条数据
+        --     send_item = send_item_func(ble_device)
+
+        -- BLE断开连接
+        elseif msg[2] == "DISCONNECTED" then
+            -- 清空ble_device对象
+            ble_device = nil
+            -- 如果存在正在等待发送结果的发送项,执行回调函数通知发送方失败
+            send_item_cbfunc(send_item, false)
+            -- 如果发送队列中有数据等待发送
+            while #send_queue>0 do
+                -- 取出来第一条数据赋值给send_item
+                -- 同时从队列send_queue中删除这一条数据
+                send_item = table.remove(send_queue,1)
+                -- 执行回调函数通知发送方失败
+                send_item_cbfunc(send_item, false)
+            end
+            -- 当前没有正在等待发送结果的发送项
+            send_item = nil
+        end
+    end
+end
+
+-- 订阅"SEND_DATA_REQ"消息;
+-- 其他应用模块如果需要发送数据,直接sys.publish这个消息即可
+-- 参数: tag(标签), service_uuid(服务UUID), char_uuid(特征值UUID), data(数据), cb(回调函数和参数)
+sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)
+
+--创建并且启动一个task
+--运行这个task的处理函数ble_client_sender_task_func
+sysplus.taskInitEx(ble_client_sender_task_func, ble_client_sender.TASK_NAME)
+
+return ble_client_sender
+

+ 67 - 0
module/Air8000/demo/ble/central/ble_timer_app.lua

@@ -0,0 +1,67 @@
+--[[
+@module  ble_timer_app
+@summary 定时器应用功能模块
+@version 1.0
+@date    2025.08.20
+@author  王世豪
+@usage
+本文件为定时器应用功能模块,核心业务逻辑为:
+创建两个独立的5秒循环定时器,一个用于定时读取外围设备特征值UUID数据,一个用于定时向外围设备特征值UUID发送数据;
+
+本文件的对外接口有2个:
+1、sys.publish("SEND_DATA_REQ", "timer", server_uuid, write_char_uuid, data, {func = send_data_cbfunc, para="timer"..data}),发布SEND_DATA_REQ消息,在ble_client_sender文件中处理,携带的参数为:
+    -- 发布消息"SEND_DATA_REQ"
+    -- 携带的第一个参数"timer"表示是定时器应用模块发布的消息
+    -- 携带的第二个参数server_uuid表示要发送的服务UUID
+    -- 携带的第三个参数write_char_uuid表示要发送的特征值UUID
+    -- 携带的第四个参数data表示要发送的数据
+    -- 携带的第五个参数cb为发送结果回调(可以为空,如果为空,表示不关心ble client 发送数据成功还是失败),其中:
+    --   cb.func为回调函数(可以为空,如果为空,表示不关心ble client发送数据成功还是失败)
+    --   cb.para为回调函数的第二个参数(可以为空),回调函数的第一个参数为发送结果(true表示成功,false表示失败)
+
+2、sysplus.sendMsg(BLE_TASK_NAME,"BLE_EVENT","READ_REQ",server_uuid,read_char_uuid), 发送读取外围设备特征值UUID数据请求,在ble_client_main文件中处理,携带的参数为:
+    -- msg[2]: "READ_REQ" --消息类型
+    -- msg[3]: server_uuid --服务UUID
+    -- msg[4]: read_char_uuid --特征值UUID
+]]
+
+local BLE_TASK_NAME = "ble_client_main"
+local server_uuid = "FA00"
+local write_char_uuid = "EA02"
+local read_char_uuid = "EA03"
+local data = "1234"
+
+-- 数据发送结果回调函数
+-- result:发送结果,true为发送成功,false为发送失败
+-- para:回调参数,sys.publish("SEND_DATA_REQ", "timer", "FA00", "EA02", data, {func=send_data_cbfunc, para="timer"..data})中携带的para
+local function send_data_cbfunc(result, para)
+    log.info("send_data_cbfunc", result, para)
+end
+
+-- 定时器回调函数
+function send_data_req_timer_cbfunc()
+    -- 发布消息"SEND_DATA_REQ"
+    -- 携带的第一个参数"timer"表示是定时器应用模块发布的消息
+    -- 携带的第二个参数server_uuid表示要发送的服务UUID
+    -- 携带的第三个参数char_uuid表示要发送的特征值UUID
+    -- 携带的第四个参数data表示要发送的数据
+    -- 携带的第五个参数cb为发送结果回调(可以为空,如果为空,表示不关心ble client 发送数据成功还是失败),其中:
+    --       cb.func为回调函数(可以为空,如果为空,表示不关心ble client发送数据成功还是失败)
+    --       cb.para为回调函数的第二个参数(可以为空),回调函数的第一个参数为发送结果(true表示成功,false表示失败)
+    sys.publish("SEND_DATA_REQ", "timer", server_uuid, write_char_uuid, data, {func = send_data_cbfunc, para="timer"..data})
+end
+
+local function read_data_req_timer_cbfunc()
+    -- msg[2]: "READ_REQ" --消息类型
+    -- msg[3]: server_uuid --服务UUID
+    -- msg[4]: read_char_uuid --特征值UUID
+    sysplus.sendMsg(BLE_TASK_NAME,"BLE_EVENT","READ_REQ",server_uuid,read_char_uuid)
+end
+
+-- 启动5秒的循环定时器用于发送数据
+sys.timerLoopStart(send_data_req_timer_cbfunc, 5000)
+log.info("TIMER_APP", "已启动发送数据循环定时器,间隔: 5000ms")
+
+-- 启动5秒的循环定时器用于读取数据
+sys.timerLoopStart(read_data_req_timer_cbfunc, 5000)
+log.info("TIMER_APP", "已启动读取数据循环定时器,间隔: 5000ms")

+ 28 - 0
module/Air8000/demo/ble/central/ble_uart_app.lua

@@ -0,0 +1,28 @@
+--[[
+@module  ble_uart_app
+@summary 串口应用功能模块
+@version 1.0
+@date    2025.08.20
+@author  王世豪
+@usage
+本文件为串口应用功能模块,核心业务逻辑为:
+1、打开uart1,波特率115200,数据位8,停止位1,无奇偶校验位;
+2、uart1和pc端的串口工具相连;
+3、收到ble_client通过notify监听的数据后,将数据通过uart1发送到pc端串口工具;
+
+本文件的对外接口有1个:
+1. sys.subscribe("RECV_BLE_NOTIFY_DATA", recv_ble_notify_data_proc),订阅RECV_BLE_NOTIFY_DATA消息,处理消息携带的数据;
+]]
+
+local UART_ID = 1
+-- 初始化UART1,波特率115200,数据位8,停止位1
+uart.setup(UART_ID, 115200, 8, 1)
+
+-- 将service_uuid,char_uuid和data数据拼接
+-- 然后末尾增加回车换行两个字符,通过uart1发送出去,方便在PC端换行显示查看
+local function recv_ble_notify_data_proc(service_uuid, char_uuid, data)
+    uart.write(UART_ID, service_uuid..","..char_uuid..","..data.."\r\n")
+end
+
+-- 订阅"RECV_BLE_NOTIFY_DATA"消息的处理函数recv_ble_notify_data_proc
+sys.subscribe("RECV_BLE_NOTIFY_DATA", recv_ble_notify_data_proc)

+ 15 - 2
module/Air8000/demo/ble/master/check_wifi.lua → module/Air8000/demo/ble/central/check_wifi.lua

@@ -1,4 +1,17 @@
--- 用于检查当前模组中WiFi是否是最新版本,如果不是最新版本则启动升级。
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.0
+@date    2025.08.20
+@author  王世豪
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+说明:Air8000的蓝牙功能依赖WiFi协处理器,需确保WiFi固件为最新版本。
+
+本文件没有对外接口,直接在main.lua中require ""check_wifi"就可以加载运行。
+]]
+
 local exfotawifi = require("exfotawifi")
 
 local function wifi_fota_task_func()
@@ -21,5 +34,5 @@ local function wait_ip_ready()
     end
 end
 
--- 在设备启动时检查网络状态
+-- 在设备启动时检查SIM卡状态
 sys.taskInit(wait_ip_ready)

+ 79 - 0
module/Air8000/demo/ble/central/main.lua

@@ -0,0 +1,79 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.08.20
+@author  wangshihao
+@usage
+本demo演示的核心功能为:
+演示了Air8000核心板作为BLE Central(中心设备)的核心功能:
+1、ble中心设备扫描并连接指定外围设备;
+2、ble中心设备连接成功后,开始定时读取外围设备特征值UUID数据, 定时发送数据给外围设备;
+3、ble中心设备收到外围设备特征值UUID的notify数据后,通过uart发送到pc端串口工具;
+4、pc端串口工具收到数据后,打印到串口工具窗口;
+
+更多说明参考本目录下的readme.md文件
+]]
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+PROJECT = "ble_central"
+VERSION = "001.000.000"
+
+log.info("main", "project name is ", PROJECT, "version is ", VERSION)
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+-- Air8000蓝牙依赖WiFi协处理器,需更新WiFi固件(默认自动更新,需插入联网SIM卡)
+require "check_wifi" ---- 自动检查并更新WiFi固件
+
+-- 加载BLE Central(中心设备)主控制模块
+require "ble_client_main"
+
+-- 加载串口应用功能模块
+require "ble_uart_app"
+
+-- 加载定时器应用功能模块
+require "ble_timer_app"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 94 - 0
module/Air8000/demo/ble/central/readme.md

@@ -0,0 +1,94 @@
+## 功能模块介绍
+
+1、main.lua:主程序入口;
+
+2、ble_client_main.lua:ble中心设备主程序,进行ble初始化,处理各类ble事件(连接、断开连接、扫描报告、GATT操作完成等);
+
+3、ble_client_receiver.lua:ble中心设备接收数据处理逻辑;
+
+4、ble_client_sender.lua:ble中心设备发送数据处理逻辑;
+
+5、ble_timer_app.lua:ble中心设备定时器处理逻辑,启动两个循环定时器,一个用于定时读取外围设备特征值UUID数据,一个用于定时向外围设备特征值UUID发送数据;
+
+6、ble_uart_app.lua:ble中心设备接uart处理逻辑,将收到的notify数据,通过uart发送到pc端串口工具;
+
+7、check_wifi.lua:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+
+## 用户消息介绍
+
+1、"RECV_BLE_READ_DATA":ble中心设备主动读取到外围设备特征值UUID数据时,会发布此消息,通知其他应用模块(如ble_uart_app)处理数据;
+
+2、"RECV_BLE_NOTIFY_DATA":ble中心设备收到外围设备特征值UUID的notify数据时,会发布此消息,通知其他应用模块(如ble_uart_app)处理数据;
+
+3、"SEND_DATA_REQ":其他应用模块(ble_timer_app)发布此消息,通知ble 中心设备发送publish数据给外围设备;
+
+## 演示功能概述
+
+使用Air8000核心板演示 ble的central(中心设备) 功能。
+
+1、ble中心设备扫描并连接外围设备;
+
+2、ble中心设备连接成功后,开始定时读取外围设备特征值UUID数据, 定时发送数据给外围设备;
+
+3、ble中心设备收到外围设备特征值UUID的notify数据后,通过uart发送到pc端串口工具;
+
+4、pc端串口工具收到数据后,打印到串口工具窗口。
+
+## 演示硬件环境
+
+1、Air8000核心板两块(本次演示中,一块作为中心设备,一块作为外围设备)
+
+2、TYPE-C USB数据线一根
+
+3、Air8000核心板和数据线的硬件接线方式为
+
+- Air8000核心板通过TYPE-C USB口供电;(正常测试时核心板背面的功耗测试开关拨到ON,正面的白色拨码(供电,充电选择脚)开关拨到供电.)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+## 演示软件环境
+
+1、[Luatools下载调试工具](https://docs.openluat.com/air8000/luatos/common/download/)
+
+2、[Air8000 V2012版本固件](https://docs.openluat.com/air8000/luatos/firmware/)(理论上,2025年7月26日之后发布的固件都可以)
+
+3、PC端的串口工具,例如SSCOM、LLCOM等都可以
+
+## 演示核心步骤
+
+1、搭建好硬件环境
+
+2、Luatools烧录内核固件和修改后的demo脚本代码
+
+作为中心设备的Air8000核心板,需要烧录central代码;
+
+作为外围设备的Air8000核心板,需要烧录peripheral代码;
+
+3、烧录成功后,自动开机运行,如果中心设备出现以下日志,表示中心设备连接外围设备成功
+
+```lua
+I/user.ble 发现目标设备: LuatOS
+I/user.ble 停止扫描, 连接设备 C8C2C68D4FF6 0
+```
+4、如果中心设备出现以下日志,则表示GATT服务发现完成,此时中心设备便可以进行读/写/监听操作
+
+```lua
+I/user.ble gatt item table: 60C78190
+I/user.ble gatt item table: 60C77F90
+I/user.ble gatt item table: 60C77DB0
+I/user.ble GATT服务发现完成
+```
+5、打开PC端的串口工具,选择对应的端口,配置波特率115200,数据位8,停止位1,无奇偶校验位;
+
+6、在PC端的串口工具窗口中,会打印出中心设备监听到的外围设备notify数据;
+
+```lua
+-- 以下便是uart接收到的notify数据,数据格式为:服务UUID,特征值UUID,特征值数据
+FA00,EA01,123456Thu Jan  1 08:00:25 1970
+```
+7、在luatools日志中可以看到中心设备主动写入外围设备指定特征值UUID的数据,以及向外围设备写入的结果。
+
+```lua
+-- 中心设备写入数据后,会有数据发送结果回调函数触发,下面打印的"true" 表示发送成功,timer后面的“1234”便是发送的数据。
+I/user.send_data_cbfunc true timer1234
+```

+ 0 - 99
module/Air8000/demo/ble/master/ble_master.lua

@@ -1,99 +0,0 @@
---[[
-@module  ble_ibeacon
-@summary Air8000演示BLE主机模式功能模块
-@version 1.0
-@date    2025.07.01
-@author  wangshihao
-@usage
-本文件为Air8000核心板演示BLE主机模式的代码示例,核心业务逻辑为:
-1. 初始化蓝牙底层框架
-    bluetooth_device = bluetooth.init()
-2. 创建BLE对象实例
-    local ble_device = bluetooth_device:ble(ble_event_cb)
-3. 创建BLE扫描
-    ble_device:scan_create({})
-4. 开始BLE扫描
-    ble_device:scan_start()
-5. 在回调函数中处理事件
-    a. 扫描报告事件(ble.EVENT_SCAN_REPORT)
-        - 每收到一个扫描report,计数器'scan_count'加1。
-        - 如果扫描次数超过100次,停止扫描,15秒后重新开始(防止过度扫描)。
-        - 检查扫描到的设备广播数据中是否包含"LuatOS"字符串,并且地址类型为0(公开地址)。如果满足条件,停止扫描并尝试连接该设备。
-    b. 连接事件(ble.EVENT_CONN)
-        - 连接成功,打印日志。
-    c. 断开连接事件(ble.EVENT_DISCONN)
-        - 打印日志,重新开始扫描。
-    d. 读写数据事件(ble.EVENT_READ_VALUE, ble.EVENT_WRITE)
-        - 打印日志。
-    e. 通知事件(ble.EVENT_NOTIFY)
-        - 打印日志。
-    f. GATT事件(ble.EVENT_GATT_ITEM, ble.EVENT_GATT_DONE)
-        - 开启指定服务(UUID:FA00)和特征值(UUID:EA01)的通知功能。
-        - 向特征值(UUID:EA02)写入数据(十六进制数据"1234")。
-        - 读取特征值(UUID:EA03)的数据。
-]]
-
-local scan_count = 0
-
-local function ble_callback(ble_device, ble_event, ble_param)
-    if ble_event == ble.EVENT_CONN then -- 连接成功
-        log.info("ble", "connect 成功")
-    elseif ble_event == ble.EVENT_DISCONN then -- 断开连接,并打印断开原因
-        log.info("ble", "disconnect", ble_param.reason)
-        sys.timerStart(function() ble_device:scan_start() end, 1000) -- 1秒后重新开始扫描
-    elseif ble_event == ble.EVENT_WRITE then --收到写请求
-        log.info("ble", "write", ble_param.handle,ble_param.uuid_service:toHex(),ble_param.uuid_characteristic:toHex())
-        log.info("ble", "data", ble_param.data:toHex())
-    elseif ble_event == ble.EVENT_READ_VALUE then -- 收到读请求
-        log.info("ble", "read", ble_param.handle,ble_param.uuid_service:toHex(),ble_param.uuid_characteristic:toHex(),ble_param.data)
-    elseif ble_event == ble.EVENT_SCAN_REPORT then -- 扫描到设备
-        print("ble scan report",ble_param.addr_type,ble_param.rssi,ble_param.adv_addr:toHex(),ble_param.data:toHex())
-        scan_count = scan_count + 1
-        if scan_count > 100 then
-            log.info("ble", "扫描次数超过100次, 停止扫描, 15秒后重新开始")
-            scan_count = 0
-            ble_device:scan_stop()
-            sys.timerStart(function() ble_device:scan_start() end, 15000)
-        end
-        -- 注意, 这里是连接到另外一个设备, 设备名称带LuatOS字样
-        if ble_param.addr_type == 0 and ble_param.data:find("LuatOS") then
-            log.info("ble", "停止扫描, 连接设备", ble_param.adv_addr:toHex(), ble_param.addr_type)
-            ble_device:scan_stop()
-            ble_device:connect(ble_param.adv_addr,ble_param.addr_type)
-        end
-    elseif ble_event == ble.EVENT_GATT_ITEM then -- 读取GATT信息
-        -- 读取GATT完成,打印出来
-        log.info("ble", "gatt item", ble_param)
-    elseif ble_event == ble.EVENT_GATT_DONE then -- GATT操作完成
-        -- GATT操作完成,打印服务数量
-        log.info("ble", "gatt done", ble_param.service_num)
-
-        -- 开启指定服务和特征值的通知
-        local wt = {uuid_service = string.fromHex("FA00"), uuid_characteristic = string.fromHex("EA01")}
-        ble_device:notify_enable(wt, true) -- 开启通知
-
-        -- 主动写入数据, 但不带通知, 带通知是 write_notify
-        local wt = {uuid_service = string.fromHex("FA00"), uuid_characteristic = string.fromHex("EA02")}
-        ble_device:write_value(wt, string.fromHex("1234")) -- 写入数据, 这里是写入十六进制数据"1234"
-        log.info("ble", "write value", wt.uuid_service:toHex(), wt.uuid_characteristic:toHex(), "1234")
-
-        -- 读取特征值数据
-        -- 注意, 这里是读取另外一个设备的特征值数据
-        local wt = {uuid_service = string.fromHex("FA00"), uuid_characteristic = string.fromHex("EA03")}
-        ble_device:read_value(wt)
-    end
-end
-
-function ble_master()
-    log.info("开始初始化蓝牙核心")
-    bluetooth_device = bluetooth.init()
-    log.info("初始化BLE功能")
-    ble_device = bluetooth_device:ble(ble_callback)
-
-    -- master
-    ble_device:scan_create({})
-    ble_device:scan_start()
-    -- ble_device:scan_stop()
-end
-
-sys.taskInit(ble_master)

+ 0 - 34
module/Air8000/demo/ble/master/readme.md

@@ -1,34 +0,0 @@
-
-## 演示功能概述
-
-将使用Air8000核心板演示master功能。
-
-## 演示硬件环境
-
-1、Air8000核心板一块
-
-2、TYPE-C USB数据线一根
-
-3、Air8000核心板和数据线的硬件接线方式为
-
-- Air8000核心板通过TYPE-C USB口供电;(正常测试时核心板背面的功耗测试开关拨到ON,正面的白色拨码(供电,充电选择脚)开关拨到供电.)
-
-- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
-
-## 演示软件环境
-
-1、Luatools下载调试工具
-
-[如何使用 LuaTools 烧录软件 - luatos@air8000 - 合宙模组资料中心](https://docs.openluat.com/air8000/luatos/common/download/)
-
-2、[Air8000 V2008版本固件(2025.06.27及之后发布)](https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/core)(测试通过的固件是LuatOS-SoC_V2008_Air8000_LVGL_0627.soc)
-
-## 演示核心步骤
-
-1、搭建好演示硬件环境
-
-2、通过Luatools将demo与固件烧录到核心板中
-
-3、烧录成功后,自动开机运行
-
-4、接下来通过蓝牙APP 演示Air8000的BLE master功能

+ 228 - 0
module/Air8101/demo/ble/central/ble_client_main.lua

@@ -0,0 +1,228 @@
+--[[
+@module  ble_client_main
+@summary ble client 主应用功能模块
+@version 1.0
+@date    2025.08.20
+@author  王世豪
+@usage
+本文件为ble client 主应用功能模块,核心业务逻辑为:
+1. 初始化BLE功能
+2. 扫描目标BLE设备(默认名称为"LuatOS")
+3. 建立与目标设备的连接
+4. 处理各类BLE事件(连接、断开连接、扫描报告、GATT操作完成等)
+5. 接收并处理来自其他模块的请求(如READ_REQ读取请求)
+6. 异常处理与自动重连机制
+7. 依赖模块
+    - ble_client_receiver: 用于处理接收到的BLE数据
+    - ble_client_sender: 用于发送BLE数据
+8. 事件处理
+    通过ble_event_cb函数处理以下BLE事件:
+    - EVENT_CONN: 连接成功
+    - EVENT_DISCONN: 断开连接
+    - EVENT_SCAN_REPORT: 扫描报告
+    - EVENT_GATT_DONE: GATT操作完成
+    - EVENT_READ_VALUE: 读取特征值完成
+
+本文件没有对外接口,直接在main.lua中require "ble_client_main"就可以加载运行;
+]]
+local ble_client_receiver = require "ble_client_receiver"
+local ble_client_sender = require "ble_client_sender"
+
+-- ble_client_main的任务名
+local TASK_NAME = ble_client_sender.TASK_NAME_PREFIX.."main"
+
+-- 配置参数
+config = {
+    target_device_name = "LuatOS", -- 目标设备名称
+    target_service_uuid = "FA00",  -- 目标服务UUID
+    target_notify_char = "EA01",   -- 目标通知特征值UUID
+    target_write_char = "EA02",    -- 目标写入特征值UUID
+    target_read_char = "EA03",     -- 目标读取特征值UUID
+    scan_timeout = 10000,          -- 等待SCAN_REPORT超时时间(ms)
+    connect_timeout = 5000,        -- 等待CONNECT超时时间(ms)
+}
+
+local bluetooth_device = nil
+local ble_device = nil
+local scan_create = false
+local scan_count = 0
+local last_operation = nil
+
+-- 设备过滤函数
+local function is_target_device(scan_param)
+    log.info("scan_param", scan_param.data:toHex())
+    -- 检查设备名称是否匹配
+    if scan_param.data and scan_param.data:find(config.target_device_name) then
+        log.info("BLE", "发现目标设备: " .. config.target_device_name)
+        return true
+    end
+    return false
+end
+
+-- 事件回调函数
+local function ble_event_cb(ble_device, ble_event, ble_param)
+    -- 仅表示连接成功,后续读/写/订阅 需等待GATT_DONE事件
+    if ble_event == ble.EVENT_CONN then
+        sysplus.sendMsg(TASK_NAME, "BLE_EVENT", "CONNECT", ble_param)
+    -- 连接断开
+    elseif ble_event == ble.EVENT_DISCONN then
+        sysplus.sendMsg(TASK_NAME, "BLE_EVENT", "DISCONNECTED", ble_param.reason)
+    -- 扫描报告
+    elseif ble_event == ble.EVENT_SCAN_REPORT then
+        sysplus.sendMsg(TASK_NAME, "BLE_EVENT", "SCAN_REPORT", ble_param)
+    -- GATT项处理
+    elseif ble_event == ble.EVENT_GATT_ITEM then
+        log.info("ble", "gatt item", ble_param)
+    -- GATT操作完成,可进行读/写/订阅操作
+    elseif ble_event == ble.EVENT_GATT_DONE then
+        sysplus.sendMsg(TASK_NAME, "BLE_EVENT", "GATT_DONE", ble_param)
+
+        -- 开启Notify监听,监听外围设备指定服务和特征值的通知,默认打开
+        local notify_params = {
+            uuid_service = string.fromHex(config.target_service_uuid),
+            uuid_characteristic = string.fromHex(config.target_notify_char)
+        }
+        ble_device:notify_enable(notify_params, true)
+    -- 读取特征值完成
+    elseif ble_event == ble.EVENT_READ_VALUE then
+        -- 通知receiver处理数据
+        ble_client_receiver.proc(ble_param.uuid_service:toHex(), ble_param.uuid_characteristic:toHex(), ble_param.data)
+    end
+end
+
+-- 初始化BLE
+local function ble_init()
+    -- 初始化蓝牙核心
+    bluetooth_device = bluetooth_device or bluetooth.init()
+    if not bluetooth_device then
+        log.error("BLE", "蓝牙初始化失败")
+        return false
+    end
+
+    -- 初始化BLE功能
+    ble_device = ble_device or bluetooth_device:ble(ble_event_cb)
+    if not ble_device then
+        log.error("BLE", "当前固件不支持完整的BLE")
+        return false
+    end
+
+    -- 创建BLE扫描
+    scan_create = scan_create or ble_device:scan_create({})
+    if not scan_create then -- 默认参数:addr_mode=ble.PUBLIC, scan_interval=100, scan_window=100
+        log.error("BLE", "BLE创建扫描失败")
+        return false
+    end
+
+    -- 启动BLE扫描
+    if not ble_device:scan_start() then
+        log.error("BLE", "BLE扫描启动失败")
+        return false
+    end
+
+    last_operation = "scan"
+    return true
+end
+
+-- 主任务处理函数
+local function ble_client_main_task_func()
+    local result,msg
+
+    while true do
+        result = ble_init()
+        if not result then
+            log.error("ble_client_main_task_func", "ble_init error")
+            goto EXCEPTION_PROC
+        end
+
+        -- 扫描上报、连接、断开连接、异常等各种事件的处理调度逻辑
+        while true do
+            -- 根据最后操作设置超时时间
+            if last_operation == "scan" then
+                timeout = config.scan_timeout
+            elseif last_operation == "connect" then
+                timeout = config.connect_timeout
+            else
+                timeout = nil
+            end
+
+            msg = sysplus.waitMsg(TASK_NAME, "BLE_EVENT", timeout)
+
+            if not msg then
+                log.error("ble_client_main_task_func", "waitMsg timeout")
+                goto EXCEPTION_PROC
+            end
+    
+            if msg[2] == "CONNECT" then
+                -- 仅表示连接成功,后续读/写/订阅 需等待GATT_DONE事件
+                local conn_param = msg[3]
+                log.info("BLE", "设备连接成功: " .. conn_param.addr:toHex())
+                last_operation = nil
+            elseif msg[2] == "GATT_DONE" then
+                -- 连接成功且服务发现完成,后续可执行业务操作(读/写/订阅)
+                -- 通知sender模块连接成功
+                log.info("BLE", "GATT服务发现完成")
+                sysplus.sendMsg(ble_client_sender.TASK_NAME, "BLE_EVENT", "CONNECT_OK", ble_device)
+                last_operation = nil
+            elseif msg[2] == "DISCONNECTED" then
+                log.info("BLE", "设备断开连接,原因: " .. msg[3])
+                break
+            elseif msg[2] == "SCAN_REPORT" then
+                local ble_param = msg[3]
+                log.info("BLE", string.format("扫描报告 | Type: %d | MAC: %s | RSSI: %d | Data: %s ",
+                    ble_param.addr_type,        -- 类型(整数)
+                    ble_param.adv_addr:toHex(), -- MAC地址(字符串)
+                    ble_param.rssi,             -- 信号强度(整数)
+                    ble_param.data:toHex()      -- 广播数据(字符串)
+                ))
+                -- 检查是否为目标设备
+                if is_target_device(ble_param) then
+                    log.info("ble", "停止扫描, 连接设备", ble_param.adv_addr:toHex(), ble_param.addr_type)
+                    ble_device:scan_stop()
+                    scan_count = 0
+                    ble_device:connect(ble_param.adv_addr, ble_param.addr_type)
+                    last_operation = "connect"
+                end
+
+                scan_count = scan_count + 1
+                if scan_count > 100 then
+                    log.info("ble", "扫描次数超过100次, 停止扫描, 10秒后重新开始")
+                    scan_count = 0
+                    ble_device:scan_stop()
+                    sysplus.sendMsg(TASK_NAME, "BLE_EVENT", "RESTART_SCAN")
+                end
+            elseif msg[2] == "RESTART_SCAN" then
+                -- 5s后重新开始扫描
+                sys.wait(5000)
+                log.info("ble", "重新开始扫描")
+                ble_device:scan_start()
+                last_operation = "scan"
+            elseif msg[2] == "READ_REQ" then
+                -- 从消息中获取传入的UUID参数,若没有则使用默认配置
+                local service_uuid = msg[3] or config.target_service_uuid
+                local char_uuid = msg[4] or config.target_read_char
+
+                local read_params = {
+                    uuid_service = string.fromHex(service_uuid),
+                    uuid_characteristic = string.fromHex(char_uuid)
+                }
+                ble_device:read_value(read_params)
+            end
+        end
+
+        -- 出现异常
+        ::EXCEPTION_PROC::
+        log.error("ble_client_main_task_func", "异常退出, 5秒后重新扫描连接")
+
+        -- 清空此task绑定的消息队列中的未处理的消息
+        sysplus.cleanMsg(TASK_NAME)
+
+        -- 通知ble sender数据发送应用模块的task,ble连接已经断开
+        sysplus.sendMsg(ble_client_sender.TASK_NAME, "BLE_EVENT", "DISCONNECTED")
+
+        -- 5秒后跳转到循环体开始位置,自动发起重连
+        sys.wait(5000)
+    end
+end
+
+-- 启动主任务
+sysplus.taskInitEx(ble_client_main_task_func, TASK_NAME)

+ 36 - 0
module/Air8101/demo/ble/central/ble_client_receiver.lua

@@ -0,0 +1,36 @@
+-- --[[
+-- @module  ble_client_receiver
+-- @summary BLE client 数据接收应用功能模块
+-- @version 1.0
+-- @date    2025.08.20
+-- @author  王世豪
+-- @usage
+-- 本文件为BLE client 数据接收应用功能模块,核心业务逻辑为:
+-- 1. 处理接收到的BLE通知数据和主动读取到的数据,根据特征值类型(Notify或Read)进行分类处理。
+
+-- 本文件的对外接口有3个:
+-- 1. ble_client_receiver.proc(service_uuid, char_uuid, data): 处理接收到的BLE通知数据和主动读取数据。
+-- 2. sys.publish("RECV_BLE_NOTIFY_DATA", service_uuid, char_uuid, data): 发布收到的通知数据给其他模块处理。
+-- 3. sys.publish("RECV_BLE_READ_DATA", service_uuid, char_uuid, data): 发布读取到的数据给其他模块处理。
+-- ]]
+
+local ble_client_receiver = {}
+
+-- 处理接收到的BLE通知数据和主动读取数据,根据特征值类型(Notify或Read)进行分类处理。
+function ble_client_receiver.proc(service_uuid, char_uuid, data)
+    -- 判断数据类型(主动读取或通知)
+    -- 通知数据
+    if char_uuid == config.target_notify_char then
+        log.info("ble_client_receiver", "收到通知数据", service_uuid, char_uuid, data)
+        -- 发布数据给其他模块
+        sys.publish("RECV_BLE_NOTIFY_DATA", service_uuid, char_uuid, data)
+        
+    -- 主动读取数据
+    elseif char_uuid == config.target_read_char then
+        log.info("ble_client_receiver", "处理主动读取的数据", service_uuid, char_uuid, data)
+        -- 发布数据给其他模块
+        sys.publish("RECV_BLE_READ_DATA", service_uuid, char_uuid, data)
+    end
+end
+
+return ble_client_receiver

+ 156 - 0
module/Air8101/demo/ble/central/ble_client_sender.lua

@@ -0,0 +1,156 @@
+--[[
+@module  ble_client_sender
+@summary BLE client 数据发送应用功能模块
+@version 1.0
+@date    2025.08.20
+@author  王世豪
+@usage
+本文件为BLE client 数据发送应用功能模块,核心业务逻辑为:
+1、订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
+2、BLE_client_sender task接收"CONNECT_OK"、"SEND_REQ"、两种类型的"BLE_EVENT"消息,处理队列中的数据;
+3、接收"DISCONNECTED"类型的"BLE_EVENT"消息,清空发送队列;
+4、数据发送完成后通过回调函数通知发送方。
+
+本文件的对外接口有1个:
+1. sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func); 订阅"SEND_DATA_REQ"消息;
+    其他应用模块如果需要发送数据,直接sys.publish这个消息即可,将需要发送的服务UUID、特征值UUID、数据、回调函数和回调参数一起publish出去;
+    本demo项目中ble_timer_app.lua中publish了这个消息;
+]]
+
+local ble_client_sender = {}
+
+--[[
+数据发送队列,数据结构为:
+{
+    [1] = {service_uuid="service1", char_uuid="char1", data="data1", cb={func=callback_function1, para=callback_para1}},
+    [2] = {service_uuid="service2", char_uuid="char2", data="data2", cb={func=callback_function2, para=callback_para2}},
+}
+service_uuid: BLE服务UUID,string类型,必须存在;
+char_uuid: BLE特征值UUID,string类型,必须存在;
+data: 要发送的数据,string类型,必须存在;
+cb.func: 数据发送结果的用户回调函数,可以不存在;
+cb.para: 数据发送结果的用户回调函数参数,可以不存在;
+]]
+
+local send_queue = {}
+
+-- BLE client的任务名前缀
+ble_client_sender.TASK_NAME_PREFIX = "ble_client_"
+
+-- ble_client_sender的任务名
+ble_client_sender.TASK_NAME = ble_client_sender.TASK_NAME_PREFIX.."sender"
+
+-- "SEND_DATA_REQ"消息的处理函数
+local function send_data_req_proc_func(tag, service_uuid, char_uuid, data, cb)
+    -- 将数据插入到发送队列send_queue中
+    table.insert(send_queue, {service_uuid=service_uuid, char_uuid=char_uuid, data="send from " .. tag .. ": " .. data, cb=cb})
+    -- 发送消息通知 BLE sender task,有新数据等待发送
+    sysplus.sendMsg(ble_client_sender.TASK_NAME, "BLE_EVENT", "SEND_REQ")
+end
+
+-- 按照顺序发送send_queue中的数据
+-- 如果发送成功,则返回当前正在发送的数据项
+-- 如果发送失败,通知回调函数发送失败后,继续发送下一条数据
+local function send_item_func(ble_device)
+    local item
+    -- 如果发送队列中有数据等待发送
+    while #send_queue>0 do
+        -- 取出来第一条数据赋值给item
+        -- 同时从队列send_queue中删除这一条数据
+        item = table.remove(send_queue, 1)
+
+        -- 发送数据
+        local write_params = {
+            uuid_service = string.fromHex(item.service_uuid),
+            uuid_characteristic = string.fromHex(item.char_uuid)
+        }
+        local data = item.data
+        local result = ble_device:write_value(write_params, data)
+
+        -- 发送接口调用成功
+        if result then
+            -- 保存当前发送项,等待写入完成通知
+            return item
+        -- 发送接口调用失败
+        else
+            -- 如果当前发送的数据有用户回调函数,则执行用户回调函数
+            if item.cb and item.cb.func then
+                item.cb.func(false, item.cb.para)
+            end
+        end
+    end
+end
+
+-- 处理发送结果的回调函数
+local function send_item_cbfunc(item, result)
+    if item then
+        -- 如果当前发送的数据有用户回调函数,则执行用户回调函数
+        if item.cb and item.cb.func then
+            item.cb.func(result, item.cb.para)
+        end
+    end
+end
+
+-- BLE client sender的任务处理函数
+local function ble_client_sender_task_func()
+    local ble_device
+    local send_item
+    local result, msg
+
+    while true do
+        -- 等待"BLE_EVENT"消息
+        msg = sysplus.waitMsg(ble_client_sender.TASK_NAME, "BLE_EVENT")
+
+        -- BLE连接成功
+        -- msg[3]表示ble_device对象
+        if msg[2] == "CONNECT_OK" then
+            ble_device = msg[3]
+            -- 发送send_queue中的数据
+            send_item = send_item_func(ble_device)
+
+        -- BLE发送数据请求
+        elseif msg[2] == "SEND_REQ" then
+            -- 如果ble_device对象存在,发送send_queue中的数据
+            if ble_device then
+                send_item_cbfunc(send_item, true)
+                send_item = send_item_func(ble_device)
+            end
+
+        -- -- BLE发送完成(待完善......)
+        -- elseif msg[2] == "SEND_OK" then
+        --     -- send完成,执行回调并继续发送
+        --     send_item_cbfunc(send_item, true)
+        --     -- 发送send_queue中的下一条数据
+        --     send_item = send_item_func(ble_device)
+
+        -- BLE断开连接
+        elseif msg[2] == "DISCONNECTED" then
+            -- 清空ble_device对象
+            ble_device = nil
+            -- 如果存在正在等待发送结果的发送项,执行回调函数通知发送方失败
+            send_item_cbfunc(send_item, false)
+            -- 如果发送队列中有数据等待发送
+            while #send_queue>0 do
+                -- 取出来第一条数据赋值给send_item
+                -- 同时从队列send_queue中删除这一条数据
+                send_item = table.remove(send_queue,1)
+                -- 执行回调函数通知发送方失败
+                send_item_cbfunc(send_item, false)
+            end
+            -- 当前没有正在等待发送结果的发送项
+            send_item = nil
+        end
+    end
+end
+
+-- 订阅"SEND_DATA_REQ"消息;
+-- 其他应用模块如果需要发送数据,直接sys.publish这个消息即可
+-- 参数: tag(标签), service_uuid(服务UUID), char_uuid(特征值UUID), data(数据), cb(回调函数和参数)
+sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)
+
+--创建并且启动一个task
+--运行这个task的处理函数ble_client_sender_task_func
+sysplus.taskInitEx(ble_client_sender_task_func, ble_client_sender.TASK_NAME)
+
+return ble_client_sender
+

+ 67 - 0
module/Air8101/demo/ble/central/ble_timer_app.lua

@@ -0,0 +1,67 @@
+--[[
+@module  ble_timer_app
+@summary 定时器应用功能模块
+@version 1.0
+@date    2025.08.20
+@author  王世豪
+@usage
+本文件为定时器应用功能模块,核心业务逻辑为:
+创建两个独立的5秒循环定时器,一个用于定时读取外围设备特征值UUID数据,一个用于定时向外围设备特征值UUID发送数据;
+
+本文件的对外接口有2个:
+1、sys.publish("SEND_DATA_REQ", "timer", server_uuid, write_char_uuid, data, {func = send_data_cbfunc, para="timer"..data}),发布SEND_DATA_REQ消息,在ble_client_sender文件中处理,携带的参数为:
+    -- 发布消息"SEND_DATA_REQ"
+    -- 携带的第一个参数"timer"表示是定时器应用模块发布的消息
+    -- 携带的第二个参数server_uuid表示要发送的服务UUID
+    -- 携带的第三个参数write_char_uuid表示要发送的特征值UUID
+    -- 携带的第四个参数data表示要发送的数据
+    -- 携带的第五个参数cb为发送结果回调(可以为空,如果为空,表示不关心ble client 发送数据成功还是失败),其中:
+    --   cb.func为回调函数(可以为空,如果为空,表示不关心ble client发送数据成功还是失败)
+    --   cb.para为回调函数的第二个参数(可以为空),回调函数的第一个参数为发送结果(true表示成功,false表示失败)
+
+2、sysplus.sendMsg(BLE_TASK_NAME,"BLE_EVENT","READ_REQ",server_uuid,read_char_uuid), 发送读取外围设备特征值UUID数据请求,在ble_client_main文件中处理,携带的参数为:
+    -- msg[2]: "READ_REQ" --消息类型
+    -- msg[3]: server_uuid --服务UUID
+    -- msg[4]: read_char_uuid --特征值UUID
+]]
+
+local BLE_TASK_NAME = "ble_client_main"
+local server_uuid = "FA00"
+local write_char_uuid = "EA02"
+local read_char_uuid = "EA03"
+local data = "1234"
+
+-- 数据发送结果回调函数
+-- result:发送结果,true为发送成功,false为发送失败
+-- para:回调参数,sys.publish("SEND_DATA_REQ", "timer", "FA00", "EA02", data, {func=send_data_cbfunc, para="timer"..data})中携带的para
+local function send_data_cbfunc(result, para)
+    log.info("send_data_cbfunc", result, para)
+end
+
+-- 定时器回调函数
+function send_data_req_timer_cbfunc()
+    -- 发布消息"SEND_DATA_REQ"
+    -- 携带的第一个参数"timer"表示是定时器应用模块发布的消息
+    -- 携带的第二个参数server_uuid表示要发送的服务UUID
+    -- 携带的第三个参数char_uuid表示要发送的特征值UUID
+    -- 携带的第四个参数data表示要发送的数据
+    -- 携带的第五个参数cb为发送结果回调(可以为空,如果为空,表示不关心ble client 发送数据成功还是失败),其中:
+    --       cb.func为回调函数(可以为空,如果为空,表示不关心ble client发送数据成功还是失败)
+    --       cb.para为回调函数的第二个参数(可以为空),回调函数的第一个参数为发送结果(true表示成功,false表示失败)
+    sys.publish("SEND_DATA_REQ", "timer", server_uuid, write_char_uuid, data, {func = send_data_cbfunc, para="timer"..data})
+end
+
+local function read_data_req_timer_cbfunc()
+    -- msg[2]: "READ_REQ" --消息类型
+    -- msg[3]: server_uuid --服务UUID
+    -- msg[4]: read_char_uuid --特征值UUID
+    sysplus.sendMsg(BLE_TASK_NAME,"BLE_EVENT","READ_REQ",server_uuid,read_char_uuid)
+end
+
+-- 启动5秒的循环定时器用于发送数据
+sys.timerLoopStart(send_data_req_timer_cbfunc, 5000)
+log.info("TIMER_APP", "已启动发送数据循环定时器,间隔: 5000ms")
+
+-- 启动5秒的循环定时器用于读取数据
+sys.timerLoopStart(read_data_req_timer_cbfunc, 5000)
+log.info("TIMER_APP", "已启动读取数据循环定时器,间隔: 5000ms")

+ 28 - 0
module/Air8101/demo/ble/central/ble_uart_app.lua

@@ -0,0 +1,28 @@
+--[[
+@module  ble_uart_app
+@summary 串口应用功能模块
+@version 1.0
+@date    2025.08.20
+@author  王世豪
+@usage
+本文件为串口应用功能模块,核心业务逻辑为:
+1、打开uart1,波特率115200,数据位8,停止位1,无奇偶校验位;
+2、uart1和pc端的串口工具相连;
+3、收到ble_client通过notify监听的数据后,将数据通过uart1发送到pc端串口工具;
+
+本文件的对外接口有1个:
+1. sys.subscribe("RECV_BLE_NOTIFY_DATA", recv_ble_notify_data_proc),订阅RECV_BLE_NOTIFY_DATA消息,处理消息携带的数据;
+]]
+
+local UART_ID = 1
+-- 初始化UART1,波特率115200,数据位8,停止位1
+uart.setup(UART_ID, 115200, 8, 1)
+
+-- 将service_uuid,char_uuid和data数据拼接
+-- 然后末尾增加回车换行两个字符,通过uart1发送出去,方便在PC端换行显示查看
+local function recv_ble_notify_data_proc(service_uuid, char_uuid, data)
+    uart.write(UART_ID, service_uuid..","..char_uuid..","..data.."\r\n")
+end
+
+-- 订阅"RECV_BLE_NOTIFY_DATA"消息的处理函数recv_ble_notify_data_proc
+sys.subscribe("RECV_BLE_NOTIFY_DATA", recv_ble_notify_data_proc)

+ 14 - 8
module/Air8000/demo/ble/master/main.lua → module/Air8101/demo/ble/central/main.lua

@@ -2,12 +2,15 @@
 @module  main
 @summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
 @version 1.0
-@date    2025.07.01
+@date    2025.08.20
 @author  wangshihao
 @usage
 本demo演示的核心功能为:
-演示了Air8000核心板作为BLE Master的核心功能。
-主要功能是扫描周围的BLE设备,当发现设备名称中包含"LuatOS"的设备时,自动连接该设备,然后进行GATT操作(如开启通知、写入数据、读取数据)等。
+演示了Air8101核心板作为BLE Central(中心设备)的核心功能:
+1、ble中心设备扫描并连接指定外围设备;
+2、ble中心设备连接成功后,开始定时读取外围设备特征值UUID数据, 定时发送数据给外围设备;
+3、ble中心设备收到外围设备特征值UUID的notify数据后,通过uart发送到pc端串口工具;
+4、pc端串口工具收到数据后,打印到串口工具窗口;
 
 更多说明参考本目录下的readme.md文件
 ]]
@@ -22,7 +25,7 @@ VERSION:项目版本号,ascii string类型
             因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
         如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
 ]]
-PROJECT = "ble_master"
+PROJECT = "ble_central"
 VERSION = "001.000.000"
 
 log.info("main", "project name is ", PROJECT, "version is ", VERSION)
@@ -58,11 +61,14 @@ end
 --     log.info("mem.sys", rtos.meminfo("sys"))
 -- end, 3000)
 
--- 如果需要升级WIFI固件,请打开下面注释
-require "check_wifi"
+-- 加载BLE Central(中心设备)主控制模块
+require "ble_client_main"
 
--- 加载 master 蓝牙功能模块
-require "ble_master"
+-- 加载串口应用功能模块
+require "ble_uart_app"
+
+-- 加载定时器应用功能模块
+require "ble_timer_app"
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句

+ 98 - 0
module/Air8101/demo/ble/central/readme.md

@@ -0,0 +1,98 @@
+## 功能模块介绍
+
+1、main.lua:主程序入口;
+
+2、ble_client_main.lua:ble中心设备主程序,进行ble初始化,处理各类ble事件(连接、断开连接、扫描报告、GATT操作完成等);
+
+3、ble_client_receiver.lua:ble中心设备接收数据处理逻辑;
+
+4、ble_client_sender.lua:ble中心设备发送数据处理逻辑;
+
+5、ble_timer_app.lua:ble中心设备定时器处理逻辑,启动两个循环定时器,一个用于定时读取外围设备特征值UUID数据,一个用于定时向外围设备特征值UUID发送数据;
+
+6、ble_uart_app.lua:ble中心设备接uart处理逻辑,将收到的notify数据,通过uart发送到pc端串口工具;
+
+## 用户消息介绍
+
+1、"RECV_BLE_READ_DATA":ble中心设备主动读取到外围设备特征值UUID数据时,会发布此消息,通知其他应用模块(如ble_uart_app)处理数据;
+
+2、"RECV_BLE_NOTIFY_DATA":ble中心设备收到外围设备特征值UUID的notify数据时,会发布此消息,通知其他应用模块(如ble_uart_app)处理数据;
+
+3、"SEND_DATA_REQ":其他应用模块(ble_timer_app)发布此消息,通知ble 中心设备发送publish数据给外围设备;
+
+## 演示功能概述
+
+使用Air8101核心板演示 ble的central(中心设备) 功能。
+
+1、ble中心设备扫描并连接外围设备;
+
+2、ble中心设备连接成功后,开始定时读取外围设备特征值UUID数据, 定时发送数据给外围设备;
+
+3、ble中心设备收到外围设备特征值UUID的notify数据后,通过uart发送到pc端串口工具;
+
+4、pc端串口工具收到数据后,打印到串口工具窗口。
+
+## 演示硬件环境
+
+1、Air8101核心板两块(本次演示中,一块作为中心设备,一块作为外围设备)
+
+2、TYPE-C USB数据线一根
+
+3、USB转串口数据线一根
+
+4、Air8101核心板和数据线的硬件接线方式为
+
+- Air8101核心板通过TYPE-C USB口供电;(核心板背面的功耗测试开关拨到OFF一端)
+
+- 如果测试发现软件频繁重启,重启原因值为:poweron reason 0,可能是供电不足,此时再通过直流稳压电源对核心板的vbat管脚进行4V供电,或者VIN管脚进行5V供电;
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+- USB转串口数据线,一般来说,白线连接核心板的12/U1TX,绿线连接核心板的11/U1RX,黑线连接核心板的gnd,另外一端连接电脑USB口;
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air8101 V1005版本固件](https://docs.openluat.com/air8101/luatos/firmware/)(理论上,2025年7月26日之后发布的固件都可以)
+
+3、PC端的串口工具,例如SSCOM、LLCOM等都可以
+
+## 演示核心步骤
+
+1、搭建好硬件环境
+
+2、Luatools烧录内核固件和修改后的demo脚本代码
+
+作为中心设备的Air8101核心板,需要烧录central代码;
+
+作为外围设备的Air8101核心板,需要烧录peripheral代码;
+
+3、烧录成功后,自动开机运行,如果中心设备出现以下日志,表示中心设备连接外围设备成功
+
+```lua
+I/user.ble 发现目标设备: LuatOS
+I/user.ble 停止扫描, 连接设备 C8C2C68D4FF6 0
+```
+4、如果中心设备出现以下日志,则表示GATT服务发现完成,此时中心设备便可以进行读/写/监听操作
+
+```lua
+I/user.ble gatt item table: 60C78190
+I/user.ble gatt item table: 60C77F90
+I/user.ble gatt item table: 60C77DB0
+I/user.ble GATT服务发现完成
+```
+5、打开PC端的串口工具,选择对应的端口,配置波特率115200,数据位8,停止位1,无奇偶校验位;
+
+6、在PC端的串口工具窗口中,会打印出中心设备监听到的外围设备notify数据;
+
+```lua
+-- 以下便是uart接收到的notify数据,数据格式为:服务UUID,特征值UUID,特征值数据
+FA00,EA01,123456Thu Jan  1 08:00:25 1970
+```
+7、在luatools日志中可以看到中心设备主动写入外围设备指定特征值UUID的数据,以及向外围设备写入的结果。
+
+```lua
+-- 中心设备写入数据后,会有数据发送结果回调函数触发,下面打印的"true" 表示发送成功,timer后面的“1234”便是发送的数据。
+I/user.send_data_cbfunc true timer1234
+```