nRF Connect SDK v3.0.2-nRF52832DK 基于NUS服务的一主多从通信

目录

一、项目介绍

二、主机代码

2.1 多连接实现

2.1.1 写入同步与多连接参数配置

2.1.2 查找、分配连接句柄

2.1.3 连接、断开连接以及连接安全等级变更回调函数

2.1.4 NUS

根据NUS句柄查找连接句柄

数据发送回调

数据接收回调函数

GATT服务发现

2.1.5 UART

UART回调

 UART初始化

2.1.6 扫描从机

扫描回调函数

扫描类型回调函数注册 

2.2 完整主机代码

三、从机代码

2.1 实现扫描数据包功能

2.1.1 设备发现

2.1.2 广播包解析

2.1.3 扫描实现

3.2 完整从机代码

3.2.1 main.c

3.2.2 scan_uuid.c

一、项目介绍

主机扫描周围的从机并建立连接,当从机建立连接之后,开始获取发送指定广播数据设备的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);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值