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