INGChips 为客户提供易用的 SDK,帮助客户便捷、高效地开发蓝牙产品。
本教材演示如何通过 INGChips SDK
开发 iBeacon 设备和 iBeacon 扫描器。iBeacon 由
苹果公司开发,2013 年发布。利用 iBeacon 可以进行室内精准导航、定位,精准消息推送等,用途广泛。
iBeacon 设备
着手开发之前,先从 App Store 下载一个 iBeacon app,比如 Locate
。 Locate
预置了
一系列 UUID,其中有一个全 0 的^[按协议规定,
最终产品中 UUID 不得全部为 0。]。下面我们就开发一个 UUID 全为 0 的 iBeacon 设备。
设置广播数据
iBeacon 广播数据包里包含两个项目:
-
Flags
值固定为
0x06
,也即置起了两个比特,LE General Discoverable Mode & BR/EDR Not Supported。 -
Manufacturer Specific Data
该项里的数据如下表所示。
Size in Bytes | Name | Value | Notes |
---|---|---|---|
2 | Company ID | 0x004C | 苹果公司 ID |
2 | Beacon Type | 0x1502 | 苹果定义的固定值 |
16 | Proximity UUID | 用户自定义的 UUID | |
2 | Major | 可理解使用在同一 UUID 下的组 ID | |
2 | Minor | 可理解为该设备在组内的 ID | |
1 | Measured Power | dBm | iPhone 5s 在距离设备 1m 测得的接收信号强度 |
开发 iBeacon 设备的方法与上一教程完成相同,整个开发过程不需要写一行代码,
唯一的区别就在于按规范进行设置广播数据。INGChips SDK
全面支持
Keil
、IAR
、SEGGER
集成开发环境及 GNU Arm Toolchain
。让我们来试试 GNU Arm Toolchain
。
用广播数据编辑器填加 0x01 - «Flags»
和 0xFF - «Manufacturer Specific Data»
两项。
点击 0x01 - «Flags»
,勾选 LE General Discoverable Mode
和 BR/EDR Not Supported
。
点击 0xFF - «Manufacturer Specific Data»
, 然后点击 Edit as
按钮从弹出的快捷菜单里选择
iBeacon ...
打开 iBeacon 数据编辑器。
UUID 全填为0,1m 处的信号功率可随意填写一个合理值,如 -50dBm,稍候我们将利用 Locate
app 校准信号功率。
试一试
完成项目向导里的其它各步骤一个可用 GNU Arm Toolchain
编译的项目就创建好了。
点击 iBeacon 项目打开控制台,输出 make
命令编译项目。
回到 ingWizard
,使用跟上一教程中相同的步骤下载程序。打开 Locate
app 就能看到我们开发的
iBeacon 设备了。
点选 iBeacon 设备,可以校准信号功率,也可以实时查看距离。
信号功率校准后,回到 ingWizard
,在项目上右键点击,选择 Edit Data
-> Advertising
菜单调出广播数据编辑器,修改信号功率。在控制台输入 make rebuild
命令重新编译项目。
重新下载程序,可以发现 Locate
app 里显示的距离精确了一些。
说明:按照规范,iBeacon 设备要使用不可连接非定向(non connectable undirected)广播包以 100ms 为周期发送 信标信号。本教程不碰代码,广播采用默认参数发送。
iBeacon 扫描器
接下来再开发一个 iBeacon 扫描器。温馨提醒:要写代码了。
像往常一样,在 ingWizard
里创建项目,这次试试 IAR Embedded Workbench
。
Role of Your Device
页里将设备角色设定成 Central,然后一路 Next
下去,iscanner
项目
就创建好了。
打开项目,在 profile.c
里找到函数 user_packet_handler
,可以看到一个
名为 GAP_EVENT_ADVERTISING_REPORT
的广播报告事件。每次扫描到广播时就会收到这个事件:
case GAP_EVENT_ADVERTISING_REPORT:
gap_get_advertisingReport(&report, packet);
// add your code
......
break;
收到广播报告后需要检查是否是合法的 iBeacon 广播。基于上一教程的讨论,iBeacon 的数据结构可以 很直接地写出来:
typedef __packed struct ibeacon_adv
{
uint16_t apple_id;
uint16_t id;
uint8_t uuid[16];
uint16_t major;
uint16_t minor;
int8_t ref_power;
} ibeacon_adv_t;
#define APPLE_COMPANY_ID 0x004C
#define IBEACON_ID 0x1502
扩展关键字 __packed
表示结构体内的各个域以 1 字节,ARM 和 IAR 的编译器皆支持。 如果是用 SEGGER
或者
GNU Arm Toolchain
,可以使用 #pragma pack
指令,或者 __attribute__ ((packed))
属性:
#pragma pack (push, 1)
typedef struct ibeacon_adv
{
...
} ibeacon_adv_t;
#pragma pack (pop)
先编写打印 UUID 的辅助函数热热身:
const char *format_uuid(char *buffer, uint8_t *uuid)
{
sprintf(buffer, "{ %02X%02X%02X%02X-%02X%02X-%02X%02X-"
"%02X%02X-%02X%02X%02X%02X%02X%02X }",
uuid[0], uuid[1], uuid[2], uuid[3],
uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9],
uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
return buffer;
}
距离估计
电磁波在自由空间中传输的损耗公式为:
\[Loss = 32.45 + 20log(d) + 20log(f)\]这里到辐射源的距离 $d$ 以 km 为单位,频率 $f$ 以 MHz 为单位,计算出的损耗 $Loss$ 用 dB 表示。
广播报告里包含接收信号强度指示 (RSSI)。假设开发板得到的 RSSI 跟 iPhone 5s 一致,
利用 RSSI 和 iBeacon 广播里携带的 ref_power
, 根据
损耗公式可直接估计距离如下:
double estimate_distance(int8_t ref_power, int8_t rssi)
{
return pow(10, (ref_power - rssi) / 20.0);
}
iBeacon 扫描器的完整代码如下:
uint8_t length;
ibeacon_adv_t *p_ibeacon;
char str_buffer[80];
......
case GAP_EVENT_ADVERTISING_REPORT:
gap_get_advertisingReport(&report, packet);
// 提取 Manufacturer Specific Data
p_ibeacon = (ibeacon_adv_t *)ad_data_from_type(report.length,
(uint8_t *)report.data, 0xff, &length);
// 判断是否为 iBeacon
if ((length != sizeof(ibeacon_adv_t))
|| (p_ibeacon->apple_id != APPLE_COMPANY_ID)
|| (p_ibeacon->id != IBEACON_ID))
break;
// 计算并打输出
printf("%s %04X,%04X, %.1fm\n",
format_uuid(str_buffer, p_ibeacon->uuid),
p_ibeacon->major, p_ibeacon->minor,
estimate_distance(p_ibeacon->ref_power, report.rssi));
break;
按下 F7
编译,然后下载。利用 Locate
app 或者用另一块开发板发送 iBeacon 信号,iBeacon 扫描器立即就能扫描到: