nRF52832蓝牙iBeacon广播

时间:2024-02-23 12:33:44

开发环境

SDK版本:nRF5_SDK_15.0.0

芯片:nRF52832-QFAA

蓝牙iBeacon实现

iBeacon的核心就是广播,不需要进行连接,通过在广播包中插入信息然后广播出去。

广播数据包最多仅可以包含31字节数据,所以设计者必须慎重选择需要包含的数据。蓝牙SIG组织在Core Specification Supplement (CSS)文件中将这31个字节数据分成多个AD Type结构,每个AD Type都有相同的结构,分别为长度字节,类型字节以及数据域。
Beacon设备可以在一个或多个标准广播数据包中编码数据,传递信息。但是编码原理可能有所差异,即帧格式不同。目前主流的三种帧格式分别为苹果公司的iBeacon,Radius Networks公司的AltBeacon以及谷歌公司的Eddystone。
因此为了与不同的beacon设备进行交互,应用开发者在开发beacon应用时需要了解对应设备的帧格式。

iBeacon帧格式

AD Field Length 表示 advertisement data的长度,即广播包有用信息的总长度;

Type 广播类型

Company ID  厂商数据字段的数据域前2字节为公司识别码。由蓝牙SIG组织分配给各公司,指示后续数据的解码方式。在上图中,0x004C为苹果公司的ID。

0x02 指明该设备为“proximity beacon”,该值在iBeacon设备中均为0x02。

UUID指明拥有该beacon设备的机构。

Major 和 Minor 主次字段用来编码位置信息,通常主字段指明某个建筑,而次字段指明在这栋建筑中的特定位置。例如“伦敦中心商场,运动产品区”。

Tx Power 发送功率字段帮助应用进行距离估算。

有关iBeacon的详细内容可以参考Getting started with iBeacon

具体代码实现

#if (GNT_IBEACON_EN)
#define APP_BEACON_INFO_LENGTH          0x17                               /**< Total length of information advertised by the Beacon. */
#define APP_ADV_DATA_LENGTH             0x15                               /**< Length of manufacturer specific data in the advertisement. */
#define APP_DEVICE_TYPE                 0x02                               /**< 0x02 refers to Beacon. */
#define APP_MEASURED_RSSI               0xC3                               /**< The Beacon\'s measured RSSI at 1 meter distance in dBm. */
#define APP_COMPANY_IDENTIFIER          0x0059                             /**< Company identifier for Nordic Semiconductor ASA. as per www.bluetooth.org. */
#define APP_MAJOR_VALUE                 0x01, 0x02                         /**< Major value used to identify Beacons. */
#define APP_MINOR_VALUE                 0x03, 0x04                         /**< Minor value used to identify Beacons. */
#define APP_BEACON_UUID                 0x01, 0x12, 0x23, 0x34, \
                                        0x45, 0x56, 0x67, 0x78, \
                                        0x89, 0x9a, 0xab, 0xbc, \
                                        0xcd, 0xde, 0xef, 0xf0            /**< Proprietary UUID for Beacon. */

#define MAJ_VAL_OFFSET_IN_BEACON_INFO   18                                 /**< Position of the MSB of the Major Value in m_beacon_info array. */
#define UICR_ADDRESS                    0x10001080                         /**< Address of the UICR register used by this example. The major and minor versions to be encoded into the advertising data will be picked up from this location. */

static uint8_t m_beacon_info[APP_BEACON_INFO_LENGTH] =                    /**< Information advertised by the Beacon. */
{
    APP_DEVICE_TYPE,     // Manufacturer specific information. Specifies the device type in this
                         // implementation.
    APP_ADV_DATA_LENGTH, // Manufacturer specific information. Specifies the length of the
                         // manufacturer specific data in this implementation.
    APP_BEACON_UUID,     // 128 bit UUID value.
    APP_MAJOR_VALUE,     // Major arbitrary value that can be used to distinguish between Beacons.
    APP_MINOR_VALUE,     // Minor arbitrary value that can be used to distinguish between Beacons.
    APP_MEASURED_RSSI    // Manufacturer specific information. The Beacon\'s measured TX power in
                         // this implementation.
};
#endif

