由于STM32的板子没有自带蓝牙和wifi模组,所以外设方面的demo,我选择放到乐鑫的ESP32模组上。一方面是由于ESP32这块板子有丰富的BT/WIFI的实现例程,还因为乐鑫的这款SOC扩展性很强,自身的SDK: ESP-IDF也比较成熟。
BLE Peripheral指的是蓝牙外设,比如蓝牙手环,蓝牙电视遥控器等,都是这类走蓝牙低功耗的外设设备。与外设设备相对应的就是BLE Central,指的是蓝牙中心设备,比如用户用的手机。通过手机去获得外设的信息,信息传输的发起方主要是手机(Central),而信息的提供方主要是外设(Peripheral)。外设会提供各种各样的蓝牙服务。比如就拿蓝牙手环举例,通过手环,我们可以获取用户的
- 心跳信息
- 行动步数
- 电池电量
以上三类信息,在BLE传输协议中,可以认为是三个GATT service。其中心跳信息和电池电量信息的信息协议其实已经有官方定义,见 GATT Services , 每个协议具体规范见GATT Specifications,另外,每个service中的characteristic 也按照官方定义GATT Characteristic。所以,peripheral只要按照协议的要求,定制相应的service uuid,descriptor,和characteristics等,host连接后就可以查询到相应服务就可以知道获取到的信息是心跳信息(0x180D),电池电量(0x180F)等。
但是对于行动步数,由于规范上并没有定义,所以这类信息只能按照私自约定的方式定义,不要和官方定义的service,characteristics等冲突就行。
比如我们可以定义步数的service和characteristic如下:
#define BT_UUID_PRIV_DATA BT_UUID_DECLARE_16(0xfff0)
#define BT_UUID_PRIV_DATA_VAL 0xfff0 //步数service
#define BT_UUID_PRIV_CHW_DATA BT_UUID_DECLARE_16(0xfff1)
#define BT_UUID_PRIV_CHW_DATA_VAL 0xfff1 //host写,用于步数清零
#define BT_UUID_PRIV_CHR_DATA BT_UUID_DECLARE_16(0xfff2)
#define BT_UUID_PRIV_CHR_DATA_VAL 0xfff2 //host读,用于读取当前步数
#define BT_UUID_PRIV_CHN_DATA BT_UUID_DECLARE_16(0xfff3)
#define BT_UUID_PRIV_CHN_DATA_VAL 0xfff3 //notify,host打开后,
//步数每累计超过5,peripheral就主动更新当前步数给host
这里我们定义了一个service和其中三个characteristic, 用于模拟手环peripheral和host同步步数信息。BLE三种主要的同步手段是write, read, notify。当然还有其他同步方式,有兴趣可以自己了解。
我们用两个byte用于记录步数(最大可以记录65535步)。具体设备端demo可以参考https://github.com/13xiaobang/AliOS-Things 上13xiaobang_modify分支上的blebanddemo用例
设备端的步数service核心code, 也就是att的设置如下:
/* Step Service Declaration */
static struct bt_gatt_attr attrs[] = {
//主服务的service UUID为0xfff0
BT_GATT_PRIMARY_SERVICE(BT_UUID_PRIV_DATA),
//设置0xfff1 characteristic的可写属性
BT_GATT_CHARACTERISTIC(BT_UUID_PRIV_CHW_DATA, BT_GATT_CHRC_WRITE),
//设置0xfff1描述符,权限为可写,并设置host端写时的回调,用于步数清零。
BT_GATT_DESCRIPTOR(BT_UUID_PRIV_CHW_DATA, BT_GATT_PERM_WRITE, NULL,
write_blsc, NULL),
//设置0xfff2 characteristic的可读属性
BT_GATT_CHARACTERISTIC(BT_UUID_PRIV_CHR_DATA, BT_GATT_CHRC_READ),
//设置0xfff2描述符,权限为可读,并设置host端读时的回调,用于返回读值。
BT_GATT_DESCRIPTOR(BT_UUID_PRIV_CHR_DATA, BT_GATT_PERM_READ,
read_blsc, NULL, NULL),
//设置0xfff3 characteristic的notify属性
BT_GATT_CHARACTERISTIC(BT_UUID_PRIV_CHN_DATA, BT_GATT_CHRC_NOTIFY),
//设置 0xfff3 characteristic的可读属性
BT_GATT_DESCRIPTOR(BT_UUID_PRIV_CHN_DATA, BT_GATT_PERM_READ, NULL,
NULL, NULL),
//设置0xfff3 characteristic 属性变换后的回调函数
BT_GATT_CCC(privc_ccc_cfg, privc_ccc_cfg_changed),
};
接着我们设计host读写device的接口,每当执行读操作,读回当前priv_step p值,每当执行写操作,priv_step 置零,重新开始计数
static ssize_t read_blsc(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, u16_t len, u16_t offset)
{
printf("host read=%d\n", priv_step);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &priv_step,
sizeof(priv_step));
}
static ssize_t write_blsc(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *buf, u16_t len, u16_t offset, u8_t flags)
{
int i = 0;
printf("host write: \n");
for(; i<len; i++)
{
printf("%x ", ((char*)buf)[i]);
}
printf("\n");
//every time host write, reset step.
priv_step = 0;
}
最后,我们为了demo简便,每隔1s,将priv_step++,并且当notify打开情况下,每隔5s向host上报一次当前priv_step值。
void ble_sample(void)
{
int err = 0;
hci_driver_init();
err = bt_enable(bt_ready);
if (err) {
printf("Bluetooth init failed (err %d)\n", err);
return;
}
#ifdef CONFIG_BT_SMP
//bt_conn_auth_cb_register(&auth_cb_display);
#endif
bt_conn_cb_register(&conn_callbacks);
while (1) {
aos_msleep(1000);
priv_notify();
}
printf("Advertising successfully started\n");
}
void priv_notify(void)
{
int err = 0;
/* step increase 1 every second*/
priv_step++;
//if notify close ,return.
if (!simulate_priv) {
return;
}
if(priv_step%5 == 0) {
err = bt_gatt_notify(NULL, &attrs[6], &priv_step, sizeof(priv_step));
printf("simulate_priv notify = %d, err = %d\n", priv_step, err);
}
}
设置完成后,我们可以通过手机侧安装的host的demo app(可以参考https://github.com/13xiaobang/bledebugger或者安装蓝牙调试助手等apk),连接esp32上运行的程序来进行读写操作。
esp32上code:https://github.com/13xiaobang/AliOS-Things 上13xiaobang_modify分支
编译方式:
aos make bluetooth.blebanddemo@esp32devkitc -c config && aos make
烧录方式:
下载esp32烧录工具:
https://www.espressif.com/zh-hans/support/download/other-tools
烧录工具设置
注意flash下载地址:编译出的镜像放在0x10000, bootloader和分区表分别放0x1000和0x8000
然后,我们就可以通过手机,对esp32板子做读,写和监控notify操作了:
fff0为我们自定义的手环私有service:
自定义service中的3个characteristic
写操作
读操作
监控notify