这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【nFR54L15Zephyr】基于NRF54L15-DK无线温湿度计

共1条 1/1 1 跳转至

【nFR54L15Zephyr】基于NRF54L15-DK无线温湿度计

高工
2026-05-07 08:19:05     打赏

【项目概述】

本项目实现了一个通过蓝牙BLE向微信小程序实时推送SHT3XD温湿度传感器数据的系统。支持NRF54L15-DK和FRDM_MCXW71两个开发板,通过微信小程序可实时查看环境温度和湿度

【主要特性】

  • 双平台支持:NRF54L15-DK 和 FRDM_MCXW71 

  • 实时推送:每2秒自动推送温湿度数据 

  • 低功耗BLE:使用BLE Notify方式推送数据 

  • 自动重连:设备断开后自动恢复广播 

  • 微信小程序:无需安装专用APP,直接使用小程序连接

【硬件要求】

开发板:Nordic nRF54L15 DK  NXP FRDM-MCXW71

传感器:SHT3XD I2C温湿度传感器 

OLED屏幕:SSD1306 128x64 I2C OLED

手机端:微信版本:支持BLE的小程序环境

【系统架构】

image.png

【固件开发】

NRF54L15-DK固件

GATT服务定义

image.png

主要配置 (prj.conf)

CONFIG_SENSOR=y
CONFIG_CBPRINTF_FP_SUPPORT=y
CONFIG_DISPLAY=y
CONFIG_LOG=y
CONFIG_CHARACTER_FRAMEBUFFER=y
CONFIG_CHARACTER_FRAMEBUFFER_USE_DEFAULT_FONTS=y
CONFIG_HEAP_MEM_POOL_SIZE=4096

# Bluetooth configuration
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_SMP=y
CONFIG_BT_MAX_CONN=3
CONFIG_BT_DEVICE_NAME="SHT3XD_NRF54L15"
CONFIG_BT_DIS=n
CONFIG_BT_BAS=n
CONFIG_BT_HRS=n

# OLED Display (SSD1306)
CONFIG_SSD1306=y

关键代码文件

src/sht3xd_ble.c - BLE GATT服务和通知