/**@brief Function for initializing the Advertising functionality. */
static void advertising_init(void)
{
    ret_code_t             err_code;
    ble_advertising_init_t init;

    memset(&init, 0, sizeof(init));

// 使用iBeacon广播,由于广播包长度需要控制好,部分信息可放置到广播应答包中    
#if (GNT_IBEACON_EN)
    ble_advdata_manuf_data_t manuf_specific_data;        //厂商自定义信息
    manuf_specific_data.company_identifier = APP_COMPANY_IDENTIFIER;    //厂商ID
    uint16_t major_value = (uint16_t)((UICR_ADDRESS & 0xFFFF0000) >> 16);
    uint16_t minor_value = (uint16_t)(UICR_ADDRESS & 0x0000FFFF);

    uint8_t index = MAJ_VAL_OFFSET_IN_BEACON_INFO;

    m_beacon_info[index++] = MSB_16(major_value);
    m_beacon_info[index++] = LSB_16(major_value);

    m_beacon_info[index++] = MSB_16(minor_value);
    m_beacon_info[index++] = LSB_16(minor_value);
    
    NRF_LOG_HEXDUMP_INFO(m_beacon_info, APP_BEACON_INFO_LENGTH);
    
    manuf_specific_data.data.p_data = (uint8_t *) m_beacon_info;
    manuf_specific_data.data.size   = APP_BEACON_INFO_LENGTH;
    init.advdata.p_manuf_specific_data     = &manuf_specific_data;
#endif
    //注意advdata长度不能超过 BLE_GAP_ADV_SET_DATA_SIZE_MAX (31)
    init.advdata.name_type               = BLE_ADVDATA_NO_NAME;       //广播iBeacon信息,长度有限 --广播时的名称显示类型(全名)BLE_ADVDATA_FULL_NAME
//    init.advdata.include_appearance      = true;                    //是否需要图标
    init.advdata.flags                   = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED;     //蓝牙设备普通发现模式 BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED   BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE
//    init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);    //广播UUID
//    init.advdata.uuids_complete.p_uuids  = m_adv_uuids;
//    init.advdata.p_tx_power_level        = &tx_power_level;       //设置发射功率
    
    init.srdata.name_type                 = BLE_ADVDATA_FULL_NAME;
    
    init.config.ble_adv_fast_enabled  = true;                       //快速广播使能
    init.config.ble_adv_fast_interval = APP_FAST_ADV_INTERVAL;      //快速广播间隔 30ms
    init.config.ble_adv_fast_timeout  = APP_FAST_ADV_DURATION;      //默认快速广播超时 20s
    
    init.config.ble_adv_slow_enabled  = true;                       //慢速广播使能
    init.config.ble_adv_slow_interval = APP_SLOW_ADV_INTERVAL;      //慢速广播间隔 200ms
//    init.config.ble_adv_slow_timeout  = APP_SLOW_ADV_DURATION;      //默认慢速广播超时 40s
    
    if(nrf_gpio_pin_read(USBVIN_CHECK) == 1)    //USB插入
    {
        gnt_led_indication_set(LED_USB_IN);
        g_gnt_info.gnt_usbvin_flag = 1;
        init.config.ble_adv_slow_timeout = 0;
    }
    else
    {
        gnt_led_indication_set(LED_USB_OUT);
        g_gnt_info.gnt_usbvin_flag = 0;
        init.config.ble_adv_slow_timeout  = APP_SLOW_ADV_DURATION;   //默认慢速广播超时 40s
    }

    init.evt_handler = on_adv_evt;

    err_code = ble_advertising_init(&m_advertising, &init);
    APP_ERROR_CHECK(err_code);

    ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}