dql2016 发表于 2021-6-24 22:27

RSL10 BLE蓝牙广播

本帖最后由 dql2016 于 2021-6-28 12:18 编辑

<p>&nbsp;</p>

<p>之前一直在研究RSL10如何跟微信小程序通信,将采集的数据发送给微信小程序,通过阅读安森美的文档和一些关于蓝牙的资料得知可以通过广播或者通知、读特征值的方式实现。其中广播或者通知是蓝牙设备主动将数据发送给微信小程序,而读特征值是需要微信小程序主动发送指令给蓝牙设备请求数据。为了简单起见,先使用广播方式发送数据,受到<a href="https://home.eeworld.com.cn/space-uid-799371.html" target="_blank">w494143467</a>大神的帖子<span style="background-color:#9b59b6;">【环境专家之智能手表】Part6:BLE广播温度、湿度和气压数据</span><a href="https://bbs.eeworld.com.cn/thread-1168955-1-1.html">https://bbs.eeworld.com.cn/thread-1168955-1-1.html</a>启发,我详细学习了一下蓝牙的广播的知识,并成功的修改了RSL10的例程,将<span style="background-color:#e74c3c;">自定义</span>的数据通过广播发送到手机蓝牙调试app上。在此对<a href="https://home.eeworld.com.cn/space-uid-799371.html" target="_blank">w494143467</a>的无私分享表示感谢<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/congra.gif" width="48" />!</p>

<p>蓝牙规范可以在官方网站<a href="https://www.bluetooth.com/specifications/specs/">https://www.bluetooth.com/specifications/specs/</a>搜索&quot;core&quot;关键字即可下载,最新的规范是5.2。蓝牙网络有三种连接方式,如图1所示,可以点对点,一对多,星型组网。每一种连接方式在连接建立之前都需要一种机制让两个蓝牙设备连接起来,广播就是这种机制中十分重要的一环。</p>

<p></p>

<p><strong><em>图1 蓝牙三种网络连接方式</em></strong></p>

<p>低功耗蓝牙设备通过广播信道发现其它设备,一个设备进行广播,而另一个设备进行扫描,这样进行扫描的设备就可以发现广播设备然后发起连接了。本次大赛提供的RSL10支持蓝牙5.0,蓝牙5.0是2016年提出的蓝牙技术标准,它把广播信道抽象为两类,一种叫主广播信道(primary advertisement channels),工作在37,38,39三个信道中,频率分别是2402 MHz、2426 MHz、2480 MHz。<span style="font-size: 14px;">在学习蓝牙广播的过程中需要知道的两个角色</span><span style="font-size: 14px;">:</span></p>

<p>广播者:发送广播数据</p>

<p>观察者:扫描广播者</p>

<p><em><strong>图2 蓝牙广播</strong></em></p>

<p>一个广播设备发送的广播可以被多个观察者设备接收到,如图2所示,广播是蓝牙通信中唯一的一种设备可以同时的向多个设备发送数据的通信方式。标准广播包的数据载荷是31字节,用于描述广播者和它具备的特性。&nbsp;但也可以包含你想发送给其它设备的自定义数据。如果标准31字节载荷不够用,BLE也支持扩展的广播载荷(称之为扫描响应),&nbsp;扫描响应载荷也是31字节,因此通过广播,蓝牙设备可以一次发送62字节的数据给周围的设备。<a data-primary="Scan Response" data-type="indexterm" id="idp287088"></a>广播最大的缺点是数据安全性,因为任何设备都能扫描到广播包。广播和扫描响应数据的格式必须满足下图3的要求,可以包含多个AD数据段,但是每个AD数据段必须由&ldquo;Length:Data&rdquo;组成,其中Length占用1个octet,Data部分占用Length个字节,所以一个AD段的长度为:Length+1。</p>

<dl>
        <dd>
        <p><em><strong>图3 蓝牙广播和响应数据包结构</strong></em></p>
        </dd>
</dl>

<p>总接一下,BLE蓝牙广播有两个基本特点,分别是:</p>

<p>1.广播包数据最大长度为<span style="background-color:#e74c3c;">31字节</span><br />
2.数据格式为<span style="background-color:#2ecc71;">长度(1字节)类型(1字节)数据(n字节)......</span></p>