/*
 * Copyright (c) 2024 Jianhua Liu
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/drivers/sensor.h>
#include <stdio.h>

#include "sht3xd_ble.h"

static volatile bool temp_ntf_enabled;
static volatile bool hum_ntf_enabled;

static struct {
    uint16_t temp;
    uint16_t hum;
} sensor_data;

static void temp_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
    temp_ntf_enabled = (value == BT_GATT_CCC_NOTIFY);
    printf("BLE: Temperature notifications %s\n", temp_ntf_enabled ? "enabled" : "disabled");
}

static void hum_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
    hum_ntf_enabled = (value == BT_GATT_CCC_NOTIFY);
    printf("BLE: Humidity notifications %s\n", hum_ntf_enabled ? "enabled" : "disabled");
}

static ssize_t read_temp(struct bt_conn *conn, const struct bt_gatt_attr *attr,
             void *buf, uint16_t len, uint16_t offset)
{
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &sensor_data.temp,
                 sizeof(sensor_data.temp));
}

static ssize_t read_hum(struct bt_conn *conn, const struct bt_gatt_attr *attr,
            void *buf, uint16_t len, uint16_t offset)
{
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &sensor_data.hum,
                 sizeof(sensor_data.hum));
}

/* GATT Service Definition */
BT_GATT_SERVICE_DEFINE(sht3xd_svc,
    BT_GATT_PRIMARY_SERVICE(BT_UUID_SHT3XD_SVC),
    BT_GATT_CHARACTERISTIC(BT_UUID_SHT3XD_TEMP,
                   BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
                   BT_GATT_PERM_NONE,
                   read_temp, NULL, &sensor_data.temp),
    BT_GATT_CCC(temp_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
    BT_GATT_CHARACTERISTIC(BT_UUID_SHT3XD_HUM,
                   BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
                   BT_GATT_PERM_NONE,
                   read_hum, NULL, &sensor_data.hum),
    BT_GATT_CCC(hum_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);

/* Advertising data */
static const struct bt_data ad[] = {
    BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    BT_DATA_BYTES(BT_DATA_UUID128_ALL,
               BT_UUID_128_ENCODE(0x00001234, 0xB5A3, 0xF393, 0xE0A9, 0xE50E24DCCA9E)),
};

/* Scan response data */
static const struct bt_data sd[] = {
    BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};

static void connected(struct bt_conn *conn, uint8_t err)
{
    if (err) {
        printf("BLE: Connection failed (err 0x%02x)\n", err);
        return;
    }
    printf("BLE: Connected\n");
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
    printf("BLE: Disconnected (reason 0x%02x)\n", reason);

    /* Restart advertising after disconnection */
    int err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
    if (err) {
        printf("BLE: Advertising restart failed (err %d)\n", err);
    } else {
        printf("BLE: Advertising restarted\n");
    }
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
    .connected = connected,
    .disconnected = disconnected,
};

int sht3xd_ble_init(void)
{
    int err;

    err = bt_enable(NULL);
    if (err) {
        printf("BLE: bt_enable failed (err %d)\n", err);
        return err;
    }
    printf("BLE: Bluetooth initialized\n");

    err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
    if (err) {
        printf("BLE: Advertising failed (err %d)\n", err);
        return err;
    }
    printf("BLE: Advertising started\n");

    return 0;
}

void sht3xd_ble_notify(const struct sensor_value *temp, const struct sensor_value *hum)
{
    int err;

    /* Convert sensor_value to uint16_t (value * 100 for 2 decimal places) */
    sensor_data.temp = (uint16_t)(sensor_value_to_double(temp) * 100);
    sensor_data.hum = (uint16_t)(sensor_value_to_double(hum) * 100);

    /* Notify temperature if enabled */
    if (temp_ntf_enabled) {
        err = bt_gatt_notify(NULL, &sht3xd_svc.attrs[2],
                     &sensor_data.temp, sizeof(sensor_data.temp));
        if (err && err != -ENOTCONN) {
            printf("BLE: Temp notify failed (err %d)\n", err);
        }
    }

    /* Notify humidity if enabled */
    if (hum_ntf_enabled) {
        err = bt_gatt_notify(NULL, &sht3xd_svc.attrs[5],
                     &sensor_data.hum, sizeof(sensor_data.hum));
        if (err && err != -ENOTCONN) {
            printf("BLE: Hum notify failed (err %d)\n", err);
        }
    }
}

src/sht3xd_ble.h

/*
 * Copyright (c) 2024 Jianhua Liu
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef SHT3XD_BLE_H__
#define SHT3XD_BLE_H__

#include <zephyr/bluetooth/gatt.h>
#include <zephyr/drivers/sensor.h>

/* Custom 128-bit UUIDs for SHT3XD Environmental Service */
#define BT_UUID_SHT3XD_SVC \
    BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00001234, 0xB5A3, 0xF393, 0xE0A9, 0xE50E24DCCA9E))

#define BT_UUID_SHT3XD_TEMP \
    BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00001235, 0xB5A3, 0xF393, 0xE0A9, 0xE50E24DCCA9E))

#define BT_UUID_SHT3XD_HUM \
    BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00001236, 0xB5A3, 0xF393, 0xE0A9, 0xE50E24DCCA9E))

/* Initialize BLE stack and start advertising */
int sht3xd_ble_init(void);

/* Send sensor data to all connected centrals via notification */
void sht3xd_ble_notify(const struct sensor_value *temp, const struct sensor_value *hum);

#endif /* SHT3XD_BLE_H__ */

【主程序main.c】

