概述
iBeacon 是苹果公司推出的基于 BLE 广播的信标方案:设备只广播(间隔发送一小包数据,像大喇叭喊话)而不建立连接,手机经过附近时就能识别出它和它携带的信息(UUID、Major、Minor 等)。它常用于商场室内定位、门店推送、寻物标签等场景。本教程让 Ai-WB2 化身一个 iBeacon 信标,用手机 App 验证广播内容。
用大白话讲:iBeacon 就像路边广告牌。广告牌不和你说话、不和你握手,只是立在那里展示内容(「我是谁、我在哪、信号多强」),你路过时抬头看一眼就知道了。本教程就是让开发板变成这样一块「蓝牙广告牌」,手机 App 相当于路过的行人,走近就能看到牌子上的字(UUID 等数据)。
本教程基于安信可官方 SDK(Ai-Thinker-Open/Ai-Thinker-WB2,版本
release_bl_iot_sdk_1.6.40)的官方示例applications/bluetooth/ble_ibeacon编写,代码可在本地 SDK 中直接找到。
本教程直接使用官方 SDK 自带的 ble_ibeacon 示例工程,打开终端进入该工程目录:
cd ~/Ai-Thinker-WB2/applications/bluetooth/ble_ibeacon
说明:
cd是「进入目录」命令,这里进入 iBeacon 示例工程目录;后续的make编译、make flash烧录命令都必须先在这个目录里执行。
工程目录结构说明:
| 文件 | 作用 |
|---|---|
ble_ibeacon/main.c |
主程序源码(本工程只有这一个源文件),本教程主要看的文件 |
ble_ibeacon/bouffalo.mk |
工程编译配置,一般无需修改 |
打开 ble_ibeacon/main.c,找到 iBeacon 数据数组 my_ibeacon[],广播出去的内容就在这里(每个字节都是广告牌上的「字」):
char my_ibeacon[]=
{
0x4C, 0x00, //公司的标志 (0x004C == Apple)
0x02, 0x15, //iBeacon advertisement indicator
0xB9, 0x40, ... 0x6D, // iBeacon proximity uuid(16 字节)
0x00, 0x01, // major(主编号)
0x00, 0x01, // minor(次编号)
0xc5 //power(发射功率,用于估算距离)
};
| 字段 | 大白话 | 作用 |
|---|---|---|
公司标志 0x4C 0x00 |
厂家印章 | 声明这是苹果 iBeacon 格式 |
| UUID(16 字节) | 身份证号 | 标识这组信标属于谁,可自定义 |
| major / minor | 分组号/编号 | 同一 UUID 下再细分(如门店号、货架号) |
power 0xc5 |
音量档位 | 表示发射功率,手机据此估算距离 |
💡 示例 UUID 对应
B9407F30-F5F8-466E-AFF9-25556B57FE6D(一个通用的测试 UUID),你可以按自己的需求改这 16 个字节;设备名由宏IBEACON_NAME控制(默认"MY_IBEACON"),想改广播里显示的名字就改它。
官方示例代码无需修改即可运行,main.c 完整源码已移至文末,见:
📜 完整代码 — 位于本页「完整代码」章节,默认折叠,点击展开,与官方示例(
applications/bluetooth/ble_ibeacon/ble_ibeacon/main.c)完全一致。
代码要点:
| API | 作用 |
|---|---|
bl_sys_init() |
初始化系统时钟等基础资源,用 BLE 必须先调用它,漏掉直接死机 |
ble_controller_init(configMAX_PRIORITIES - 1) |
初始化 BLE 协议栈(蓝牙通信的大脑),不初始化后面蓝牙函数全部无效 |
hci_driver_init() |
初始化蓝牙硬件驱动,负责 CPU 和蓝牙射频「传话」,漏掉就无法广播 |
bt_enable(NULL) |
打开蓝牙协议栈,相当于手机「打开蓝牙开关」,不打开无法广播 |
bt_le_adv_start(¶m, ibeacon_data, ...) |
真正开始「大喇叭喊话」,喊的内容就是 ibeacon_data,不调用手机搜不到 |
bt_set_name(IBEACON_NAME) |
给设备起名(MY_IBEACON),手机搜索列表显示的就是它 |
在工程目录执行编译:
make -j8
说明:
make是「编译」命令,把代码变成开发板能运行的固件(烧进开发板的程序);-j8表示用 8 个 CPU 核并行编译,更快。
编译成功后生成固件 build_out/ble_ibeacon.bin。
⚠️ 若提示
riscv64-unknown-elf-gcc: command not found,说明工具链权限未配置,先执行cd toolchain/riscv/Linux && . chmod755.sh再重新编译。
开发板保持 USB 连接,确认串口设备号后执行烧录:
make flash p=/dev/ttyUSB0 b=921600
说明:
make flash是「烧录」命令,把编译好的固件写进开发板芯片。p=后面是串口设备号(要改成你电脑上实际的串口,可用ls /dev/ttyUSB*查看),b=921600是烧录波特率(串口传数据的速度),保持默认即可。
⏳ 烧录过程中按提示长按开发板 EN 键进入下载模式(部分开发板自动进入),等待进度条完成即烧录成功。
烧录完成后开发板自动重启运行。先看串口日志——注意!本示例代码把串口波特率设成了 115200(bl_uart_init(0, 16, 7, 255, 255, 115200)),串口助手要选 115200 而不是 921600:
AXK BLE IBEACON
ble_controller_init
hci_driver_init
bt_enable
再打开手机蓝牙调试 App(如 nRF Connect,Android/iOS 均可),扫描周围设备,应能看到名为 MY_IBEACON 的设备,或识别出 UUID B9407F30-F5F8-466E-AFF9-25556B57FE6D 的 iBeacon 信标。
串口打印出 AXK BLE IBEACON 且手机 App 能搜到 MY_IBEACON 即为成功;如果串口没输出,先检查波特率是否选成了 115200;如果手机搜不到 MY_IBEACON,见文末 FAQ。
💡 进阶验证:把手机放在开发板 0.5~5 米范围内走动,部分 iBeacon App 会显示距离估算(power 字段就是用来算这个的),说明广播数据被完整解析。
代码执行流程
例程从启动到运行的完整流程如下(图中的循环箭头表示反复执行):
本文 API 汇总
bl_uart_init(id, tx_pin, rx_pin, cts_pin, rts_pin, baudrate)
初始化串口(UART,电脑和开发板之间逐位传数据的通道),本例把日志串口设为 115200 波特率(传输速度)。
参数:
id:串口号,可选值:0/1,本示例用0tx_pin:发送引脚,本示例16rx_pin:接收引脚,本示例7cts_pin:流控引脚,不用传255rts_pin:流控引脚,不用传255baudrate:波特率(串口传数据的「语速」,两端必须一致),本示例115200
返回值:成功返回 0;失败返回负值错误码
ble_controller_init(task_priority)
初始化 BLE 协议栈(蓝牙通信的「大脑」),所有 BLE 功能的第一步。
参数:
task_priority:协议栈任务优先级(uint8_t),官方示例传configMAX_PRIORITIES - 1(系统最高优先级)
返回值:无
bt_enable(cb)
打开蓝牙协议栈,相当于手机「打开蓝牙开关」,不调用则无法广播/连接。
参数:
cb:协议栈就绪回调(bt_ready_cb_t),形如void cb(int err),不需要可传NULL
返回值:成功返回 0;失败返回负值错误码
bt_le_adv_start(param, ad, ad_len, sd, sd_len)
开始 BLE 广播(大喇叭喊话),广播数据由 ad 数组指定。
参数:
param:广播参数结构体指针,含广播间隔(interval_min/interval_max)、选项(可连接/带设备名)等ad:广播数据数组(bt_data_t),本示例为ibeacon_data(含 iBeacon 数据)ad_len:广播数据条数,本示例ARRAY_SIZE(ibeacon_data)(2 条)sd:扫描应答数据数组,不需要传NULLsd_len:扫描应答数据条数,传0
返回值:成功返回 0;失败返回负值错误码
xTaskCreate(task, name, stack, param, prio, handle)
创建任务并加入就绪队列,由调度器按优先级调度执行。
参数:
task:任务入口函数指针,形如void task(void *arg),必填name:任务名称字符串(调试用),本示例"ibeacon"stack:任务栈大小(单位:字),本示例1024param:传给入口函数的参数指针,无参传NULLprio:任务优先级,可选值:0(最低)~19(最高,SDK 配置),本示例15handle:任务句柄输出指针,不需要可传NULL
返回值:成功返回 pdPASS;失败返回 pdFAIL(如内存不足)
完整代码
以下为 ble_ibeacon/main.c 完整源码,与官方示例(applications/bluetooth/ble_ibeacon/ble_ibeacon/main.c)完全一致:
📜 点击展开 main.c 完整代码
/*
* Copyright (c) 2020 Bouffalolab.
*
* This file is part of
* *** Bouffalolab Software Dev Kit ***
* (see www.bouffalolab.com).
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of Bouffalo Lab nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <blog.h>
#include <stdio.h>
#include <cli.h>
#include <blog.h>
#include <bl_uart.h>
#include <bl_sys.h>
#include "hci_driver.h"
#include "ble_lib_api.h"
#include "bluetooth.h"
#include "gatt.h"
#include "uuid.h"
#include <hosal_uart.h>
#define PRIORITIE_OFFSET 4
/*set ibeacon name*/
#define IBEACON_NAME "MY_IBEACON"
/*ibeacon data*/
char my_ibeacon[]=
{
0x4C, 0x00, //公司的标志 (0x004C == Apple)
0x02, 0x15, //iBeacon advertisement indicator
0xB9, 0x40, 0x7F, 0x30, 0xF5, 0xF8, 0x46, 0x6E, 0xAF, 0xF9, 0x25, 0x55, 0x6B, 0x57, 0xFE, 0x6D, // iBeacon proximity uuid
0x00, 0x01, // major
0x00, 0x01, // minor
0xc5 //power
};
static struct bt_data ibeacon_data[2] =
{
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(BT_DATA_MANUFACTURER_DATA, my_ibeacon, sizeof(my_ibeacon)),//
};
/*start ble advertise*/
void ble_start_advertise(void)
{
struct bt_le_adv_param param;
param.id = BT_ID_DEFAULT;
param.interval_min = BT_GAP_ADV_FAST_INT_MIN_2;
param.interval_max = BT_GAP_ADV_FAST_INT_MAX_2;
//param.options = BT_LE_ADV_OPT_USE_NAME | BT_LE_ADV_OPT_ONE_TIME;
param.options = BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME | BT_LE_ADV_OPT_ONE_TIME;
/*Get mode, 0:General discoverable, 1:non discoverable, 2:limit discoverable*/
bt_le_adv_start(¶m,ibeacon_data, ARRAY_SIZE(ibeacon_data),NULL,0);
bt_set_name(IBEACON_NAME);
}
/*BLE ibeacon init*/
void ble_ibeacon_init(void)
{
printf("ble_controller_init\r\n");
ble_controller_init(configMAX_PRIORITIES - 1); //ble协议栈初始化
printf("hci_driver_init\r\n");
hci_driver_init();//初始化驱动
printf("bt_enable\r\n");
bt_enable(NULL);
ble_start_advertise();//开启广播
}
static void app_init_thread(void *param)
{
vTaskDelay(10 / portTICK_RATE_MS);
ble_ibeacon_init();
vTaskDelete(NULL);
}
static void app_init_entry(void)
{
if(xTaskCreate(app_init_thread, ((const char*)"app_init"), 1024*6, NULL, tskIDLE_PRIORITY + 3 + PRIORITIE_OFFSET, NULL) != pdPASS)
printf("\n\r%s xTaskCreate(init_thread) failed", __FUNCTION__);
}
static void ble_loop_proc(void *pvParameters)
{
app_init_entry();
vTaskDelete(NULL);
}
void main(void)
{
bl_uart_init(0, 16, 7, 255, 255, 115200);//set uart baud 115200
printf("AXK BLE IBEACON\r\n");//log
bl_sys_init(); //if use ble,must init
xTaskCreate(ble_loop_proc, (char*)"ibeacon", 1024, NULL, 15, NULL);
}常见问题与踩坑提示
⚠️ 手机搜不到 MY_IBEACON
原因:广播没启动、距离太远,或手机蓝牙/定位权限没开
解决:确认串口打印了 bt_enable 之后程序没卡死;Android 手机扫描 BLE 必须开启「定位权限」;手机靠近开发板(0.5~5 米)再扫;iBeacon 类 App 有缓存,先杀掉 App 重开
⚠️ 串口完全没有日志输出
原因:串口助手波特率选错——本例代码把串口设成了 115200,不是常用的 921600
解决:把串口助手波特率改成 115200 再重新打开;确认串口号选对(ls /dev/ttyUSB* 查看)
⚠️ 改了 UUID/名字,手机还是显示旧的
原因:修改后没有重新编译烧录,或手机 App 缓存了旧数据
解决:改完 my_ibeacon[]/IBEACON_NAME 后重新 make -j8 && make flash p=/dev/ttyUSB0 b=921600;手机端重启 App、关闭再打开手机蓝牙
⚠️ 烧录一直卡住等待,进度条不动
原因:未进入下载模式,或数据线只能充电不能传数据
解决:烧录时按提示长按 EN 键进入下载模式;换一根能传数据的 Type-C 数据线后重试
⚠️ 串口找不到设备 / 打不开
原因:USB 转串口驱动未装、权限不足,或设备号不对
解决:Linux 用 lsusb/dmesg 查看设备,权限不足执行 sudo usermod -aG dialout $USER 后重新登录;Windows 到设备管理器查看 COM 口
⚠️ 执行 make 报找不到 Makefile
原因:在错误的目录执行了编译命令
解决:先执行 cd ~/Ai-Thinker-WB2/applications/bluetooth/ble_ibeacon 进入工程目录,再执行 make -j8
运行自检
串口依次打印 AXK BLE IBEACON → ble_controller_init → hci_driver_init → bt_enable,且手机蓝牙 App 能搜到名为 MY_IBEACON 的设备(iBeacon UUID B9407F30-F5F8-466E-AFF9-25556B57FE6D),即广播功能验证通过。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

