这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【e起DIY】低功耗蓝牙温湿度计BLE广播温湿度数据

共2条 1/1 1 跳转至

【e起DIY】低功耗蓝牙温湿度计BLE广播温湿度数据

助工
2026-05-09 14:29:32     打赏

之前我们成功通过 I2C 把 SHT30 的温湿度数据读出来了,接下来就是怎么通过蓝牙发给手机。

最开始我尝试直接把数据扔进蓝牙的无连接广播包(比如 Manufacturer Data)里,但实际测试发现一个非常头疼的问题:通用手机测试软件(比如 nRF Connect)非常死板,它严格遵循蓝牙底层规范,会把接收到的数据全部强制显示为 16 进制(Hex)格式。这导致每次看温度还得自己去查 ASCII 码表翻译,非常不直观。

后面原因分析及对策: 既然底层广播规范限制了纯文本的直观显示,那如果想在 APP 里直接看到类似 T:23.5C H:45.2% 这样肉眼能看懂的纯文本,最好的办法就是让开发板支持蓝牙连接,并创建一个 GATT 服务端(Server)。我们可以将读取到的浮点数转换为字符串,塞进特征值(Characteristic)里,通过 Notify(通知)的方式主动推送到手机。

修改配置文件 prj.conf为了支持手机连接,我们需要打开 Zephyr 的蓝牙外设模式相关配置:

代码段
# 原有的 I2C 和打印配置保留
CONFIG_I2C=y
CONFIG_PRINTK=y
CONFIG_NRFX_TWIM=y
CONFIG_CBPRINTF_FP_SUPPORT=y

# 新增:蓝牙基础与外设模式配置
CONFIG_BT=y
CONFIG_BT_DEVICE_NAME="SHT30_GATT"
CONFIG_BT_PERIPHERAL=y  # 开启外设模式,允许被手机连接
CONFIG_BT_GATT_CLIENT=n

主要实现代码在原本 read_gy_sht30 成功读取数据的基础上,我们加入自定义的 GATT 服务和 Notify 逻辑。

C
#include <zephyr/kernel.h>#include <zephyr/drivers/i2c.h>#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>#include <stdio.h>#include <string.h>/* I2C 及 SHT30 读取部分略(与之前完全一致) */// extern int read_gy_sht30(double *temp, double *hum);/* 1. 自定义 128-bit UUID */static struct bt_uuid_128 my_service_uuid = BT_UUID_INIT_128(
    BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0));static struct bt_uuid_128 my_char_uuid = BT_UUID_INIT_128(
    BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1));static char temp_string[32] = "No Data";static bool notify_enabled = false;/* 2. 监听手机是否开启了 Notify 订阅 */static void my_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) {
    notify_enabled = (value == BT_GATT_CCC_NOTIFY);
}/* 3. 注册我们自己的 GATT 服务端 */BT_GATT_SERVICE_DEFINE(my_sensor_svc,
    BT_GATT_PRIMARY_SERVICE(&my_service_uuid),
    BT_GATT_CHARACTERISTIC(&my_char_uuid.uuid,
                           BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
                           BT_GATT_PERM_READ,                           NULL, NULL, temp_string),
    BT_GATT_CCC(my_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)
);/* 连接状态管理 */struct bt_conn *current_conn = NULL;static void connected(struct bt_conn *conn, uint8_t err) {    if (!err) current_conn = bt_conn_ref(conn);
}static void disconnected(struct bt_conn *conn, uint8_t reason) {    if (current_conn) {
        bt_conn_unref(current_conn);
        current_conn = NULL;
        notify_enabled = false;
    }
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
    .connected = connected,
    .disconnected = disconnected,
};/* 蓝牙广播包内容设置 */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_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};int main(void) {    double temperature, humidity;    
    // 初始化蓝牙
    bt_enable(NULL);    // 开启可连接广播
    bt_le_adv_start(BT_LE_ADV_CONN_NAME_AD, ad, ARRAY_SIZE(ad), NULL, 0);
    printk("BLE GATT Server Started...\n");    while (1) {        if (read_gy_sht30(&temperature, &humidity) == 0) {            /* 格式化为直观的纯文本字符串 */
            snprintf(temp_string, sizeof(temp_string), "T:%.1fC H:%.1f%%", temperature, humidity);            /* 如果手机已经连上,并且开启了 Notify 接收,就主动推过去 */
            if (current_conn && notify_enabled) {
                bt_gatt_notify(current_conn, &my_sensor_svc.attrs[2], temp_string, strlen(temp_string));
            }
        }
        k_msleep(2000); // 也是两秒刷新一次
    }    return 0;
}

最终效果验证将代码编译并烧录进去。通过手机打开 nRF Connect APP:

  1. 扫描找到 SHT30_GATT,点击 CONNECT 连接。

  2. 展开找到我们的自定义服务(Unknown Service),长按特征值右侧的数据区域,将格式选为 UTF-8 或者 String。

  3. 点击特征值旁边的 Notify(三个向下箭头)开启接收。

通过 APP 界面查看,成功将纯文本数据(例如 T:24.5C H:45.2%)读取并实时显示了出来,每两秒钟自动刷新一次数据

08ad6346-540c-405b-a657-8696b32be528.jpg

67e1feb1-f70d-4826-b5b4-3df9898ce28e.jpg


院士
2026-05-09 16:31:55     打赏
2楼

学习了,谢谢分享。


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]