/*
 * Copyright (c) 2018 Peter Bigot Consulting, LLC
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/display/cfb.h>
#include <stdio.h>
#include <string.h>

#include "sht3xd_ble.h"

#define ALERT_HUMIDITY_LO 50
#define ALERT_HUMIDITY_HI 60

#ifdef CONFIG_SHT3XD_TRIGGER
static volatile bool alerted;

static void trigger_handler(const struct device *dev,
                const struct sensor_trigger *trig)
{
    alerted = !alerted;
}

#endif

int main(void)
{
    const struct device *const sht3xd_dev = DEVICE_DT_GET_ONE(sensirion_sht3xd);
    const struct device *display_dev;
    int rc;

    if (!device_is_ready(sht3xd_dev)) {
        printf("Error: SHT3XD device not ready\n");
        return 0;
    }
    printf("SHT3XD sensor initialized\n");

#ifdef CONFIG_SSD1306
    display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
    printf("Display device: %s\n", display_dev->name);
    printf("Display device addr: %p\n", (void *)display_dev);
    k_sleep(K_MSEC(100));
    if (!device_is_ready(display_dev)) {
        printf("Error: Display device not ready\n");
        return 0;
    }
    printf("SSD1306 display initialized\n");

    if (display_set_pixel_format(display_dev, PIXEL_FORMAT_MONO10) != 0) {
        if (display_set_pixel_format(display_dev, PIXEL_FORMAT_MONO01) != 0) {
            printf("Failed to set pixel format\n");
        }
    }

    if (cfb_framebuffer_init(display_dev)) {
        printf("Framebuffer init failed\n");
        return 0;
    }

    cfb_framebuffer_clear(display_dev, true);
    display_blanking_off(display_dev);
    display_set_contrast(display_dev, 200);

    printf("Display ready: %dx%d, rows=%d, cols=%d\n",
           cfb_get_display_parameter(display_dev, CFB_DISPLAY_WIDTH),
           cfb_get_display_parameter(display_dev, CFB_DISPLAY_HEIGHT),
           cfb_get_display_parameter(display_dev, CFB_DISPLAY_ROWS),
           cfb_get_display_parameter(display_dev, CFB_DISPLAY_COLS));

    uint8_t font_width, font_height;
    cfb_framebuffer_set_font(display_dev, 0);
    cfb_get_font_size(display_dev, 0, &font_width, &font_height);
    printf("Font: %dx%d\n", font_width, font_height);
#endif

#ifdef CONFIG_SHT3XD_TRIGGER
    struct sensor_trigger trig = {
        .type = SENSOR_TRIG_THRESHOLD,
        .chan = SENSOR_CHAN_HUMIDITY,
    };
    struct sensor_value lo_thr = { ALERT_HUMIDITY_LO };
    struct sensor_value hi_thr = { ALERT_HUMIDITY_HI };
    bool last_alerted = false;

    rc = sensor_attr_set(sht3xd_dev, SENSOR_CHAN_HUMIDITY,
                 SENSOR_ATTR_LOWER_THRESH, &lo_thr);
    if (rc == 0) {
        rc = sensor_attr_set(sht3xd_dev, SENSOR_CHAN_HUMIDITY,
                     SENSOR_ATTR_UPPER_THRESH, &hi_thr);
    }
    if (rc == 0) {
        rc = sensor_trigger_set(sht3xd_dev, &trig, trigger_handler);
    }
    if (rc != 0) {
        printf("SHT3XD: trigger config failed: %d\n", rc);
        return 0;
    }
    printf("Alert outside %d..%d %%RH\n", lo_thr.val1, hi_thr.val1);
#endif

    /* Initialize BLE and start advertising */
    rc = sht3xd_ble_init();
    if (rc != 0) {
        printf("BLE init failed: %d\n", rc);
    }

    char temp_str[32];
    char hum_str[32];

    while (true) {
        struct sensor_value temp, hum;

        rc = sensor_sample_fetch(sht3xd_dev);
        if (rc == 0) {
            rc = sensor_channel_get(sht3xd_dev, SENSOR_CHAN_AMBIENT_TEMP,
                        &temp);
        }
        if (rc == 0) {
            rc = sensor_channel_get(sht3xd_dev, SENSOR_CHAN_HUMIDITY,
                        &hum);
        }
        if (rc != 0) {
            printf("SHT3XD: failed: %d\n", rc);
            break;
        }

#ifdef CONFIG_SHT3XD_TRIGGER
        if (alerted != last_alerted) {
            if (lo_thr.val1 > hum.val1) {
                printf("ALERT: humidity %d < %d\n",
                       hum.val1, lo_thr.val1);
            } else if (hi_thr.val1 < hum.val1) {
                printf("ALERT: humidity %d > %d\n",
                       hum.val1, hi_thr.val1);
            } else {
                printf("ALERT: humidity %d <= %d <= %d\n",
                       lo_thr.val1, hum.val1, hi_thr.val1);
            }
            last_alerted = alerted;
        }
#endif

        printf("SHT3XD: %.2f Cel ; %.2f %%RH\n",
               sensor_value_to_double(&temp),
               sensor_value_to_double(&hum));

        /* Send sensor data via BLE notifications */
        sht3xd_ble_notify(&temp, &hum);

#ifdef CONFIG_SSD1306
        snprintf(temp_str, sizeof(temp_str), "T:%.1fC H:%.1f%%",
            sensor_value_to_double(&temp),
            sensor_value_to_double(&hum));

        cfb_framebuffer_clear(display_dev, false);
        cfb_framebuffer_set_font(display_dev, 0);
        cfb_print(display_dev, "NRF54L15_SHT30", 0, 0);
        cfb_framebuffer_set_font(display_dev, 1);
        cfb_print(display_dev, temp_str, 0, 16);
        cfb_framebuffer_finalize(display_dev);
#endif

        k_sleep(K_MSEC(2000));
    }
    return 0;
}

