跳转至

02 BLE 外围设备

一、BLE 概述

BLE(Bluetooth Low Energy),也称为 Bluetooth Smart,是蓝牙 4.0 及更高版本引入的低功耗无线通信技术,专为低带宽、间歇性数据传输的物联网(IoT)和穿戴设备设计。

Air8000 支持最新的 BLE 5.4 版本,BLE 5.4 在上一代基础上继续优化了功耗和性能,为用户提供了更高效、更稳定的蓝牙连接体验。具体的 BLE 版本区别若有兴趣请自行上网查询,本处不再赘述。

Air8000 的 BLE 支持 4 种模式,分别是主机模式(central),从机模式(peripheral),广播者模式(ibeacon),以及观察者模式(scan)。

1.主机模式(central):

主机模式是能够搜索别人并主动建立连接的一方,从扫描状态转化而来的。其可以和一个或多个从设备进行连接通信,它会定期的扫描周围的广播状态设备发送的广播信息,可以对周围设备进行搜索并选择所需要连接的从设备进行配对连接,建立通信链路成功后,主从双方就可以发送接收数据。

2.从机模式(peripheral):

从机模式是从广播者模式转化而来的,未被连接的从机首先进入广播状态,等待被主机搜索,当主机扫描到从设备建立连接后,就可以和主机设备进行数据的收发,其不能主动的建立连接,只能等别人来连接自己。和广播模式有区别的地方在于,从机模式的设备是可以被连接的,定期的和主机进行连接和数据传输,在数据传输过程中作从机。

3.广播者模式(ibeacon)

处于广播模式的设备,会周期性的广播 beacon 信息, 可以被扫描到, 但一般不会被连接。

4.观察者模式(scan)

观察者模式,该模式下模块为非连接,相对广播者模式的一对多发送广播,观察者可以一对多接收数据。在该模式中,设备可以仅监听和读取空中的广播数据。和主机唯一的区别是不能发起连接,只能持续扫描从机。

蓝牙中的重要概念

1. GATT(通用属性配置文件)

定义 BLE 设备如何组织和传输数据,以 “服务(Service)” 和 “特征(Characteristic)” 为单位。

示例:心率监测设备的 GATT 服务包含 “心率特征”,手机通过读取该特征获取心率数据。

2. 服务和特征

服务是特征的容器,通过逻辑分组简化复杂功能的管理;

特征是数据交互的最小单元,通过属性定义实现灵活的读写与推送机制;

两者结合构成 GATT 协议的核心框架,支撑蓝牙设备间的标准化数据交互(如智能穿戴、医疗设备、物联网传感器)。

3. 特征的关键属性(Properties)

特征通过 “属性” 定义数据的操作方式,常见属性包括:

a. 可读(Read):允许客户端读取特征值(如读取电池电量)。

b. 可写(Write):允许客户端写入特征值(如设置设备参数)。

c. 通知(Notification):服务端主动发送特征值更新(如心率变化时推送给手机)。

d. 指示(Indication):比通知更可靠的推送(需客户端确认接收)

4. UUID

UUID 是蓝牙 GATT 协议的 “数字身份证”,通过标准化的唯一标识机制,实现了跨厂商设备的功能互认(标准 UUID)与厂商个性化功能的扩展(自定义 UUID)

Air8000 的所有操作,都通过 UUID 来索引和管理

二、演示功能概述

本示例将演示如何使用 Air8000 在从机模式下工作。

从机模式(peripheral)的基本流程(概要描述)如下:

1. 初始化蓝牙框架

2. 创建 BLE 对象

local ble_device = bluetooth_device:ble(ble_event_cb)

3. 创建 GATT 描述

local att_db = { string.fromHex("FA00"), -- 服务 UUID ble.NOTIFY | ble.READ | ble.WRITE -- 该特征的熟悉,支持通知,读,写 }

4. 创建广播信息

5. 开始广播

ble_device:adv_start()

6. 等待连接

7. 在回调函数中处理连接事件, 如接收数据, 发送数据等

三、准备硬件环境

3.1 Air8000 核心板

使用 Air8000 开发套件,如下图所示:

