第一个BLE项目:创建项目、配置prj.conf、理解main.c结构、编译与烧录

好,咱们正式开始动手了。

前面铺垫了那么多理论,说实话,我自己当年学蓝牙协议栈的时候,最烦的就是光看不练。所以这一章,咱们直接创建一个真正的BLE项目。你跟着我走一遍,跑通了,信心就有了。

1. 创建项目:从零开始搭架子

我个人习惯用 west 命令来管理Zephyr项目。你想想看,如果每次都要手动复制一堆模板文件,多累啊。

打开终端,先确认你的工作目录。我一般放在 ~/zephyr_workspace 下。然后执行:

west init -m https://github.com/zephyrproject-rtos/zephyr --mr v3.5.0 zephyr_project
cd zephyr_project
west update

嗯,这里要注意:west update 这一步会下载所有依赖模块,包括蓝牙协议栈的核心库。我第一次跑的时候,网络不好,卡了半小时。建议你找个网络稳定的环境。

项目初始化完成后,我们创建一个自己的应用目录:

mkdir -p apps/my_first_ble
cd apps/my_first_ble

在这个目录下,你需要创建三个文件:

  • prj.conf —— 项目配置文件
  • src/main.c —— 主程序文件
  • CMakeLists.txt —— 构建描述文件

说白了,这三个文件就是Zephyr项目的骨架。缺一个都跑不起来。

2. 配置prj.conf:打开蓝牙的开关

Zephyr的配置系统基于Kconfig。你不需要理解它的全部原理,只要知道:prj.conf 里写的每一行,都是在告诉编译器“我要用这个功能”。

对于最基础的BLE项目,我建议你这样配置:

# 启用蓝牙功能
CONFIG_BT=y

# 启用蓝牙中心角色(可以扫描和连接其他设备)
CONFIG_BT_CENTRAL=y

# 启用蓝牙外设角色(可以被其他设备连接)
CONFIG_BT_PERIPHERAL=y

# 设置蓝牙设备名称
CONFIG_BT_DEVICE_NAME="MyFirstBLE"

# 启用日志输出,方便调试
CONFIG_LOG=y
CONFIG_LOG_BACKEND_UART=y

我在项目中遇到过一个问题:有些初学者只开了 CONFIG_BT=y,结果发现设备能广播但连不上。为什么?因为没开 CONFIG_BT_PERIPHERAL。你想想看,广播只是喊一嗓子,真正要建立连接,必须把外设角色打开。

避坑指南: 我曾经在调试一个低功耗项目时,忘了加 CONFIG_BT_DEVICE_NAME,结果手机扫描到的设备名是乱码。后来查了半天才发现,默认名称是Zephyr的默认字符串,长度和编码都不对。所以,设备名一定要显式配置。

3. 理解main.c结构:BLE程序的骨架

好,现在来看核心代码。一个最简单的BLE程序,main.c 长这样:

#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>

void main(void)
{
    int err;

    printk("BLE示例程序启动\n");

    /* 初始化蓝牙协议栈 */
    err = bt_enable(NULL);
    if (err) {
        printk("蓝牙初始化失败 (err %d)\n", err);
        return;
    }

    printk("蓝牙初始化成功\n");

    /* 开始广播 */
    err = bt_le_adv_start(BT_LE_ADV_CONN, NULL, 0, NULL, 0);
    if (err) {
        printk("广播启动失败 (err %d)\n", err);
        return;
    }

    printk("广播已启动,设备名称: MyFirstBLE\n");

    /* 程序不能退出,否则蓝牙会停止 */
    while (1) {
        k_sleep(K_SECONDS(1));
    }
}

这段代码的结构其实很清晰,我拆开给你讲:

  • 头文件包含bluetooth.h 是蓝牙协议栈的主头文件,所有蓝牙API都在里面。
  • bt_enable(NULL):这是蓝牙协议栈的初始化函数。参数是回调函数指针,这里传NULL表示不需要回调通知。我建议你初期都传NULL,等后面需要处理连接事件时再改。
  • bt_le_adv_start():启动广播。第一个参数 BT_LE_ADV_CONN 表示可连接广播。说白了,就是告诉周围的设备:“我在这里,快来连我”。
  • while(1) 循环:这个很重要。main函数不能返回,否则整个程序就退出了,蓝牙也会停止。我见过有人忘了加这个循环,程序跑完就结束了,蓝牙根本没工作。
个人经验: 调试阶段,我习惯在 bt_enable() 前后加打印信息。如果蓝牙初始化失败,90%的原因是硬件问题——比如模块没上电、天线没接好。别急着怀疑代码,先检查硬件。

4. CMakeLists.txt:告诉编译器怎么编译

这个文件很简单,就几行:

cmake_minimum_required(VERSION 3.20)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my_first_ble)

target_sources(app PRIVATE src/main.c)

嗯,这里要注意:find_package(Zephyr REQUIRED) 这一行,必须确保环境变量 ZEPHYR_BASE 已经设置。如果你是用 west 初始化的项目,这个变量会自动设置。否则,你需要手动 source zephyr-env.sh

5. 编译与烧录:让代码跑起来

编译命令很简单:

cd ~/zephyr_project
west build -b nrf52840dk_nrf52840 apps/my_first_ble

这里 -b 参数指定开发板。我用的nRF52840 DK,你可以换成自己的板子,比如 nrf52dk_nrf52832qemu_cortex_m3(如果没有硬件,可以用QEMU模拟)。

编译成功后,烧录命令:

west flash

如果你用的是JLink调试器,它会自动识别。如果是其他调试器,可能需要加参数,比如 --runner pyocd

避坑指南: 我曾经在烧录时遇到“无法连接调试器”的错误。折腾了半天,发现是USB线的问题——有些USB线只能充电,不能传数据。所以,请务必用数据线,别用充电线。

6. 验证结果:用手机看看

烧录成功后,打开手机的蓝牙扫描。你应该能看到一个叫 MyFirstBLE 的设备。点击连接,如果一切正常,手机会显示“已连接”。

如果你没有手机,也可以用nRF Connect桌面版。我个人更喜欢用手机,方便。

如果扫描不到设备,检查以下几点:

  • 开发板是否上电?LED灯亮了吗?
  • 串口是否有打印信息?如果有“蓝牙初始化成功”和“广播已启动”,说明代码没问题。
  • 天线是否接好?nRF52840 DK有板载天线,但有些模块需要外接天线。
核心要点: 第一个BLE项目,目标不是做复杂功能,而是打通“创建-配置-编译-烧录-验证”这条链路。只要手机能扫描到你的设备,这节课就成功了。

好,这一章就到这里。下一章,我们会深入广播数据的结构,教你如何自定义广播包,让设备看起来更专业。