【项目概述】
本项目实现了一个通过蓝牙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的小程序环境
【系统架构】

【固件开发】
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);
}
}
}/* * 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】温湿度计的微信小程序。-电子产品世界论坛
在此基础之上,我添加了一个选择对应开发板的选项。

在其核心代码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']【实验现象】
打开微信小程序,选择对应的开发板就可以获取相应的环境温湿度数据:



我要赚赏金