淘宝购买链接:Air8000 开发套件淘宝购买链接

此开发套件的详细使用说明参考:硬件手册和证书 - product@air8000 - 合宙模组资料中心中的 Core_Air8000 核心板使用说明 V1.3.pdf

3.2 PC 电脑

WINDOWS 系统,其他暂无特别要求;

3.3 数据通信线

USB 数据线(其一端为 Type-C 接口,用于连接 Air8000 开发板)。

四、准备软件环境

4.1 下载调试工具

使用说明参考:如何使用 LuaTools 烧录软件 - luatos@air8000 - 合宙模组资料中心

4.2 蓝牙调试工具

本示例使用的蓝牙调试软件是 nrf connect ,Android 版,下载下面的压缩包;IOS 版请在APP Store搜索 nrf connect下载。

点我,下载ANDROID版 nrf connect APP

五、软硬件资料

5.1 源码及固件

5.2 demo 使用 api 介绍

本教程使用的 api 库: https://docs.openluat.com/osapi/core/ble/

六、代码解析

6.1 初始化蓝牙框架

-- 初始化蓝牙框架, 仅需要调用一次
-- 若创建失败, 会返回nil, 请检查内存是否足够
bluetooth_device = bluetooth.init()

6.2 创建 BLE 对象

ble_callback 是自定义函数,用于处理 BLE 事件,详见 6.6

-- 创建BLE对象, 需要先调用bluetooth.init()
ble_device = bluetooth_device:ble(ble_callback)

6.3 创建 GATT 描述

定义一个的 GATT 服务及其特征,并通过 ble_device 对象创建该服务。

Properties, 对应蓝牙特征的属性,包括 READ,WRITE,NOTIFY,IND,这些属性是通过按位或(|)操作组合起来的。

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.IND: 可指示
    }
}

-- 创建一个BLE GATT服务
ble_device:gatt_create(att_db)

6.4 创建广播信息

-- 创建广播信息
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")}}
    }
)

6.5 开始广播

ble_device:adv_start()

6.6 在回调函数中处理连接事件,如接受数据,发送数据等

-- function ble_callback(dev, evt, param)
-- ble_callback 是自定义函数, 用于处理BLE事件
-- BLE事件回调函数, 回调时的参数如下:
-- dev 是BLE设备对象, 可以通过dev:adv_create()等方法进行操作
-- evt 是BLE事件类型, 可以是以下几种:
    -- ble.EVENT_CONN: 连接成功
        -- param 是事件参数, 包含以下字段:
        -- param.addr: 对端设备的地址, 6字节的二进制数据, 代表BLE设备的MAC地址
    -- ble.EVENT_DISCONN: 断开连接
        -- param 是事件参数, 包含以下字段:
        -- param.reason: 断开连接的原因
    -- ble.EVENT_WRITE: 收到写请求
        -- param 是事件参数, 包含以下字段:
        -- param.uuid_service: 服务的UUID
        -- param.uuid_characteristic: 特征的UUID
        -- param.uuid_descriptor: 描述符的UUID, 可选, 不一定存在
        -- param.data: 写入的数据
    -- ble.EVENT_SCAN_REPORT: 扫描到设备
        -- param 是事件参数, 包含以下字段:
        -- param.rssi: 信号强度
        -- param.adv_addr: 广播地址, 6字节的二进制数据
        -- param.data: 广播数据, 二进制数据

local function ble_callback(dev, evt, param)
    if evt == ble.EVENT_CONN then
        log.info("ble", "connect 成功", param, param and param.addr and param.addr:toHex() or "unknow")
        ble_stat = true
    elseif evt == ble.EVENT_DISCONN then
        log.info("ble", "disconnect")
        ble_stat = false
        -- 1秒后重新开始广播
        sys.timerStart(function() dev:adv_start() end, 1000)
    elseif evt == ble.EVENT_WRITE_REQ then
        -- 收到写请求
        log.info("ble", "接收到写请求", param.uuid_service:toHex(), param.data:toHex())
    end
end

七、运行结果展示

7.1 完整代码

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "ble"
VERSION = "1.0.0"

-- 引入必要的库文件(lua编写), 内部库不需要require
sys = require("sys")