<p>其中<span style="background-color:#f1c40f;">长度=n+1</span></p>

<p>结合例程<span style="color:#e74c3c;"><strong>ble_peripheral_server_hrp</strong></span>进行测试,将工程复制到工作区。</p>

<p>这个例程没有开启SEGGER RTT打印调试,不是很方便,这里就手动添加一下。首先在工程属性中增加宏定义<span style="background-color:#9b59b6;">RSL10_DEBUG=DBG_RTT</span></p>

<p>然后双击<span style="background-color:#9b59b6;">ble_peripheral_server_hrp.rteconfig</span>,勾上SEGGER RTT功能。</p>

<p>编译一下,可以看到相关文件自动添加进来了,没有错误。</p>

<p></p>

<p>在app.c的main函数中调用了函数</p>

<pre>
<code class="language-bash">APP_SetAdvScanData();</code></pre>

<p>该函数进行广播数据和扫描响应数据内容的设置,在app_config.c中实现</p>

<pre>
<code class="language-cpp">/* ----------------------------------------------------------------------------
* Function      : void APP_SetAdvScanData(void)
* ----------------------------------------------------------------------------
* Description   : Initializes the advertisement data structure
* Inputs      : None
* Outputs       : None
* Assumptions   : None
* ------------------------------------------------------------------------- */
/**
* <a href="home.php?mod=space&amp;uid=159083" target="_blank">@brief</a> Initializes the advertisement data structure
*
* Assumptions   : None
*/
void APP_SetAdvScanData(void)
{
    uint8_t companyID[] = APP_COMPANY_ID;
    uint8_t devName[]   = APP_DEVICE_NAME_DEFAULT;
    uint8_t hrp_uuid[]= HRPS_SERVICE_UUID;

    /* Set advertising data as device name */
    app_adv_info.host.adv_data_len = 0;
    GAPM_AddAdvData(GAP_AD_TYPE_COMPLETE_NAME, devName,
                  sizeof(APP_DEVICE_NAME_DEFAULT)-1, app_adv_info.host.adv_data,
                  &amp;app_adv_info.host.adv_data_len);


    /* Set scan response data as heart rate UUID and company ID */
    app_adv_info.host.scan_rsp_data_len = 0;
    GAPM_AddAdvData(GAP_AD_TYPE_SERVICE_16_BIT_DATA, hrp_uuid,
                  sizeof(hrp_uuid), app_adv_info.host.scan_rsp_data,
                  &amp;app_adv_info.host.scan_rsp_data_len);
    GAPM_AddAdvData(GAP_AD_TYPE_MANU_SPECIFIC_DATA, companyID,
                  APP_COMPANY_ID_LEN, app_adv_info.host.scan_rsp_data,
                  &amp;app_adv_info.host.scan_rsp_data_len);
}</code></pre>

<p>用到的宏在app.h中定义(我将APP_DEVICE_NAME_DEFAULT修改了以方便观察)</p>

<pre>
<code class="language-cpp">/* Advertising data is composed by device name and company id */
#define APP_DEVICE_NAME_DEFAULT         "a0"
/* Maximum length for the device name.
* This is limited by GAP_MAX_NAME_SIZE during the stack build. */
#define APP_DEVICE_NAME_MAXLEN          0x20

/* Manufacturer info (ON SEMICONDUCTOR Company ID) */
#define APP_COMPANY_ID                  {0x62, 0x3}
#define APP_COMPANY_ID_LEN            2</code></pre>

<p>在ble_hrps.h中定义了</p>

<pre>
<code class="language-cpp">/** Heart rate UUID */
#define HRPS_SERVICE_UUID               {0x0D, 0x18}
</code></pre>

<p>具体实现在ble_gap.c中</p>

