INGCHIPS 为客户提供易用的 SDK,帮助客户便捷、高效地开发蓝牙产品。
本教程演示如何通过INGChips SDK开发 iBeacon 设备和 iBeacon 扫描器。iBeacon 由苹果公司开发,2013 年发布。利用 iBeacon 可以进行室内精准导航、定位,精准消息推送等,用途广泛。
1、iBeacon 设备
着手开发之前,先从 App Store 下载一个 iBeacon app,比如Locate。Locate预置了一系列 UUID,其中有一个全 0 的(按协议规定,最终产品中 UUID 不得全部为 0)。下面我们就开发一个 UUID 全为 0 的 iBeacon 设备。
01、iBeacon 规范
iBeacon 广播数据包里包含两个项目:
Flags
值固定为0x06,也即置起了两个比特,LE General Discoverable Mode & BR/EDR Not Supported。
Manufacturer Specific Data
该项里的数据如下表所示:
02、创建 iBeacon 项目
开发 iBeacon 设备的方法与上一教程完成相同,整个开发过程不需要写一行代码,唯一的区别就在于按规范进行设置广播数据。INGChips SDK全面支持Keil、IAR、SEGGER集成开发环境及GNU Arm Toolchain。让我们来试试GNU Arm Toolchain。
03、编辑广播数据
用广播数据编辑器填加0x01 - «Flags»和0xFF - «Manufacturer Specific Data»两项。点击0x01 - «Flags»,勾选LE General Discoverable Mode和BR/EDR Not Supported。
04、编辑 iBeacon 厂家数据
点击0xFF - «Manufacturer Specific Data», 然后点击Edit as按钮从弹出的快捷菜单里选择iBeacon ...打开 iBeacon 数据编辑器。
UUID 全填为0,1m 处的信号功率可随意填写一个合理值,如 -50dBm,稍候我们将利用Locate app 校准信号功率。
05、试一试
完成项目向导里的其它各步骤一个可用GNU Arm Toolchain编译的项目就创建好了。
点击 iBeacon 项目打开控制台,输入make命令编译项目。回到ingWizard,使用跟上一教程中相同的步骤下载程序。打开Locate app 就能看到我们开发的 iBeacon 设备了。
06、在 Locate app 里查看 iBeacon
点选 iBeacon 设备,可以校准信号功率,也可以实时查看距离。
信号功率校准后,回到ingWizard,在项目上右键点击,选择Edit Data -> Advertising菜单调出广播数据编辑器,修改信号功率。在控制台输入make rebuild命令重新编译项目。重新下载程序,可以发现Locate app 里显示的距离精确了一些。
说明:按照规范,iBeacon 设备要使用不可连接非定向(non connectable undirected)广播包以 100ms 为周期发送信标信号。本教程不碰代码,广播采用默认参数发送。
2、iBeacon 扫描器
接下来再开发一个 iBeacon 扫描器。温馨提醒:要写代码了。
01、创建“iscanner” 项目
像往常一样,在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}