log.info("main", "project name is ", PROJECT, "version is ", VERSION)

-- 通过boot按键方便刷Air8000S
function PWR8000S(val) gpio.set(23, val) end

gpio.debounce(0, 1000)
gpio.setup(0, function()
    sys.taskInit(function()
        log.info("复位Air8000S")
        PWR8000S(0)
        sys.wait(20)
        PWR8000S(1)
    end)
end, gpio.PULLDOWN)

local att_db =   { -- Service
    string.fromHex("FA00"), -- Service UUID
    -- Characteristic
    { -- Characteristic 1
        string.fromHex("EA01"), -- Characteristic UUID Value
        ble.NOTIFY | ble.READ | ble.WRITE -- Properties
    }, { -- Characteristic 2
        string.fromHex("EA02"), ble.WRITE
    }, { -- Characteristic 3
        string.fromHex("EA03"), ble.READ
    }, { -- Characteristic 4
        string.fromHex("EA04"), ble.IND | ble.READ
    }
}

ble_stat = false

local function ble_callback(dev, evt, param)
    if evt == ble.EVENT_CONN then
        log.info("ble", "connect 成功", param, param and param.addr and param.addr:toHex() or "unknow")
        ble_stat = true
    elseif evt == ble.EVENT_DISCONN then
        log.info("ble", "disconnect")
        ble_stat = false
        -- 1秒后重新开始广播
        sys.timerStart(function() dev:adv_start() end, 1000)
    elseif evt == ble.EVENT_WRITE_REQ then
        -- 收到写请求
        log.info("ble", "接收到写请求", param.uuid_service:toHex(), param.data:toHex())
    end
end

local bt_scan = false -- 是否扫描蓝牙

sys.taskInit(function()
    local ret = 0
    sys.wait(500)
    log.info("开始初始化蓝牙核心")
    bluetooth_device = bluetooth.init()
    sys.wait(100)
    log.info("初始化BLE功能")
    ble_device = bluetooth_device:ble(ble_callback)
    if ble_device == nil then
        log.error("当前固件不支持完整的BLE")
        return
    end
    sys.wait(100)

    log.info('开始创建GATT')
    ret = ble_device:gatt_create(att_db)
    log.info("创建的GATT", ret)

    sys.wait(100)
    log.info("开始设置广播内容")
    ble_device:adv_create({
        addr_mode = ble.PUBLIC,
        channel_map = ble.CHNLS_ALL,
        intv_min = 120,
        intv_max = 120,
        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")}
        }
    })

    sys.wait(100)
    log.info("开始广播")
    ble_device:adv_start()

    while 1 do
        sys.wait(3000)
        if ble_stat then
            local wt = {
                uuid_service = string.fromHex("FA00"),
                uuid_characteristic = string.fromHex("EA01"), 
            }
            local result = ble_device:write_notify(wt, "123456" .. os.date())
            log.info("ble", "发送数据", result)
        else
            -- log.info("等待连接成功之后发送数据")
        end
    end
end)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

7.2 结果演示

7.2.1 写操作-APP 发送到蓝牙从机设备的操作

1、烧录示例 demo 到 Air8000 核心板后,手机端打开 nrf connect 蓝牙调试软件,找到广播的设备名称为"LuatOS123"的设备并连接。

2、手机连接从机设备后,界面如下所示:

Generic Access 和 Generic Attribute 是 BLE 共有的服务,Unknown Service 为广播者自定义添加的服务,点开 Unknown Service ,对应的是自定义的服务 UUID 和特征值 UUID 以及其属性值。

3、写操作

手机通过 nrf connect 中服务 UUID=0xFA00,特征值 UUID=0xEA02(可写操作),向从机设备发送数据。

nrf connect 操作界面如下:

luatools 日志如下:

7.2.2 监听操作-蓝牙从机设备发送到 APP 的数据

点击图标”↓↓↓“后,APP 后台自动监听从设备 notify 上来的数据,右滑界面,可以看到接收到的每条数据。

八、总结

本文介绍了 Air8000 的 BLE 从机模式,通过示例演示了如何发送通知,以及如何通过手机对从机设备进行读写操作。