<pre>
<code class="language-cpp">/* ----------------------------------------------------------------------------
* Function      : bool GAPM_AddAdvData(enum gap_ad_type newDataFlag,
*                           uint8_t *newData, uint8_t newDataLen,
*                           uint8_t *resultAdvData, uint8_t* resultAdvDataLen)
* ----------------------------------------------------------------------------
* Description   : Utility function to add advertising data into the result
*               buffer.
*               function
* Inputs      : newDataFlag      - GAP Advertising flag (see
*                                    enum gap_ad_type)
*               newData          - Data to be added to resultAdvData
*               newDataLen       - Length of the data being added (excluding
*                                    flag)
* Outputs       : resultAdvData    - Resulting advertising data
*               resultAdvDataLen - Full advertising data length
*                                    (including flags and field length bytes)
* Assumptions   : resultAdvDataLen must be initialized to 0 before adding data
* ------------------------------------------------------------------------- */
bool GAPM_AddAdvData(enum gap_ad_type newDataFlag, uint8_t *newData,
                     uint8_t newDataLen, uint8_t *resultAdvData,
                     uint8_t* resultAdvDataLen)
{
    /* If new data element cannot be added to current accumulated data */
    if ((ADV_DATA_LEN - *resultAdvDataLen) &lt; (newDataLen + 2))
    {
      return false;
    }

    /* Else, add new data element into current accumulated data */
    resultAdvData[*resultAdvDataLen] = newDataLen + 1;
    resultAdvData[*resultAdvDataLen + 1] = newDataFlag;
    memcpy(resultAdvData + *resultAdvDataLen + 2, newData, newDataLen);

    *resultAdvDataLen += newDataLen + 2;
    return true;
}</code></pre>

<p>其中ADV_DATA_LEN定义在文件co_bt_defines.h中</p>

<pre>
<code class="language-cpp">#define ADV_DATA_LEN      0x1F</code></pre>

<p>0x1F也就是十进制的31。通过源码可以知道,GAPM_AddAdvData函数的作用就是将newData数据填充到resultAdvData,并按照格式<span style="background-color:#2ecc71;">长度:类型:数据</span>填充</p>

<p>广播类型定义在gap.h中</p>

<pre>
<code class="language-cpp">/*
* Enumerations
****************************************************************************************
*/
/// GAP Advertising Flags
enum gap_ad_type
{
    /// Flag
    GAP_AD_TYPE_FLAGS                      = 0x01,
    /// Use of more than 16 bits UUID
    GAP_AD_TYPE_MORE_16_BIT_UUID         = 0x02,
    /// Complete list of 16 bit UUID
    GAP_AD_TYPE_COMPLETE_LIST_16_BIT_UUID= 0x03,
    /// Use of more than 32 bit UUD
    GAP_AD_TYPE_MORE_32_BIT_UUID         = 0x04,
    /// Complete list of 32 bit UUID
    GAP_AD_TYPE_COMPLETE_LIST_32_BIT_UUID= 0x05,
    /// Use of more than 128 bit UUID
    GAP_AD_TYPE_MORE_128_BIT_UUID          = 0x06,
    /// Complete list of 128 bit UUID
    GAP_AD_TYPE_COMPLETE_LIST_128_BIT_UUID = 0x07,
    /// Shortened device name
    GAP_AD_TYPE_SHORTENED_NAME             = 0x08,
    /// Complete device name
    GAP_AD_TYPE_COMPLETE_NAME            = 0x09,
    /// Transmit power
    GAP_AD_TYPE_TRANSMIT_POWER             = 0x0A,
    /// Class of device
    GAP_AD_TYPE_CLASS_OF_DEVICE            = 0x0D,
    /// Simple Pairing Hash C
    GAP_AD_TYPE_SP_HASH_C                  = 0x0E,
    /// Simple Pairing Randomizer
    GAP_AD_TYPE_SP_RANDOMIZER_R            = 0x0F,
    /// Temporary key value
    GAP_AD_TYPE_TK_VALUE                   = 0x10,
    /// Out of Band Flag
    GAP_AD_TYPE_OOB_FLAGS                  = 0x11,
    /// Slave connection interval range
    GAP_AD_TYPE_SLAVE_CONN_INT_RANGE       = 0x12,
    /// Require 16 bit service UUID
    GAP_AD_TYPE_RQRD_16_BIT_SVC_UUID       = 0x14,
    /// Require 32 bit service UUID
    GAP_AD_TYPE_RQRD_32_BIT_SVC_UUID       = 0x1F,
    /// Require 128 bit service UUID
    GAP_AD_TYPE_RQRD_128_BIT_SVC_UUID      = 0x15,
    /// Service data 16-bit UUID
    GAP_AD_TYPE_SERVICE_16_BIT_DATA      = 0x16,
    /// Service data 32-bit UUID
    GAP_AD_TYPE_SERVICE_32_BIT_DATA      = 0x20,
    /// Service data 128-bit UUID
    GAP_AD_TYPE_SERVICE_128_BIT_DATA       = 0x21,
    /// Public Target Address
    GAP_AD_TYPE_PUB_TGT_ADDR               = 0x17,
    /// Random Target Address
    GAP_AD_TYPE_RAND_TGT_ADDR            = 0x18,
    /// Appearance
    GAP_AD_TYPE_APPEARANCE               = 0x19,
    /// Advertising Interval
    GAP_AD_TYPE_ADV_INTV                   = 0x1A,
    /// LE Bluetooth Device Address
    GAP_AD_TYPE_LE_BT_ADDR               = 0x1B,
    /// LE Role
    GAP_AD_TYPE_LE_ROLE                  = 0x1C,
    /// Simple Pairing Hash C-256
    GAP_AD_TYPE_SPAIR_HASH               = 0x1D,
    /// Simple Pairing Randomizer R-256
    GAP_AD_TYPE_SPAIR_RAND               = 0x1E,
    /// 3D Information Data
    GAP_AD_TYPE_3D_INFO                  = 0x3D,

    /// Manufacturer specific data
    GAP_AD_TYPE_MANU_SPECIFIC_DATA         = 0xFF,
};</code></pre>

<p>用户自定义的数据需要指定类型为GAP_AD_TYPE_MANU_SPECIFIC_DATA。输入参数是在app_config.c中定义的</p>

<pre>
<code class="language-cpp">/* ADV_DATA and SCAN_RSP data are set in APP_BLE_Initialize() */
union gapm_adv_info app_adv_info;</code></pre>

<p>跳转到gapm_task.h得到</p>

<pre>
<code class="language-cpp">/// Set advertising mode Command
struct gapm_start_advertise_cmd
{
    /// GAPM requested operation:
    /// - GAPM_ADV_NON_CONN: Start non connectable advertising
    /// - GAPM_ADV_UNDIRECT: Start undirected connectable advertising
    /// - GAPM_ADV_DIRECT: Start directed connectable advertising
    /// - GAPM_ADV_DIRECT_LDC: Start directed connectable advertising using Low Duty Cycle
    struct gapm_air_operation op;

    /// Minimum interval for advertising
    uint16_t             intv_min;
    /// Maximum interval for advertising
    uint16_t             intv_max;

    ///Advertising channel map
    uint8_t            channel_map;

    /// Advertising information
    union gapm_adv_info
    {
      /// Host information advertising data (GAPM_ADV_NON_CONN and GAPM_ADV_UNDIRECT)
      struct gapm_adv_host host;
      ///Direct address information (GAPM_ADV_DIRECT)
      /// (used only if reconnection address isn't set or privacy disabled)
      struct gap_bdaddr direct;
    } info;
};
</code></pre>

<p>其中结构体在该文件gapm_task.h中定义为</p>

<pre>
<code class="language-cpp">/// Advertising data that contains information set by host.
struct gapm_adv_host
{
    /// Advertising mode :
    /// - GAP_NON_DISCOVERABLE: Non discoverable mode
    /// - GAP_GEN_DISCOVERABLE: General discoverable mode
    /// - GAP_LIM_DISCOVERABLE: Limited discoverable mode
    /// - GAP_BROADCASTER_MODE: Broadcaster mode
    uint8_t            mode;

    /// Advertising filter policy:
    /// - ADV_ALLOW_SCAN_ANY_CON_ANY: Allow both scan and connection requests from anyone
    /// - ADV_ALLOW_SCAN_WLST_CON_ANY: Allow both scan req from White List devices only and
    ///   connection req from anyone
    /// - ADV_ALLOW_SCAN_ANY_CON_WLST: Allow both scan req from anyone and connection req
    ///   from White List devices only
    /// - ADV_ALLOW_SCAN_WLST_CON_WLST: Allow scan and connection requests from White List
    ///   devices only
    uint8_t            adv_filt_policy;