【注】获取温湿计的内容我在帖子:【nFR54L15Zephyr】基于OLED的温湿计-电子产品世界论坛 有单独的帖子

【微信小程序】

在这一篇文章中,我详细的介绍了基于FRDM_MCXW71的数据交互:【FRDMMCXW71|Zephyr】温湿度计的微信小程序。-电子产品世界论坛

在此基础之上,我添加了一个选择对应开发板的选项。

image.png

在其核心代码index.js中添加了多开发板的支持:

// 支持的开发板配置
const BOARDS = {
  FRDM_MCXW71: {
    name: 'MCXW71_TH',
    service: '00001809-0000-1000-8000-00805f9b34fb',
    tempChar: '00002a1c-0000-1000-8000-00805f9b34fb',
    humService: '0000181a-0000-1000-8000-00805f9b34fb',
    humChar: '00002a6f-0000-1000-8000-00805f9b34fb',
    tempSigned: true,   // 温度为有符号数
  },
  NRF54L15DK: {
    name: 'SHT3XD_NRF54L15',
    service: '00001234-B5A3-F393-E0A9-E50E24DCCA9E',
    tempChar: '00001235-B5A3-F393-E0A9-E50E24DCCA9E',
    humService: '00001234-B5A3-F393-E0A9-E50E24DCCA9E',
    humChar: '00001236-B5A3-F393-E0A9-E50E24DCCA9E',
    tempSigned: false,  // 温度为无符号数
  },
}

const BOARD_LIST = ['FRDM_MCXW71', 'NRF54L15DK']

【实验现象】

打开微信小程序,选择对应的开发板就可以获取相应的环境温湿度数据:

77f406cb971e7022a81d51306a639c6c.jpg

c75b8c3ab4258c705f1ff18edbc33fe9.jpg






关键词: nFR54L15Zephyr     NRF54L15-D    

共1条 1/1 1 跳转至

回复

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