目录
一、项目介绍
主机扫描周围的从机并建立连接,当从机建立连接之后,开始获取发送指定广播数据设备的RSSI,并通过NUS发送给主机。

二、主机代码
主要的逻辑实现为处理从机的连接句柄与NUS句柄。
2.1 多连接实现
2.1.1 写入同步与多连接参数配置
//定义最大可连接数
#define MAX_CONN 4
//向从机写入数据信号量,确保每次仅在收到 sent() 回调后再写下一帧数据
K_SEM_DEFINE(nus_write_sem, 0, MAX_CONN);
//为每个连接分配一个连接句柄
static struct bt_conn *conns[MAX_CONN]; /* 当前活跃的单个连接引用 */
2.1.2 查找、分配连接句柄
由于断开连接时,会将断开的句柄清除,于是当检索到句柄为NULL时,此句柄为新句柄。并且在连接数小于最大值时,开启扫描。
/* 查找句柄对应的索引,返回句柄索引*/
static int conn_get_index(struct bt_conn *conn)
{
for (int i = 0; i < MAX_CONN; i++)
{
// 查找是否已有句柄
if (conns[i] == conn)
{
return i;
}
// 若为新句柄则保存此句柄
if (conns[i] == NULL)
{
printk("conn_get_index\n");
conns[i] = conn;
bt_conn_ref(conns[i]);
// 连接数小于4时,开启扫描
if (i < 3)
{
(void)k_work_submit(&scan_work); /* 重新扫描 */
}
else
{
int err = bt_scan_stop();
if (err)
{
LOG_ERR("Stop LE scan failed (err %d)\n", err);
}
}
return i;
}
}
return -1;
}
2.1.3 连接、断开连接以及连接安全等级变更回调函数
/* ──────────────────────────────────────────────────────────────────────────── */
/* 连接、断开、加密状态回调 */
/* ──────────────────────────────────────────────────────────────────────────── */
static void connected(struct bt_conn *conn, uint8_t conn_err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
// 连接成功,分配句柄
if (conn_err)
{
printk("Failed to connect to %s, 0x%02x %s\n", addr, conn_err, bt_hci_err_to_str(conn_err));
return;
}
else
{
int connect_num = conn_get_index(conn);
if (connect_num < 0)
{
printk("Error:the number of connected device larger than MAX_CONN\n");
return;
}
}
printk("Connected: %s\n", addr);
/* 协商 ATT MTU */
static struct bt_gatt_exchange_params exchange_params = {.func = exchange_func};
if (bt_gatt_exchange_mtu(conn, &exchange_params))
{
printk("MTU exchange failed");
}
/* 尝试升级安全等级到 L2 (加密) */
if (bt_conn_set_security(conn, BT_SECURITY_L2))
{
printk("Failed to set security, fallback to discovery without encryption\n");
gatt_discover(conn);
}
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Disconnected: %s, reason 0x%02x %s\n", addr, reason, bt_hci_err_to_str(reason));
for (int i = 0; i < MAX_CONN; i++)
{
if (conns[i] == conn)
{
bt_conn_unref(conns[i]);
conns[i] = NULL;
}
}
(void)k_work_submit(&scan_work); /* 自动重连 */
}
static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (!err)
{
printk("Security changed: %s level %u\n", addr, level);
}
else
{
printk("Security failed: %s level %u err %d %s\n", addr, level, err, bt_security_err_to_str(err));
}
gatt_discover(conn); /* 加密成功/失败都继续做服务发现 */
}
/* 将上面的回调注册到全局 */
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
.security_changed = security_changed,
};
2.1.4 NUS
根据NUS句柄查找连接句柄
用于在NUS处理时判断来自于哪个从机
static struct bt_conn *conn_by_nus(const struct bt_nus_client *nus)
{
for (int i = 0; i < MAX_CONN; i++)
{
if (&nus_client[i] == nus)
{ /* 指针比较即可 */
return conns[i]; /* 可能为 NULL → 还没连上或已断开 */
}
}
return NULL;
}
数据发送回调
/**
* @brief NUS 写完成回调 (Central 作为 Client 往 Peripheral TX Characteristic 写)
*/
static void ble_data_sent(struct bt_nus_client *nus, uint8_t err,
const uint8_t *const data, uint16_t len)
{
ARG_UNUSED(nus);
ARG_UNUSED(data);
ARG_UNUSED(len);
/* 释放信号量,允许主线程继续发送下一包 */
k_sem_give(&nus_write_sem);
if (err)
{
printk("ATT error code: 0x%02X\n", err);
}
}
数据接收回调函数
用于接收从机发送的RSSI并打印
/**
* @brief NUS 接收回调 (Peripheral 通过 Notify 把数据发给 Central)
*
* 把 BLE 侧收到的字节流分片后透传到本地 UART。
* 注意:这里的实现一次最多写 19 字节,剩 1 字节给可能追加的 '\n'。
*/
static uint8_t ble_data_received(struct bt_nus_client *nus,
const uint8_t *data, uint16_t len)
{
ARG_UNUSED(nus);
int err;
struct bt_conn *conn = conn_by_nus(nus);
char addr[BT_ADDR_LE_STR_LEN] = {0};
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Received from %s:", addr);
for (uint16_t pos = 0; pos < len;)
{
/* 动态申请一个 UART 发送缓冲区 */
struct uart_data_t *tx = k_malloc(sizeof(*tx));
if (!tx)
{
printk("Not able to allocate UART send data buffer");
return BT_GATT_ITER_CONTINUE; /* 低内存时保持订阅,不中断 GATT */
}
/* 预留 1 字节给可能追加的 LF */
size_t tx_data_size = sizeof(tx->data) - 1;
/* 计算本次应复制的字节数 */
tx->len = ((len - pos) > tx_data_size) ? tx_data_size : (len - pos);
memcpy(tx->data, &data[pos], tx->len);
pos += tx->len;
/* 如果当前 BLE 包最后一个字节是 CR,则追加 LF,方便串口终端换行 */
if ((pos == len) && (data[len - 1] == '\r'))
{
tx->data[tx->len] = '\n';
tx->len++;
}
/* 非阻塞发送到 UART,若返回 -EBUSY 则放入待发队列 */
err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
if (err)
{
k_fifo_put(&fifo_uart_tx_data, tx);
}
}
printk("\n");
return BT_GATT_ITER_CONTINUE;
}
GATT服务发现
用于获取连接设备的NUS服务,并分配NUS句柄,便于双发进行数据交换
/* GATT DM 回调结构体 */
static struct bt_gatt_dm_cb discovery_cb = {
.completed = discovery_complete,
.service_not_found = discovery_service_not_found,
.error_found = discovery_error,
};
/* 主动启动 GATT Service 发现 */
static void gatt_discover(struct bt_conn *conn)
{
int err;
int connect_num = conn_get_index(conn);
if (connect_num < 0)
{
LOG_INF("Error:the number of connected device larger than MAX_CONN\n");
}
err = bt_gatt_dm_start(conn, BT_UUID_NUS_SERVICE, &discovery_cb, &nus_client[connect_num]);
if (err)
{
LOG_INF("Could not start discovery (err %d)\n", err);
}
}
2.1.5 UART
从设备树上获取UART句柄
static const struct device *uart = DEVICE_DT_GET(DT_CHOSEN(nordic_nus_uart));
数据缓冲区结构体
/* UART 数据缓冲区结构体 —— 通过 k_malloc() 动态申请 */
struct uart_data_t
{
void *fifo_reserved; /* k_fifo 头部保留字段 (Zephyr 内部使用) */
uint8_t data[UART_BUF_SIZE]; /* 实际数据区 */
uint16_t len; /* 当前已写入长度 */
};
/* FIFO:用于缓存待发送/待处理的数据缓冲区指针 */
static K_FIFO_DEFINE(fifo_uart_tx_data); /* 串口 TX 等待队列 */
static K_FIFO_DEFINE(fifo_uart_rx_data); /* 串口 RX 就绪队列 */
UART回调
/* ──────────────────────────────────────────────────────────────────────────── */
/* UART 异步回调 —— 处理 TX/RX 状态机 */
/* ──────────────────────────────────────────────────────────────────────────── */
static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
ARG_UNUSED(dev);
/* 下面这几个静态变量在多次回调间保持状态 */
static size_t aborted_len; /* 累计已中止但尚未重发的字节数 */
static uint8_t *aborted_buf; /* 指向被中止的缓冲区 */
static bool disable_req; /* 表示已请求暂停 RX,等待 RX_DISABLED */
struct uart_data_t *buf;
switch (evt->type)
{
/* -------------------------- 发送完成 --------------------------- */
case UART_TX_DONE:
LOG_DBG("UART_TX_DONE");
/* 保护:过滤空事件 */
if ((evt->data.tx.len == 0) || (!evt->data.tx.buf))
{
return;
}
/* 判断本次回调是否来自“重发”场景 */
if (aborted_buf)
{
buf = CONTAINER_OF(aborted_buf, struct uart_data_t, data[0]);
aborted_buf = NULL;
aborted_len = 0;
}
else
{
buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t, data[0]);
}
k_free(buf); /* 释放已发送缓冲区 */
/* 检查队列里是否还有待发送的帧 */
buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT);
if (!buf)
{
return; /* 队列空,直接返回 */
}
if (uart_tx(uart, buf->data, buf->len, SYS_FOREVER_MS))
{
LOG_INF("Failed to send data over UART");
}
break;
/* -------------------------- 接收到数据 ------------------------- */
case UART_RX_RDY:
LOG_DBG("UART_RX_RDY");
buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data[0]);
buf->len += evt->data.rx.len; /* 累加实际长度 */
/* 若上层已请求暂停,则直接返回 */
if (disable_req)
{
return;
}
/* 行尾检测:遇到 CR/LF 立即停止 RX,让上层处理一整行 */
if ((evt->data.rx.buf[buf->len - 1] == '\n') ||
(evt->data.rx.buf[buf->len - 1] == '\r'))
{
disable_req = true;
uart_rx_disable(uart);
}
break;
/* -------------------------- RX 已停 --------------------------- */
case UART_RX_DISABLED:
LOG_DBG("UART_RX_DISABLED");
disable_req = false; /* 清除标志,准备重启接收 */
buf = k_malloc(sizeof(*buf));
if (buf)
{
buf->len = 0;
}
else
{
LOG_INF("Not able to allocate UART receive buffer");
/* 延时后再次尝试 enable RX */
k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
return;
}
uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_RX_TIMEOUT);
break;
/* -------------------- DMA 申请新缓冲区 ------------------------- */
case UART_RX_BUF_REQUEST:
LOG_DBG("UART_RX_BUF_REQUEST");
buf = k_malloc(sizeof(*buf));
if (buf)
{
buf->len = 0;
uart_rx_buf_rsp(uart, buf->data, sizeof(buf->data));
}
else
{
LOG_INF("Not able to allocate UART receive buffer");
}
break;
/* ------------------ DMA 缓冲区已释放 (满) ---------------------- */
case UART_RX_BUF_RELEASED:
LOG_DBG("UART_RX_BUF_RELEASED");
buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t, data[0]);
if (buf->len > 0)
{
k_fifo_put(&fifo_uart_rx_data, buf); /* 推到主线程处理 */
}
else
{
k_free(buf); /* 空包直接丢弃 */
}
break;
/* -------------------------- 发送被中止 ------------------------- */
case UART_TX_ABORTED:
LOG_DBG("UART_TX_ABORTED");
if (!aborted_buf)
{
aborted_buf = (uint8_t *)evt->data.tx.buf;
}
aborted_len += evt->data.tx.len; /* 记录已传输长度,稍后续传 */
buf = CONTAINER_OF(aborted_buf, struct uart_data_t, data[0]);
uart_tx(uart, &buf->data[aborted_len],
buf->len - aborted_len, SYS_FOREVER_MS);
break;
default:
break;
}
}
UART初始化
/* ──────────────────────────────────────────────────────────────────────────── */
/* UART 初始化 —— 注册回调 & 启动异步 RX */
/* ──────────────────────────────────────────────────────────────────────────── */
static int uart_init(void)
{
int err;
struct uart_data_t *rx;
if (!device_is_ready(uart))
{
LOG_ERR("UART device not ready");
return -ENODEV;
}
rx = k_malloc(sizeof(*rx));
if (rx)
{
rx->len = 0;
}
else
{
return -ENOMEM;
}
k_work_init_delayable(&uart_work, uart_work_handler);
err = uart_callback_set(uart, uart_cb, NULL);
if (err)
{
return err;
}
/* 开启第一个 DMA RX 缓冲区 */
return uart_rx_enable(uart, rx->data, sizeof(rx->data), UART_RX_TIMEOUT);
}
2.1.6 扫描从机
只有当从机广播并且主机扫描到才会触发连接
扫描回调函数
static void scan_filter_match(struct bt_scan_device_info *device_info,
struct bt_scan_filter_match *filter_match,
bool connectable)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr));
LOG_INF("Filters matched. Address: %s connectable: %d\n", addr, connectable);
}
static void scan_connecting_error(struct bt_scan_device_info *device_info)
{
LOG_INF("Connecting failed\n");
}
static void scan_connecting(struct bt_scan_device_info *device_info, struct bt_conn *conn)
{
LOG_INF("Try Connecting\n");
}
扫描类型回调函数注册
/* 扫描模块全局回调注册宏 */
BT_SCAN_CB_INIT(scan_cb, scan_filter_match, NULL, scan_connecting_error, scan_connecting);
2.2 完整主机代码
/* ──────────────────────────────────────────────────────────────────────────── */
/* 头文件区域 —— 依赖关系由上到下逐层展开 */
/* ──────────────────────────────────────────────────────────────────────────── */
#include <errno.h> /* 标准错误码 */
#include <zephyr/kernel.h> /* Zephyr 内核:线程、同步原语、k_fifo 等 */
#include <zephyr/device.h> /* Zephyr 设备模型 */
#include <zephyr/devicetree.h> /* 设备树解析宏 */
#include <zephyr/sys/byteorder.h> /* 字节序辅助函数 */
#include <zephyr/sys/printk.h> /* printk() 调试输出 */
/* 蓝牙协议栈相关头文件 */
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
/* Nordic 专用 Service & Client、GATT Discovery Manager、扫描模块 */
#include <bluetooth/services/nus.h>
#include <bluetooth/services/nus_client.h>
#include <bluetooth/gatt_dm.h>
#include <bluetooth/scan.h>
#include <zephyr/settings/settings.h> /* NVS/Settings:用来持久化绑定信息 */
#include <zephyr/drivers/uart.h> /* 异步 UART 驱动 */
#include <zephyr/logging/log.h> /* 模块化日志 */
/* ──────────────────────────────────────────────────────────────────────────── */
/* 日志模块初始化 */
/* ──────────────────────────────────────────────────────────────────────────── */
#define LOG_MODULE_NAME central_uart /* 日志前缀 */
LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_INF); // 可选:LOG_LEVEL_INF
/* ──────────────────────────────────────────────────────────────────────────── */
/* 配置宏 —— 根据实际场景可在 prj.conf/dts 中调整 */
/* ──────────────────────────────────────────────────────────────────────────── */
#define UART_BUF_SIZE 20 /* 每个 UART 缓冲区大小:默认与 ATT MTU 20 B 对齐 */
#define MAX_CONN 4
#define KEY_PASSKEY_ACCEPT DK_BTN1_MSK /* DK 板上 BTN1:配对时接受 passkey */
#define KEY_PASSKEY_REJECT DK_BTN2_MSK /* DK 板上 BTN2:拒绝配对 */
#define NUS_WRITE_TIMEOUT K_MSEC(150) /* 等待 NUS 写完成回调的最长时间 */
#define UART_WAIT_FOR_BUF_DELAY K_MSEC(50) /* 申请 RX 缓冲失败后的重试延时 */
#define UART_RX_TIMEOUT 50000 /* UART DMA Idle 超时 (µs) */
/* ──────────────────────────────────────────────────────────────────────────── */
/* 全局/静态对象声明 */
/* ──────────────────────────────────────────────────────────────────────────── */
/* 通过设备树 chosen 节点拿到用于 NUS 的 UART 设备句柄 */
static const struct device *uart = DEVICE_DT_GET(DT_CHOSEN(nordic_nus_uart));
/* 延时工作:当 RX 缓冲申请失败时用于稍后重建接收链 */
static struct k_work_delayable uart_work;
/* 工作项:掉线后重新启动扫描 */
static struct k_work scan_work;
/* 信号量:确保每次仅在收到 sent() 回调后再写下一帧数据 */
K_SEM_DEFINE(nus_write_sem, 0, MAX_CONN);
/* UART 数据缓冲区结构体 —— 通过 k_malloc() 动态申请 */
struct uart_data_t
{
void *fifo_reserved; /* k_fifo 头部保留字段 (Zephyr 内部使用) */
uint8_t data[UART_BUF_SIZE]; /* 实际数据区 */
uint16_t len; /* 当前已写入长度 */
};
/* FIFO:用于缓存待发送/待处理的数据缓冲区指针 */
static K_FIFO_DEFINE(fifo_uart_tx_data); /* 串口 TX 等待队列 */
static K_FIFO_DEFINE(fifo_uart_rx_data); /* 串口 RX 就绪队列 */
/* BLE 连接与 NUS Client 对象 */
static struct bt_conn *conns[MAX_CONN]; /* 当前活跃的单个连接引用 */
static struct bt_nus_client nus_client[MAX_CONN]; /* NUS Client 实例 (包含句柄、CCC 等) */
/* 查找句柄对应的索引,返回句柄索引*/
static int conn_get_index(struct bt_conn *conn)
{
for (int i = 0; i < MAX_CONN; i++)
{
// 查找是否已有句柄
if (conns[i] == conn)
{
return i;
}
// 若为新句柄则保存此句柄
if (conns[i] == NULL)
{
LOG_INF("conn_get_index\n");
conns[i] = conn;
bt_conn_ref(conns[i]);
// 连接数小于4时,开启扫描
if (i < 3)
{
(void)k_work_submit(&scan_work); /* 重新扫描 */
}
else
{
int err = bt_scan_stop();
if (err)
{
LOG_ERR("Stop LE scan failed (err %d)\n", err);
}
}
return i;
}
}
return -1;
}
/* ──────────────────────────────────────────────────────────────────────────── */
/* NUS 回调 —— BLE ↔ UART 数据路径 */
/* ──────────────────────────────────────────────────────────────────────────── */
/**
* @brief NUS 写完成回调 (Central 作为 Client 往 Peripheral TX Characteristic 写)
*/
static void ble_data_sent(struct bt_nus_client *nus, uint8_t err,
const uint8_t *const data, uint16_t len)
{
ARG_UNUSED(nus);
ARG_UNUSED(data);
ARG_UNUSED(len);
/* 释放信号量,允许主线程继续发送下一包 */
k_sem_give(&nus_write_sem);
if (err)
{
LOG_INF("ATT error code: 0x%02X\n", err);
}
}
static struct bt_conn *conn_by_nus(const struct bt_nus_client *nus)
{
for (int i = 0; i < MAX_CONN; i++)
{
if (&nus_client[i] == nus)
{ /* 指针比较即可 */
return conns[i]; /* 可能为 NULL → 还没连上或已断开 */
}
}
return NULL;
}
/**
* @brief NUS 接收回调 (Peripheral 通过 Notify 把数据发给 Central)
*
* 把 BLE 侧收到的字节流分片后透传到本地 UART。
* 注意:这里的实现一次最多写 19 字节,剩 1 字节给可能追加的 '\n'。
*/
static uint8_t ble_data_received(struct bt_nus_client *nus,
const uint8_t *data, uint16_t len)
{
ARG_UNUSED(nus);
int err;
struct bt_conn *conn = conn_by_nus(nus);
char addr[BT_ADDR_LE_STR_LEN] = {0};
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Received from %s:", addr);
for (uint16_t pos = 0; pos < len;)
{
/* 动态申请一个 UART 发送缓冲区 */
struct uart_data_t *tx = k_malloc(sizeof(*tx));
if (!tx)
{
LOG_INF("Not able to allocate UART send data buffer");
return BT_GATT_ITER_CONTINUE; /* 低内存时保持订阅,不中断 GATT */
}
/* 预留 1 字节给可能追加的 LF */
size_t tx_data_size = sizeof(tx->data) - 1;
/* 计算本次应复制的字节数 */
tx->len = ((len - pos) > tx_data_size) ? tx_data_size : (len - pos);
memcpy(tx->data, &data[pos], tx->len);
pos += tx->len;
/* 如果当前 BLE 包最后一个字节是 CR,则追加 LF,方便串口终端换行 */
if ((pos == len) && (data[len - 1] == '\r'))
{
tx->data[tx->len] = '\n';
tx->len++;
}
/* 非阻塞发送到 UART,若返回 -EBUSY 则放入待发队列 */
err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
if (err)
{
k_fifo_put(&fifo_uart_tx_data, tx);
}
}
LOG_INF("\n");
return BT_GATT_ITER_CONTINUE;
}
/* ──────────────────────────────────────────────────────────────────────────── */
/* UART 异步回调 —— 处理 TX/RX 状态机 */
/* ──────────────────────────────────────────────────────────────────────────── */
static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
ARG_UNUSED(dev);
/* 下面这几个静态变量在多次回调间保持状态 */
static size_t aborted_len; /* 累计已中止但尚未重发的字节数 */
static uint8_t *aborted_buf; /* 指向被中止的缓冲区 */
static bool disable_req; /* 表示已请求暂停 RX,等待 RX_DISABLED */
struct uart_data_t *buf;
switch (evt->type)
{
/* -------------------------- 发送完成 --------------------------- */
case UART_TX_DONE:
LOG_DBG("UART_TX_DONE");
/* 保护:过滤空事件 */
if ((evt->data.tx.len == 0) || (!evt->data.tx.buf))
{
return;
}
/* 判断本次回调是否来自“重发”场景 */
if (aborted_buf)
{
buf = CONTAINER_OF(aborted_buf, struct uart_data_t, data[0]);
aborted_buf = NULL;
aborted_len = 0;
}
else
{
buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t, data[0]);
}
k_free(buf); /* 释放已发送缓冲区 */
/* 检查队列里是否还有待发送的帧 */
buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT);
if (!buf)
{
return; /* 队列空,直接返回 */
}
if (uart_tx(uart, buf->data, buf->len, SYS_FOREVER_MS))
{
LOG_INF("Failed to send data over UART");
}
break;
/* -------------------------- 接收到数据 ------------------------- */
case UART_RX_RDY:
LOG_DBG("UART_RX_RDY");
buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data[0]);
buf->len += evt->data.rx.len; /* 累加实际长度 */
/* 若上层已请求暂停,则直接返回 */
if (disable_req)
{
return;
}
/* 行尾检测:遇到 CR/LF 立即停止 RX,让上层处理一整行 */
if ((evt->data.rx.buf[buf->len - 1] == '\n') ||
(evt->data.rx.buf[buf->len - 1] == '\r'))
{
disable_req = true;
uart_rx_disable(uart);
}
break;
/* -------------------------- RX 已停 --------------------------- */
case UART_RX_DISABLED:
LOG_DBG("UART_RX_DISABLED");
disable_req = false; /* 清除标志,准备重启接收 */
buf = k_malloc(sizeof(*buf));
if (buf)
{
buf->len = 0;
}
else
{
LOG_INF("Not able to allocate UART receive buffer");
/* 延时后再次尝试 enable RX */
k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
return;
}
uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_RX_TIMEOUT);
break;
/* -------------------- DMA 申请新缓冲区 ------------------------- */
case UART_RX_BUF_REQUEST:
LOG_DBG("UART_RX_BUF_REQUEST");
buf = k_malloc(sizeof(*buf));
if (buf)
{
buf->len = 0;
uart_rx_buf_rsp(uart, buf->data, sizeof(buf->data));
}
else
{
LOG_INF("Not able to allocate UART receive buffer");
}
break;
/* ------------------ DMA 缓冲区已释放 (满) ---------------------- */
case UART_RX_BUF_RELEASED:
LOG_DBG("UART_RX_BUF_RELEASED");
buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t, data[0]);
if (buf->len > 0)
{
k_fifo_put(&fifo_uart_rx_data, buf); /* 推到主线程处理 */
}
else
{
k_free(buf); /* 空包直接丢弃 */
}
break;
/* -------------------------- 发送被中止 ------------------------- */
case UART_TX_ABORTED:
LOG_DBG("UART_TX_ABORTED");
if (!aborted_buf)
{
aborted_buf = (uint8_t *)evt->data.tx.buf;
}
aborted_len += evt->data.tx.len; /* 记录已传输长度,稍后续传 */
buf = CONTAINER_OF(aborted_buf, struct uart_data_t, data[0]);
uart_tx(uart, &buf->data[aborted_len],
buf->len - aborted_len, SYS_FOREVER_MS);
break;
default:
break;
}
}
/* ──────────────────────────────────────────────────────────────────────────── */
/* UART 接收缓冲失败后的延时工作处理 */
/* ──────────────────────────────────────────────────────────────────────────── */
static void uart_work_handler(struct k_work *item)
{
ARG_UNUSED(item);
struct uart_data_t *buf = k_malloc(sizeof(*buf));
if (buf)
{
buf->len = 0;
}
else
{
LOG_INF("Not able to allocate UART receive buffer");
k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
return;
}
uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_RX_TIMEOUT);
}
/* ──────────────────────────────────────────────────────────────────────────── */
/* UART 初始化 —— 注册回调 & 启动异步 RX */
/* ──────────────────────────────────────────────────────────────────────────── */
static int uart_init(void)
{
int err;
struct uart_data_t *rx;
if (!device_is_ready(uart))
{
LOG_ERR("UART device not ready");
return -ENODEV;
}
rx = k_malloc(sizeof(*rx));
if (rx)
{
rx->len = 0;
}
else
{
return -ENOMEM;
}
k_work_init_delayable(&uart_work, uart_work_handler);
err = uart_callback_set(uart, uart_cb, NULL);
if (err)
{
return err;
}
/* 开启第一个 DMA RX 缓冲区 */
return uart_rx_enable(uart, rx->data, sizeof(rx->data), UART_RX_TIMEOUT);
}
/* ──────────────────────────────────────────────────────────────────────────── */
/* GATT Discovery Manager 回调 */
/* ──────────────────────────────────────────────────────────────────────────── */
static void discovery_complete(struct bt_gatt_dm *dm, void *context)
{
struct bt_nus_client *nus = context;
LOG_INF("Service discovery completed");
bt_gatt_dm_data_print(dm); /* 打印整个 Attribute Table(调试用) */
bt_nus_handles_assign(dm, nus); /* 保存 NUS 特征句柄 */
bt_nus_subscribe_receive(nus); /* 订阅 TX Characteristic 的 Notify */
bt_gatt_dm_data_release(dm); /* 释放 DM 资源 */
}
static void discovery_service_not_found(struct bt_conn *conn, void *context)
{
LOG_INF("Service not found\n");
}
static void discovery_error(struct bt_conn *conn, int err, void *context)
{
LOG_INF("Error while discovering GATT database: (%d)\n", err);
}
/* GATT DM 回调结构体 */
static struct bt_gatt_dm_cb discovery_cb = {
.completed = discovery_complete,
.service_not_found = discovery_service_not_found,
.error_found = discovery_error,
};
/* 主动启动 GATT Service 发现 */
static void gatt_discover(struct bt_conn *conn)
{
int err;
int connect_num = conn_get_index(conn);
if (connect_num < 0)
{
LOG_INF("Error:the number of connected device larger than MAX_CONN\n");
}
err = bt_gatt_dm_start(conn, BT_UUID_NUS_SERVICE, &discovery_cb, &nus_client[connect_num]);
if (err)
{
LOG_INF("Could not start discovery (err %d)\n", err);
}
}
/* ──────────────────────────────────────────────────────────────────────────── */
/* MTU 协商完成回调 */
/* ──────────────────────────────────────────────────────────────────────────── */
static void exchange_func(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
{
if (!err)
{
LOG_INF("MTU exchange done");
}
else
{
LOG_INF("MTU exchange failed (err %" PRIu8 ")\n", err);
}
}
/* ──────────────────────────────────────────────────────────────────────────── */
/* 连接、断开、加密状态回调 */
/* ──────────────────────────────────────────────────────────────────────────── */
static void connected(struct bt_conn *conn, uint8_t conn_err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
// 连接成功,分配句柄
if (conn_err)
{
LOG_INF("Failed to connect to %s, 0x%02x %s\n", addr, conn_err, bt_hci_err_to_str(conn_err));
return;
}
else
{
int connect_num = conn_get_index(conn);
if (connect_num < 0)
{
LOG_INF("Error:the number of connected device larger than MAX_CONN\n");
return;
}
}
LOG_INF("Connected: %s\n", addr);
/* 协商 ATT MTU */
static struct bt_gatt_exchange_params exchange_params = {.func = exchange_func};
if (bt_gatt_exchange_mtu(conn, &exchange_params))
{
LOG_INF("MTU exchange failed");
}
/* 尝试升级安全等级到 L2 (加密) */
if (bt_conn_set_security(conn, BT_SECURITY_L2))
{
LOG_INF("Failed to set security, fallback to discovery without encryption\n");
gatt_discover(conn);
}
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Disconnected: %s, reason 0x%02x %s\n", addr, reason, bt_hci_err_to_str(reason));
for (int i = 0; i < MAX_CONN; i++)
{
if (conns[i] == conn)
{
bt_conn_unref(conns[i]);
conns[i] = NULL;
}
}
(void)k_work_submit(&scan_work); /* 自动重连 */
}
static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (!err)
{
LOG_INF("Security changed: %s level %u\n", addr, level);
}
else
{
LOG_INF("Security failed: %s level %u err %d %s\n", addr, level, err, bt_security_err_to_str(err));
}
gatt_discover(conn); /* 加密成功/失败都继续做服务发现 */
}
/* 将上面的回调注册到全局 */
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
.security_changed = security_changed,
};
/* ──────────────────────────────────────────────────────────────────────────── */
/* 扫描模块回调 */
/* ──────────────────────────────────────────────────────────────────────────── */
static void scan_filter_match(struct bt_scan_device_info *device_info,
struct bt_scan_filter_match *filter_match,
bool connectable)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr));
LOG_INF("Filters matched. Address: %s connectable: %d\n", addr, connectable);
}
static void scan_connecting_error(struct bt_scan_device_info *device_info)
{
LOG_INF("Connecting failed\n");
}
static void scan_connecting(struct bt_scan_device_info *device_info, struct bt_conn *conn)
{
LOG_INF("Try Connecting\n");
}
/* ──────────────────────────────────────────────────────────────────────────── */
/* NUS Client 初始化 */
/* ──────────────────────────────────────────────────────────────────────────── */
static int nus_client_init(void)
{
struct bt_nus_client_init_param init = {
.cb = {
.received = ble_data_received,
.sent = ble_data_sent,
},
};
int err;
for (int i = 0; i < MAX_CONN; i++)
{
err = bt_nus_client_init(&nus_client[i], &init);
if (err)
{
LOG_ERR("NUS Client initialization failed (err %d)\n", err);
return err;
}
}
LOG_INF("NUS Client module initialized\n");
return 0;
}
/* 扫描模块全局回调注册宏 */
BT_SCAN_CB_INIT(scan_cb, scan_filter_match, NULL, scan_connecting_error, scan_connecting);
/* ──────────────────────────────────────────────────────────────────────────── */
/* 动态补充地址过滤器:遍历已绑定设备 */
/* ──────────────────────────────────────────────────────────────────────────── */
static void try_add_address_filter(const struct bt_bond_info *info, void *user_data)
{
char addr[BT_ADDR_LE_STR_LEN];
uint8_t *filter_mode = user_data;
bt_addr_le_to_str(&info->addr, addr, sizeof(addr));
/* 若当前地址已经在连接中,则跳过 */
struct bt_conn *conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &info->addr);
if (conn)
{
bt_conn_unref(conn);
return;
}
int err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_ADDR, &info->addr);
if (err)
{
LOG_ERR("Address filter cannot be added (err %d): %s\n", err, addr);
return;
}
LOG_INF("Address filter added: %s\n", addr);
*filter_mode |= BT_SCAN_ADDR_FILTER;
}
/* ──────────────────────────────────────────────────────────────────────────── */
/* 扫描启动函数 —— 配置 UUID/地址过滤并开始扫描 */
/* ──────────────────────────────────────────────────────────────────────────── */
static int scan_start(void)
{
int err;
uint8_t filter_mode = 0;
/* 若之前已在扫描则先停止 */
err = bt_scan_stop();
if (err)
{
LOG_ERR("Failed to stop scanning (err %d)\n", err);
return err;
}
/* 先清空旧过滤器 */
bt_scan_filter_remove_all();
/* 添加 UUID = NUS Service 过滤器 */
err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_NUS_SERVICE);
if (err)
{
LOG_ERR("UUID filter cannot be added (err %d)\n", err);
return err;
}
filter_mode |= BT_SCAN_UUID_FILTER;
/* 遍历 Bond 列表,把已配对过的地址也加入过滤器 */
bt_foreach_bond(BT_ID_DEFAULT, try_add_address_filter, &filter_mode);
/* 启用组合过滤器 */
err = bt_scan_filter_enable(filter_mode, false);
if (err)
{
LOG_ERR("Filters cannot be turned on (err %d)\n", err);
return err;
}
/* 开启主动扫描(能接收 Scan Rsp),Legacy 模式 */
err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
if (err)
{
LOG_ERR("Scanning failed to start (err %d)\n", err);
return err;
}
LOG_INF("Scan started\n");
return 0;
}
/* 扫描重启工作项 */
static void scan_work_handler(struct k_work *item)
{
ARG_UNUSED(item);
(void)scan_start();
}
/* 扫描模块初始化:注册回调与工作项 */
static void scan_init(void)
{
struct bt_scan_init_param scan_init = {
.connect_if_match = true, /* 过滤命中即自动连接 */
};
bt_scan_init(&scan_init);
bt_scan_cb_register(&scan_cb);
k_work_init(&scan_work, scan_work_handler);
LOG_INF("Scan module initialized\n");
}
/* ──────────────────────────────────────────────────────────────────────────── */
/* 配对/安全相关回调 —— 这里只实现基本日志 */
/* ──────────────────────────────────────────────────────────────────────────── */
static void auth_cancel(struct bt_conn *conn)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Pairing cancelled: %s\n", addr);
}
static void pairing_complete(struct bt_conn *conn, bool bonded)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Pairing completed: %s, bonded: %d\n", addr, bonded);
}
static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Pairing failed: %s, reason %d %s\n", addr, reason, bt_security_err_to_str(reason));
}
static struct bt_conn_auth_cb conn_auth_callbacks = {
.cancel = auth_cancel,
};
static struct bt_conn_auth_info_cb conn_auth_info_callbacks = {
.pairing_complete = pairing_complete,
.pairing_failed = pairing_failed,
};
/* ──────────────────────────────────────────────────────────────────────────── */
/* 主函数 —— 初始化各模块并进入主循环 */
/* ──────────────────────────────────────────────────────────────────────────── */
int main(void)
{
int err;
/* 注册配对/授权回调 */
err = bt_conn_auth_cb_register(&conn_auth_callbacks);
if (err)
{
LOG_ERR("Failed to register authorization callbacks");
return 0;
}
err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
if (err)
{
LOG_ERR("Failed to register authorization info callbacks");
return 0;
}
/* 启动 BLE 协议栈 */
err = bt_enable(NULL);
if (err)
{
LOG_ERR("Bluetooth init failed (err %d)\n", err);
return 0;
}
LOG_INF("Bluetooth initialized");
/* 加载持久化设置(Bond/CCC 等) */
if (IS_ENABLED(CONFIG_SETTINGS))
{
settings_load();
}
/* 初始化 UART、NUS Client、扫描 */
if (uart_init())
{
LOG_ERR("uart_init failed");
return 0;
}
if (nus_client_init())
{
return 0;
}
scan_init();
if (scan_start())
{
return 0;
}
LOG_INF("Starting Bluetooth Central UART sample\n");
/* ------------------------------------------------------------------ */
/* 主循环:等待来自 UART 的数据 → 透传到 BLE */
/* ------------------------------------------------------------------ */
struct uart_data_t nus_data = {.len = 0};
for (;;)
{
/* 阻塞等待 FIFO 里出现已收完一行的缓冲区 */
struct uart_data_t *buf = k_fifo_get(&fifo_uart_rx_data, K_FOREVER);
/* 逐字节拷贝到临时拼包缓存 (nus_data),处理粘包/半包 */
int plen = MIN(sizeof(nus_data.data) - nus_data.len, buf->len);
int loc = 0;
while (plen > 0)
{
memcpy(&nus_data.data[nus_data.len], &buf->data[loc], plen);
nus_data.len += plen;
loc += plen;
/* 满一包或遇到换行符就发送 */
if (nus_data.len >= sizeof(nus_data.data) ||
nus_data.data[nus_data.len - 1] == '\n' ||
nus_data.data[nus_data.len - 1] == '\r')
{
for (size_t i = 0; i < MAX_CONN; i++)
{
err = bt_nus_client_send(&nus_client[i], nus_data.data, nus_data.len);
if (err)
{
LOG_INF("Failed to send data over BLE (err %d)\n", err);
}
/* 等待写完成或超时 */
if (k_sem_take(&nus_write_sem, NUS_WRITE_TIMEOUT))
{
LOG_INF("NUS send timeout");
}
}
nus_data.len = 0; /* 清空缓冲继续收集 */
}
plen = MIN(sizeof(nus_data.data), buf->len - loc);
}
k_free(buf); /* 用完释放 */
}
}
三、从机代码
由于从机不需要多连接,因此只需要修改官方从机样例即可,比如实现从机作为RSSI采集器的功能。
2.1 实现扫描数据包功能
2.1.1 设备发现
用于识别指定设备,当识别到包含指定设备信息的数据包时,直接获取该设备的RSSI用于传递给主机。这里的RSSI利用extern传递给main文件,并通过nus发送给主机。
/* ------------------------ 设备发现回调 ------------------------ */
static void device_found_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
struct net_buf_simple *ad)
{
// 示例目标数据(实际使用时可以通过参数传入)
static const uint8_t target_data[] = TARGET_DATA;
struct parse_context ctx = {
.target_type = CUSTOM_DATA_TYPE,
.target_data = target_data,
.target_len = sizeof(target_data),
.found = false,
};
// 解析广播数据
bt_data_parse(ad, ad_parse_cb, &ctx);
if (ctx.found)
{
target_rssi = rssi;
char addr_str[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
}
}
2.1.2 广播包解析
先识别数据类型,后解析数据。
/* ------------------------ 广播数据解析回调 ------------------------ */
static bool ad_parse_cb(struct bt_data *data, void *user_data)
{
struct parse_context *ctx = user_data;
// 只处理目标数据类型
if (data->type == ctx->target_type)
{
// 检查数据是否匹配(如果target_len为0则匹配所有该类型数据)
if (ctx->target_len == 0 ||
(data->data_len >= ctx->target_len &&
memcmp(data->data, ctx->target_data, ctx->target_len) == 0))
{
ctx->found = true;
return false; // 找到匹配,停止解析
}
}
return true; // 继续解析
}
2.1.3 扫描实现
这里需要注意的是,开启扫描之前必须关闭广播,不然会报错。
/* 连接回调函数:连接建立 */
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err)
{
printk("Connection failed (err %u)\n", err);
}
else
{
current_conn = bt_conn_ref(conn); /* 记下来 */
printk("Connected\n");
}
struct bt_conn_info info;
int rc = bt_conn_get_info(conn, &info);
if (rc == 0)
{
char addr_str[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(&info.le.dst, addr_str, sizeof(addr_str));
printk("Peer MAC address: %s\n", addr_str);
}
else
{
printk("Failed to get peer info: %d\n", rc);
}
err = bt_le_adv_stop();
if (err)
{
printk("bt_le_adv_stop failed err: %d\n", err);
}
err = start_scan();
if (err)
{
printk("start_scan failed err: %d\n", err);
}
else
{
printk("Scan success\n");
}
}
3.2 完整从机代码
3.2.1 main.c
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/services/nus.h>
/* 设备名称,来源于 prj.conf 的 CONFIG_BT_DEVICE_NAME 宏定义 */
#define DEVICE_NAME "NUS_TEST"
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
extern volatile int8_t target_rssi;
uint8_t target_byte[] = {0xff, 0xff, 0x12, 0x34, 0x56}; // 要匹配的数据模式
/* ---------- 广播数据设置 ---------- */
/* 广播包内容:标记为通用可连接设备 + 不支持 BR/EDR + 完整设备名 */
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(BT_DATA_MANUFACTURER_DATA, target_byte, sizeof(target_byte)),
};
/* 扫描响应数据:包含 NUS 服务 UUID,提示中心设备可以连接进行 UART 通信 */
static const struct bt_data sd[] = {
BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_SRV_VAL),
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};
static void adv_restart_fn(struct k_work *work)
{
/* 保险起见:先停,再启 */
bt_le_scan_stop();
int err = bt_le_adv_start(BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE,
BT_GAP_ADV_FAST_INT_MIN_1,
BT_GAP_ADV_FAST_INT_MAX_1,
NULL),
ad, ARRAY_SIZE(ad),
sd, ARRAY_SIZE(sd));
if (err)
{
printk("adv_restart_fn failed: %d\n", err);
}
else
{
printk("advertise restart success\n");
}
}
K_WORK_DEFINE(adv_restart_work, adv_restart_fn);
static struct bt_conn *current_conn;
/* 连接回调函数:连接建立 */
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err)
{
printk("Connection failed (err %u)\n", err);
}
else
{
current_conn = bt_conn_ref(conn); /* 记下来 */
printk("Connected\n");
}
struct bt_conn_info info;
int rc = bt_conn_get_info(conn, &info);
if (rc == 0)
{
char addr_str[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(&info.le.dst, addr_str, sizeof(addr_str));
printk("Peer MAC address: %s\n", addr_str);
}
else
{
printk("Failed to get peer info: %d\n", rc);
}
err = bt_le_adv_stop();
if (err)
{
printk("bt_le_adv_stop failed err: %d\n", err);
}
err = start_scan();
if (err)
{
printk("start_scan failed err: %d\n", err);
}
else
{
printk("Scan success\n");
}
}
/* 连接回调函数:断开连接 */
static void disconnected(struct bt_conn *conn, uint8_t err)
{
printk("%x\n", current_conn);
if (current_conn)
{
bt_conn_unref(current_conn);
current_conn = NULL;
printk("Disconnected (reason 0x%02x)\n", err);
k_work_submit(&adv_restart_work);
}
}
/* 注册 bt_conn_cb 回调结构体 */
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
};
/* ---------- 回调:通知状态改变(中心是否订阅了 notify) ---------- */
static void notif_enabled(bool enabled, void *ctx)
{
ARG_UNUSED(ctx); // 当前未使用上下文参数
printk("%s() - %s\n", __func__, (enabled ? "Enabled" : "Disabled"));
// 输出:当前 notify 是否已启用(中心设备是否订阅了通知)
}
/* ---------- 回调:接收到中心发来的数据 ---------- */
static void received(struct bt_conn *conn, const void *data, uint16_t len, void *ctx)
{
char message[CONFIG_BT_L2CAP_TX_MTU + 1] = "";
ARG_UNUSED(conn);
ARG_UNUSED(ctx);
// 拷贝数据至 message 缓冲区,确保不会溢出
memcpy(message, data, MIN(sizeof(message) - 1, len));
message[sizeof(message) - 1] = '\0'; // 保证以 NULL 结尾
printk("%s() - Len: %d, Message: %s\n", __func__, len, message);
}
/* ---------- 注册的 NUS 服务回调结构体 ---------- */
struct bt_nus_cb nus_listener = {
.notif_enabled = notif_enabled, // 订阅通知回调
.received = received, // 数据接收回调
};
/* ---------- 主函数 ---------- */
int main(void)
{
int err;
printk("Sample - Bluetooth Peripheral NUS\n");
/* 注册 NUS 服务的回调监听函数(连接事件、接收数据、通知开启等) */
err = bt_nus_cb_register(&nus_listener, NULL);
if (err)
{
printk("Failed to register NUS callback: %d\n", err);
return err;
}
/* 启动 BLE 协议栈 */
err = bt_enable(NULL);
if (err)
{
printk("Failed to enable bluetooth: %d\n", err);
return err;
}
err = bt_conn_cb_register(&conn_callbacks);
if (err)
{
printk("Failed to register NUS callback: %d\n", err);
return err;
}
/* 开始广播,允许中心设备扫描并连接
* 广播参数使用默认快速连接配置(BT_LE_ADV_CONN_FAST_1)
*/
err = bt_le_adv_start(BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE,
BT_GAP_ADV_FAST_INT_MIN_1,
BT_GAP_ADV_FAST_INT_MAX_1,
NULL),
ad, ARRAY_SIZE(ad),
sd, ARRAY_SIZE(sd));
if (err)
{
printk("Failed to start advertising: %d\n", err);
return err;
}
else
{
printk("Advertising success\n");
}
printk("Initialization complete\n");
char str[4]; // 最多 3 位数字 + '\0'
int len;
/* 主循环:每隔 3 秒向所有连接的中心设备发送一条消息 */
while (true)
{
const char *hello_world = "Hello World!\n";
k_sleep(K_MSEC(500)); // 休眠 3 秒
len = int8_to_digits(target_rssi, str);
// err = bt_nus_send(NULL, hello_world, strlen(hello_world)); // 向所有连接发送数据
err = bt_nus_send(NULL, &str, strlen(str));
printk("target_rssi:%d\n", target_rssi);
/* 错误处理:若连接丢失、缓存满等,则退出 */
if (err < 0 && (err != -EAGAIN) && (err != -ENOTCONN))
{
return err;
}
}
return 0;
}
3.2.2 scan_uuid.c
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/sys/printk.h>
#include <string.h>
/* ------------------------ 自定义数据类型定义 ------------------------ */
// 示例:我们要识别的自定义数据类型(可修改)
#define CUSTOM_DATA_TYPE 0xFF // 制造商特定数据
#define TARGET_DATA {0xff, 0xff, 0x12, 0x34, 0x56, 0x78} // 要匹配的数据模式
volatile int8_t target_rssi = -99;
/* ------------------------ 数据解析上下文结构体 ------------------------ */
struct parse_context
{
uint8_t target_type; // 要匹配的数据类型
const uint8_t *target_data; // 要匹配的数据内容
size_t target_len; // 目标数据长度
bool found; // 是否找到匹配
};
/* ------------------------ 广播数据解析回调 ------------------------ */
static bool ad_parse_cb(struct bt_data *data, void *user_data)
{
struct parse_context *ctx = user_data;
// 只处理目标数据类型
if (data->type == ctx->target_type)
{
// 检查数据是否匹配(如果target_len为0则匹配所有该类型数据)
if (ctx->target_len == 0 ||
(data->data_len >= ctx->target_len &&
memcmp(data->data, ctx->target_data, ctx->target_len) == 0))
{
ctx->found = true;
return false; // 找到匹配,停止解析
}
}
return true; // 继续解析
}
/* ------------------------ 设备发现回调 ------------------------ */
static void device_found_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
struct net_buf_simple *ad)
{
// 示例目标数据(实际使用时可以通过参数传入)
static const uint8_t target_data[] = TARGET_DATA;
struct parse_context ctx = {
.target_type = CUSTOM_DATA_TYPE,
.target_data = target_data,
.target_len = sizeof(target_data),
.found = false,
};
// 解析广播数据
bt_data_parse(ad, ad_parse_cb, &ctx);
if (ctx.found)
{
target_rssi = rssi;
char addr_str[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
}
}
/* ------------------------ 启动扫描函数 ------------------------ */
int start_scan(void)
{
struct bt_le_scan_param scan_param = {
.type = BT_LE_SCAN_TYPE_PASSIVE,
.options = BT_LE_SCAN_OPT_NONE,
.interval = 0x0040, // 扫描间隔
.window = 0x0040, // 扫描窗口
};
return bt_le_scan_start(&scan_param, device_found_cb);
}

655

被折叠的 条评论
为什么被折叠?