    /// Advertising data length - maximum 28 bytes, 3 bytes are reserved to set
    /// Advertising AD type flags, shall not be set in advertising data
    uint8_t            adv_data_len;
    /// Advertising data
    //To resolve issue CEVA-212
    //uint8_t            adv_data;
    uint8_t            adv_data;
    /// Scan response data length- maximum 31 bytes
    uint8_t            scan_rsp_data_len;
    /// Scan response data
    uint8_t            scan_rsp_data;
    /// Peer address
    struct gap_bdaddr peer_addr;
};</code></pre>

<p>其中宏在gap.h中定义为</p>

<pre>
<code class="language-cpp">/// ADV Data and Scan Response length
#define GAP_ADV_DATA_LEN      (0x1F)
#define GAP_SCAN_RSP_DATA_LEN (0x1F)</code></pre>

<p>也就是广播数据包载荷这里规定了最大长度为<span style="background-color:#e74c3c;">28字节,预留3字节。</span></p>

<pre>
<code class="language-cpp">void APP_SetAdvScanData(void)
{
    uint8_t companyID[] = APP_COMPANY_ID;
    uint8_t devName[]   = APP_DEVICE_NAME_DEFAULT;
    uint8_t hrp_uuid[]= HRPS_SERVICE_UUID;

    /* Set advertising data as device name */
    app_adv_info.host.adv_data_len = 0;
    GAPM_AddAdvData(GAP_AD_TYPE_COMPLETE_NAME, devName,sizeof(APP_DEVICE_NAME_DEFAULT)-1,
                    app_adv_info.host.adv_data,&amp;app_adv_info.host.adv_data_len);

    APP_FollowSetAdvScanData();


    /* Set scan response data as heart rate UUID and company ID */
    app_adv_info.host.scan_rsp_data_len = 0;
    GAPM_AddAdvData(GAP_AD_TYPE_SERVICE_16_BIT_DATA, hrp_uuid,sizeof(hrp_uuid), app_adv_info.host.scan_rsp_data,
                  &amp;app_adv_info.host.scan_rsp_data_len);
    GAPM_AddAdvData(GAP_AD_TYPE_MANU_SPECIFIC_DATA, companyID,
                  APP_COMPANY_ID_LEN, app_adv_info.host.scan_rsp_data,
                  &amp;app_adv_info.host.scan_rsp_data_len);
}</code></pre>

<p>这里我就直接拿来<a href="https://home.eeworld.com.cn/space-uid-799371.html" target="_blank">w494143467</a>大神分享的代码,我将数据增加到广播数据内容增加到22字节,这里需要注意的是app_adv_info.host.adv_data_len这个参数,它与前面填充的字节长度有关。</p>

<pre>
<code class="language-cpp">#define BROADCASTER_DATA_LEN         22
uint8_t Broadcaster_Data = {0x00,0x01, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x1a,0x2a,0x3a,0x4a,0x5a,0x6a};
void APP_FollowSetAdvScanData(void)
{
        app_adv_info.host.adv_data_len = 4;

    GAPM_AddAdvData(GAP_AD_TYPE_MANU_SPECIFIC_DATA, Broadcaster_Data,
                                    BROADCASTER_DATA_LEN, app_adv_info.host.adv_data,
                  &amp;app_adv_info.host.adv_data_len);

}</code></pre>

<p>烧录进去后,打开安卓蓝牙调试助手,可以扫描到设备了</p>

<p>广播包详细信息栏里的Len=Type(1字节)+Data长度</p>

<p></p>

<p>&nbsp;</p>

dql2016 发表于 2021-6-28 12:30

<p><strong><span style="color:#e74c3c;"><span style="font-size:24px;">蓝牙规范文件</span></span></strong></p>

<p></p>

<p></p>

<p></p>

<p>&nbsp;</p>

<p>&nbsp;</p>
页: [1]
查看完整版本: RSL10 BLE蓝牙广播