BLE:低功耗蓝牙,采用蓝牙4.0技术具有低成本,短距离特点。可以用于电子手环,蓝牙门锁等场景。
BLE如何进行通信 广播BLE分为:中心设备和外设设备。
外设设备用GAP协议以广播形式告知中心设备,本机蓝牙可以被链接。中心设备可选择对发送过来的广播数据回复。回复的内容有设备名称,设备地址。也可不回复。
连接建立后就以client-server方式联系
中心设备(手机)作为client
外设设备(手环)作为serve
GATT协议链接建立后,中心设备与外设设备用GATT协议通信
蓝牙应用层的协议,以profile文件的形式存储。
Profile文件结构如下
Service1
Characterisitic1
Characterisitic2
Service2
Characterisitic
….
每个profile包含若干个service,每个service包含若干个characteristic
而characteristic包含Properties ,value。
Properties 规定了cilent应以怎样的方式处理characteristic的内容
Properties的属性如下
属性名称 | 描述 |
---|---|
read | 支持 read 操作 |
write | 支持 write 操作 |
notify | 支持 notify 操作 |
indicate | 支持 indicate 操作 |
当service端的蓝牙 characteristicr是Indicate 属性 客户端就必须要以indicate方式来读写characteristic 不能用其他三种。 |
无论是service还是characteristic最后都被封装成(Attribute
)att数据包。
ATT协议包括三部分 handle ,type,value
Handle: Attribute句柄。Client要访问Server的Attribute,都是通过这个句柄来访问的
在蓝牙数据包中一般都有这个值
Type :由UUID来定义。蓝牙联盟有统一的UUID设备标识。
具体使用使用时可以自定义也可用蓝牙联盟的。
Value:蓝牙数据包具体携带的值。
EFR32的BLE配置工具到此我们只关心三件事情。
1.创建我们自定义的service和characteristic
2.用自定义的的characteristic向client发送数据
3.client接收数据
而GAP 和GATT都由EFR32的蓝牙SDK完成。
为了方便起见,我们首先分析一下EFR32的温度传感器例程代码。
在使用IDE的配置工具配置自定义的BLE任务
EFR32把蓝牙程序分成了两部分,一部分是bootloader,另一部分是蓝牙程序。
在下载蓝牙例程,需要先下载蓝牙的bootloader
导入一个蓝牙温度的例程
下载
https://www.silabs.com/documents/public/software/android-efr-connect.apk
如图所示
打开串口
打开IDE
看到有BLE组件
EFR32开发蓝牙工程分析及自定义一个蓝牙任务发送数据蓝牙工程分析先来找蓝牙发送函数
搜索字符串“Temperature:”在工程中位置
在app.c的app_periodic_timer_cb函数里
这个函数是定时器的回调函数
周期性的获取温度值,并通过app_log_info和sl_bt_ht_temperature_measurement_indicate发送到串口与蓝牙。
sl_sensor_rht_get是温度获取函数
那么谁来调用app_periodic_timer_cb函数呢?
在app.的sl_bt_ht_temperature_measurement_indication_changed_cb
函数里调用用了app_periodic_timer_cb
通过源码可知,这个函数是在蓝牙连接建立后,client访问server中characteristic中的temperature_measurement子项时调用的。
调用方式是indicate。意思是server端发送完成后,client需要回复确认收到。
在sl_bt_ht_temperature_measurement_indication_changed_cb
进行了开启定时器,绑定回调函数操作
那sl_bt_ht_temperature_measurement_indication_changed_cb
是谁在调用?
是sl_bt_ht_on_event在调用
sl_bt_ht_on_event是蓝牙温度测量事件函数。
这个函数完成了温度测量的 characteristic从开始发送到发送完成关闭定时器的操作。
关心这几个部分。
sl_bt_gatt_server_write_attribute_value
在本地 GATT 数据库中写入属性值。如果本地 GATT 数据库中的属性具有指示或通知属性,且客户端已启用通知或指示功能,则写入该属性的值不会触发向远程 GATT 客户端发送通知或指示。
gattdb_temperature_measurement
case sl_bt_evt_gatt_server_characteristic_status_id:
*@brief表示本地客户端特征配置
*描述符被远程GATT客户端更改,或者来自的确认
*远程GATT客户端在成功接收
*指示
*
来梳理下sl_bt_ht_on_event完成的事情
刚开始进入蓝牙温度任务
先执行 health_thermometer_init();
往蓝牙gatt数据库中写入temperature_type的属性。
温度数据发送都是按照这个属性(att)发送的
之后case sl_bt_evt_gatt_server_characteristic_status_id
说temperature_type的属性的蓝牙通信建立。
gattdb_temperature_measurement == evt->data.evt_gatt_server_characteristic_status.characteristic
判断蓝牙句柄拿到的uuid是否是gatt数据库中有的
(sl_bt_gatt_server_client_config == (sl_bt_gatt_server_characteristic_status_flag_t)evt->data.evt_gatt_server_characteristic_status.status_flags
客户端的characteristic已经改变
执行sl_bt_ht_temperature_measurement_indication_changed_cb
当蓝牙温度att通信关闭执行
case sl_bt_evt_connection_closed_id:
sl_bt_connection_closed_cb(evt->data.evt_connection_closed.reason,
evt->data.evt_connection_closed.connection);
break;
接下来看看sl_bt_ht_on_event是由谁调用
在sl_bluebooth.c的sl_bt_process_event里调用。
这是处理蓝牙任务函数包括
sl_bt_in_place_ota_dfu_on_event(evt);
sl_gatt_service_device_information_on_event(evt);
sl_bt_ht_on_event(evt);
sl_bt_on_event(evt);//这是个虚函数里面没有代码
与配置工具对应
在在sl_bluebooth.c的sl_bt_step里调用。sl_bt_step轮询蓝牙堆栈中的事件并进行处理
在sl_event_handler.c的sl_stack_process_action调用sl_bt_step
在sl_system_process_action.c的sl_system_process_action调用sl_stack_process_action
sl_system_process_action进行系统初始化和操作处理
*此函数调用一组自动生成的函数,这些函数位于`autogen/sl_event_handler.c`中。
可以使用事件处理程序组件提供的事件处理程序API为以下事件注册处理程序:
* - platform_init -> sl_platform_init()
* - driver_init -> sl_driver_init()
* - service_init -> sl_service_init()
* - stack_init -> sl_stack_init()
* - internal_app_init -> sl_internal_app_init()
sl_system_process_action()在main函数while中调用
到这里已经把应用相关代码过了一遍
如图
我们只需要关心
修改sl_bt_ht_on_event函数变成我们自己的
仿照修改即可
接下来看看发送函数
sc = sl_bt_ht_temperature_measurement_indicate(app_connection,
false);
app_connection,蓝牙句柄
temperature温度值
false是摄氏度/true是华氏温度
在sl_bt_ht_temperature_measurement_indicate里进行了温度的数据封装操作
temperature_measurement_val_to_buf
把温度数据封装到五个字节的数据包里
第一个字节是温度类型
其余是温度值
uint32_t tmp_value = ((uint32_t)value & 0x00ffffffu) \
| ((uint32_t)(-3) << 24);
buffer[0] = fahrenheit ? TEMPERATURE_MEASUREMENT_FLAG_UNITS : 0;
buffer[1] = tmp_value & 0xff;
buffer[2] = (tmp_value >> 8) & 0xff;
buffer[3] = (tmp_value >> 16) & 0xff;
buffer[4] = (tmp_value >> 24) & 0xff;
要转回去
a= (data[4]<<8)|data[3];
a=(a<<8)|data[2];
a=(a<<8)|data[1];
a=a&0xffffff;
用最底层sl_bt_gatt_server_send_indication发送
sc = sl_bt_gatt_server_send_indication(
gattdb_temperature_measurement,
sizeof(buf),
connection句柄
gattdb_temperature_measurement gatt的属性
sizeof(buf),数据包大小
buf数据包 是一个指针类型const uint8_t* value
自定义GATT的service characteristic 并发送
进入blue配置工具配置GATT数据库
如图
GATT数据库
再添加一个
接下
修改即可
在Health_Thermometer添加
void demo_init()
{
}
void sl_bt_demo_on_event(sl_bt_msg_t *evt)
{
// Handle stack events
switch (SL_BT_MSG_ID(evt->header)) {
case sl_bt_evt_system_boot_id:
demo_init();
break;
case sl_bt_evt_connection_closed_id:
sl_bt_demo_connection_closed_cb(evt->data.evt_connection_closed.reason,
evt->data.evt_connection_closed.connection);
break;
case sl_bt_evt_gatt_server_characteristic_status_id:
if (gattdb_test == evt->data.evt_gatt_server_characteristic_status.characteristic) {
// client characteristic configuration changed by remote GATT client
if (sl_bt_gatt_server_client_config == (sl_bt_gatt_server_characteristic_status_flag_t)evt->data.evt_gatt_server_characteristic_status.status_flags) {
sl_bt_demo_indication_changed_cb(
evt->data.evt_gatt_server_characteristic_status.connection,
(sl_bt_gatt_client_config_flag_t)evt->data.evt_gatt_server_characteristic_status.client_config_flags);
}
// confirmation of indication received from remove GATT client
else if (sl_bt_gatt_server_confirmation == (sl_bt_gatt_server_characteristic_status_flag_t)evt->data.evt_gatt_server_characteristic_status.status_flags) {
sl_bt_demo_indication_confirmed_cb(
evt->data.evt_gatt_server_characteristic_status.connection);
} else {
}
}
break;
default:
break;
}
}
App.c里添加
//demo code
#include "gatt_db.h"
static app_timer_t app_periodic_timer1;
static uint8_t app_connection1 = 0;
void demo_timer_cb()
{printf("demo_timer_cb()");
uint8_t c=10;
sl_bt_gatt_server_send_indication(
app_connection1,
gattdb_test,//要发送数据的characteristic
1,
&c);
}
/**************************************************************************//**
demo
* Indication changed callback
*
* Called when indication of temperature measurement is enabled/disabled by
* the client.
*****************************************************************************/
void sl_bt_demo_indication_changed_cb(uint8_t connection,
sl_bt_gatt_client_config_flag_t client_config)
{
app_connection1 = connection;
// Indication or notification enabled.
if (sl_bt_gatt_disable != client_config) {
// Start timer used for periodic indications.
app_timer_start(&app_periodic_timer1,
1 * 1000,
demo_timer_cb,
NULL,
true);
// Send first indication.
demo_timer_cb();
}
// Indications disabled.
else {
// Stop timer used for periodic indications.
(void)app_timer_stop(&demo_timer_cb);
}
}
void sl_bt_demo_connection_closed_cb(uint16_t reason, uint8_t connection)
{
(void)reason;
(void)connection;
sl_status_t sc;
// Stop timer.
sc = app_timer_stop(&app_periodic_timer1);
}
并在sl_bt_process_event添加sl_bt_demo_on_event
结果如图主要参考
Silabs的蓝牙demo文档
UG103.14: Bluetooth® LE Fundamentals (silabs.com)
QSG169: Bluetooth® Quick-Start Guide for SDK v3.x and Higher (silabs.com)
ug434-bluetooth-c-soc-dev-guide-sdk-v3x.pdf (silabs.com)
博客园夜行过客专栏
https://www.cnblogs.com/yongdaimi/category/1543239.html
龙言飞语
https://zhuanlan.zhihu.com/p/346972549
作者: x鑫鑫, 来源:面包板社区
链接: https://mbb.eet-china.com/blog/uid-me-3984612.html
版权声明:本文为博主原创,未经本人允许,禁止转载!
文章评论(0条评论)
登录后参与讨论