传感器与可信数据 / 传感器实验发布 内容更新

做一个 ESP32 温湿度计:先把 SHT40 数据读对

给指定 SHT40 模块接好电源与 I2C,用锁定版本的 Sensirion 库读取带 CRC 检查的温湿度。完整示例遇到断线输出 NA,恢复后重新确认数据有效。

读完能做什么
得到每两秒一条、有单位和错误状态的串口读数,并完成断线与恢复检查记录。
开始之前
已完成 P06 I2C 扫描,能在指定板上编译、上传并打开 115200 串口;没有实物时先做构建。
所需器材
ESP32-S3-DevKitC-1 v1.1 N8R8、Adafruit SHT40 breakout PID4885、USB 数据线、短杜邦线、故障实验可选 SDA 串联 SPST 开关
版本组合
开发板:DevKitC-1 v1.1 / N8R8工具链:Arduino CLI 1.5.1 / Arduino-ESP32 3.3.12传感器驱动:Sensirion I2C SHT4x 1.1.2 / 41632a3690aef80c813de1d302f10ef79b3f8e1b依赖:Sensirion Core 0.7.3器件资料:SHT4x datasheet V6.4 (2023-11)
适用范围

仅官方 ESP32-S3-DevKitC-1 v1.1 / WROOM-1-N8R8 + Adafruit SHT40 PID4885;3.3V、GPIO8/9、100kHz;Arduino-ESP32 3.3.12。其他 SHT4x 地址或裸芯片不直接套接线。

这篇验证到了哪一步

资料核对已通过
docs/esp32-evidence/sensors.md#s01;模块 pinouts、数据手册 §4.3–4.8、驱动源码与 CRC 路径。验证日期:
构建 / 配置已通过
docs/esp32-evidence/logs/esp32-sht40-thermometer-esp32-sht40-thermometer.log;当前源SHA与arduino-builds.json一致,Exit 0。对象:ESP32-S3 / N8R8版本:CLI1.5.1 / core3.3.12 / SHT4x1.1.2 / Core0.7.3验证日期:
仿真不适用
尚无器件级 I2C/CRC 故障仿真证据;预期日志不作为仿真结果。
本站实板待验证
本站未完成正常读数、断线、恢复或参考仪表比较。

构建通过只说明指定环境中的代码或配置通过检查;实物功能、功耗和稳定性以对应的实板记录为准。

01 / 传感器实验

先看完成后要观察什么

接好一块 SHT40 后,让串口每两秒出现一条带单位、时间和状态的温湿度记录。正常时输出新读数;通信或校验失败时输出 NA;重新读到有效数据时标一次 RECOVERED。下面按接线、编译、上传、观察的顺序完成,再试一次可恢复的断线实验。

本篇只连一个模块,不加屏幕、Wi-Fi 或加热器。温度用摄氏度 °C,湿度用相对湿度 %RH。I2C 扫描成功只表示地址应答,还要让真正的测量命令和数据校验通过。

条件应观察的字段怎样判断
正常连接state=VALID;T_C、RH_raw、RH_display每条都来自本轮成功读取,保留时间与样本号
SDA 开关断开state=ERROR T=NA RH=NA;error、detail记录实际错误,不把旧温湿度当新值
闭合开关后重新读成功state=RECOVERED,随后 state=VALID先看到新的有效值,再判恢复

参考:[1] Sensirion SHT4x datasheet V6.4:数据格式、CRC、命令、换算与复位[2] Sensirion Arduino SHT4x 1.1.2:高精度读取与错误返回

02 / 传感器实验

先确认模块版本,再接四根线

这里采用带稳压、电平转换和上拉的 Adafruit PID4885,不是裸 SHT40 芯片。模块 VIN 接主控 3V3,让电源和逻辑处于同一电平;3Vo 是模块稳压器输出,保持不接。地址固定为 0x44,不能通过改软件地址让这一模块变成 0x45。其他 SHT4x 料号可能有不同地址选项,需另查其完整后缀。

完全断电后按端点表连接,GPIO 指信号号而非排针序号。只接 USB-to-UART 供电与监视。模块已有上拉,初次用短线、单模块和 100kHz;先不额外并联电阻。

主控端模块端核对
3V3 / J1-1VIN不使用5V或3Vo作为输入
GND / J1-22GND共地
GPIO8 / J1-12SDA按程序显式指定
GPIO9 / J1-15SCL100kHz
无3Vo留空
做到这里,你应该看到
断电逐端追线,四条连接与表一致,其他端口无第二路供电。

参考:[4] Adafruit SHT40 PID4885:供电、地址、上拉和引脚[5] 乐鑫 DevKitC-1 v1.1:固定修订排针与 N8R8 范围[6] Arduino-ESP32 3.3.12 Wire 实现:超时与总线初始化

03 / 传感器实验

装好两个指定库,先确认程序能编译

先完成 F05 的 Arduino-ESP32 3.3.12 环境。安装 Sensirion Core 0.7.3,再以 --no-deps 安装 Sensirion I2C SHT4x 1.1.2,避免自动换掉前一个依赖。本例不使用 Adafruit_SHT4x 类,下载库时看完整名字。

Windows 使用 PowerShell,macOS/Linux 使用终端;下方单引号命令不适用于 Windows cmd.exe。命令从本项目仓库根目录执行。只下载本页 ZIP 的读者,先解压并进入 esp32-sht40-thermometer 目录,把编译命令末尾的 examples/esp32/esp32-sht40-thermometer 换成一个点“.”。IDE 用户可在库管理器选择同样版本,打开目录内的同名 .ino,按 F05 的 N8R8 菜单配置后点击 Verify。

代码 / 配置
arduino-cli lib install 'Sensirion Core@0.7.3'
arduino-cli lib install --no-deps 'Sensirion I2C SHT4x@1.1.2'
arduino-cli compile --fqbn 'esp32:esp32:esp32s3:FlashSize=8M,PSRAM=opi,FlashMode=qio,PartitionScheme=default_8MB,CDCOnBoot=default,USBMode=hwcdc' examples/esp32/esp32-sht40-thermometer
做到这里,你应该看到
编译结束没有最终错误,日志使用指定 core 和两份库。编译阶段不需要插传感器。
如果卡住了
头文件找不到时先检查库名、版本和安装路径;sketch 路径不存在时,先核对是在仓库根目录还是 ZIP 解压目录。

参考:[2] Sensirion Arduino SHT4x 1.1.2:高精度读取与错误返回[3] Sensirion Core 0.7.3:接收帧与 CRC 验证[6] Arduino-ESP32 3.3.12 Wire 实现:超时与总线初始化

04 / 传感器实验

上传后,在 115200 串口里找启动行

先关闭占用端口的其他串口窗口。板子通过 USB-to-UART 接口连接电脑,USB CDC On Boot 保持 Disabled,按 F06 的方法用插拔确认真实端口。下方 PORT 是占位符,运行前替换为自己的端口名;不要原样照抄。

仓库用户沿用下方路径;ZIP 用户在刚才的解压目录内,把 upload 命令最后的项目路径也换成“.”。上传完成后打开监视器;如果没看到启动行,保持监视器打开,按一下板上复位键重新观察。IDE 用户可直接上传,再打开串口监视器并选择 115200。

启动时寻找后续动作
S01 core=3.3.12 driver=1.1.2 SDA=8 SCL=9 address=0x44 bus=1核对程序版本、引脚、地址和总线初始化状态
sensor_serial= 后的实际序列号保存自己的序列号;若是错误说明,保留完整文本
attempt=… uptime_ms=… state=VALID T_C=… RH_raw=… RH_display=… clipped=…这些省略号代表实际读数;继续记录至少 10 条
代码 / 配置
arduino-cli upload --fqbn 'esp32:esp32:esp32s3:FlashSize=8M,PSRAM=opi,FlashMode=qio,PartitionScheme=default_8MB,CDCOnBoot=default,USBMode=hwcdc' -p PORT examples/esp32/esp32-sht40-thermometer
arduino-cli monitor -p PORT --config baudrate=115200
做到这里,你应该看到
能看到本程序的启动信息和后续采样行;出现 ERROR 也先保存,按末尾症状表排查。
如果卡住了
窗口完全空白先查接口、实际端口、115200 和复位;有 S01 启动行却持续 ERROR,再查传感器线路。两种现象不要混在一起处理。

参考:[2] Sensirion Arduino SHT4x 1.1.2:高精度读取与错误返回[5] 乐鑫 DevKitC-1 v1.1:固定修订排针与 N8R8 范围[6] Arduino-ESP32 3.3.12 Wire 实现:超时与总线初始化

05 / 传感器实验

让完整程序处理校验、失效与重试

SHT4x 每个 16 位数据字后跟一个 CRC。固定驱动的高精度函数发出 0xFD,等待 10ms 后读取数据;Sensirion Core 的 receiveFrame 检查每个数据字的 CRC,错误沿返回码传给应用。程序必须先判断返回值,再使用输出变量。两个浮点变量每轮都初始化,失败分支只输出 NA,不会把上一次成功的温湿度再次作为新值。

周期计时采用无符号差值。Wire 的等待设为 20ms,测量不无限等待串口打开。每三次连续失败尝试一次传感器软复位,再等下一轮;软复位本身失败也记录错误。首次重新读到有效数据输出 RECOVERED,后续为 VALID。

湿度换算可能在量程边界产生小于0或大于100的原始数。按厂商建议,展示值限制到0–100 %RH,同时保留 RH_raw 和 clipped;裁边只改变显示,不证明误差变小。示例从不启动加热器。

代码 / 配置
// SPDX-License-Identifier: MIT
// S01: S3 DevKitC-1 v1.1 N8R8 + Adafruit SHT40 breakout PID 4885.
#include <Arduino.h>
#include <Wire.h>
#include <SensirionI2cSht4x.h>
#include <math.h>

SensirionI2cSht4x sensor;
constexpr uint8_t SDA_PIN = 8, SCL_PIN = 9;
constexpr uint32_t PERIOD_MS = 2000;
bool busReady = false, hadError = false;
uint32_t sampledAt = 0, attempts = 0, failures = 0;

bool startBus() {
  const bool ok = Wire.begin(SDA_PIN, SCL_PIN, 100000);
  Wire.setTimeOut(20);
  sensor.begin(Wire, SHT40_I2C_ADDR_44);
  return ok;
}
void setup() {
  Serial.begin(115200);
  busReady = startBus();
  delay(10);
  Serial.printf("S01 core=3.3.12 driver=1.1.2 SDA=8 SCL=9 address=0x44 bus=%u\n", busReady);
  if (busReady) {
    uint32_t serialNumber = 0;
    const int16_t error = sensor.serialNumber(serialNumber);
    if (error == 0) Serial.printf("sensor_serial=%lu\n", (unsigned long)serialNumber);
    else Serial.printf("sensor_serial_unavailable error=%d; measurements will retry\n", error);
  }
  sampledAt = millis();
}
void loop() {
  const uint32_t now = millis();
  if (uint32_t(now - sampledAt) < PERIOD_MS) return;
  sampledAt = now;
  ++attempts;
  if (!busReady) busReady = startBus();
  if (!busReady) {
    hadError = true;
    Serial.printf("attempt=%lu uptime_ms=%lu state=BUS_ERROR T=NA RH=NA\n", (unsigned long)attempts, (unsigned long)now);
    return;
  }
  float temperature = NAN, humidityRaw = NAN;
  const int16_t error = sensor.measureHighPrecision(temperature, humidityRaw);
  if (error != 0 || !isfinite(temperature) || !isfinite(humidityRaw)) {
    hadError = true;
    ++failures;
    char message[128] = "non-finite sensor result";
    if (error != 0) errorToString(error, message, sizeof(message));
    Serial.printf("attempt=%lu uptime_ms=%lu state=ERROR T=NA RH=NA error=%d detail=%s\n",
      (unsigned long)attempts, (unsigned long)now, error, message);
    if (failures % 3 == 0) {
      const int16_t resetError = sensor.softReset();
      Serial.printf("retry_soft_reset error=%d\n", resetError);
      delay(10);
    }
    return;
  }
  const float humidityDisplay = fminf(100.0f, fmaxf(0.0f, humidityRaw));
  Serial.printf("attempt=%lu uptime_ms=%lu state=%s T_C=%.2f RH_raw=%.2f RH_display=%.2f clipped=%u\n",
    (unsigned long)attempts, (unsigned long)now, hadError ? "RECOVERED" : "VALID",
    temperature, humidityRaw, humidityDisplay, humidityRaw != humidityDisplay);
  hadError = false;
  failures = 0;
}

参考:[1] Sensirion SHT4x datasheet V6.4:数据格式、CRC、命令、换算与复位[2] Sensirion Arduino SHT4x 1.1.2:高精度读取与错误返回[3] Sensirion Core 0.7.3:接收帧与 CRC 验证[6] Arduino-ESP32 3.3.12 Wire 实现:超时与总线初始化

06 / 传感器实验

把正常、断线、恢复各留一段记录

上传后先保存启动版本、总线状态与真实序列号。之后至少记录10条 VALID,保留 attempt 和 uptime_ms,检查单位与采样间隔。不要用本页截图或任意示例数值填自己的观察表。程序的2秒间隔是实验配置,不是精度保证。

故障实验先断电在 SDA 串入一只开关,再上电。通过开关断开信号,观察后续记录转为 ERROR、T=NA、RH=NA;错误码以实际结果为准。闭合后等待下一轮读取和必要的重试,第一次成功应是 RECOVERED。用开关执行断线,不带电移动供电导线。

将三种条件写入下载包 observations.csv,包含日期、板修订、驱动版本、样本号、错误与恢复。若对照另一温湿度计,记下其型号、摆放、等待时间和误差来源;普通仪表互相比对不等于完成计量校准。

  1. 正常连接保存启动和连续10条读数。
  2. 断开预装的SDA开关,保存失败码与NA,不把旧值算为有效。
  3. 闭合开关,观察RECOVERED之后重新进入VALID。
  4. 断电并恢复最小接线,保存CSV和未通过项目。
做到这里,你应该看到
每条记录都能分清当前条件与有效性;没有实板条件就保留空白观察表。

参考:[1] Sensirion SHT4x datasheet V6.4:数据格式、CRC、命令、换算与复位[2] Sensirion Arduino SHT4x 1.1.2:高精度读取与错误返回[4] Adafruit SHT40 PID4885:供电、地址、上拉和引脚

07 / 传感器实验

按看到的现象排错,再调整摆放

先保留实际错误文本,再按下表从最贴近现象的一项检查。扫描应答、数据读取、CRC 校验和环境精度分别回答不同问题;0x44 出现过,并不能让持续 ERROR 的测量变成有效。

读数稳定后,把模块移离 S3、稳压器、手指、USB 线接头和局部热源,等环境稳定再比较。湿度异常时检查水滴、溶剂、灰尘和气流影响,不向敏感窗口喷水或用热风烘烤。普通温湿度计对照只能帮助观察差异;要记录其型号、位置和等待时间,不写成完成计量校准。

现象先做这项检查恢复判据
没有任何启动或采样文字核对 USB-to-UART、端口、115200,打开监视器后复位出现 S01 启动行和采样行
持续 state=BUS_ERROR核对板目标和 Wire.begin(8, 9) 的总线初始化;保留启动状态总线初始化成功,程序进入测量
持续 state=ERROR用 P06 扫描 0x44,再断电核对 VIN/GND、SDA8/SCL9、模块型号和短线重新读取成功,输出新的有效值
扫描有 0x44,测量仍报错保存 error/detail,核对两个库版本、接触和 CRC 相关错误测量返回成功;不能只以扫描应答收口
闭合 SDA 开关后没有恢复等待后续采样与重试;仍失败则断电复核线路,再按原版本重启出现 RECOVERED 或重启后的 VALID,并继续得到新值
能读数,但温度持续偏高远离板上热源与手指,稳定后与已记录条件的参考仪表比较保留调整前后真实记录,不以任意固定温度为答案

参考:[1] Sensirion SHT4x datasheet V6.4:数据格式、CRC、命令、换算与复位[4] Adafruit SHT40 PID4885:供电、地址、上拉和引脚[5] 乐鑫 DevKitC-1 v1.1:固定修订排针与 N8R8 范围[6] Arduino-ESP32 3.3.12 Wire 实现:超时与总线初始化

BACK TO THE SOURCE

资料来源与版本说明

按具体模块供电、SHT4x V6.4 手册和固定版本驱动核对。无本站温湿度测量、拔线与恢复实测。资料核对日期:。资料核对、程序构建与实板测试分别记录,不互相替代。

  1. Sensirion SHT4x datasheet V6.4:数据格式、CRC、命令、换算与复位
  2. Sensirion Arduino SHT4x 1.1.2:高精度读取与错误返回
  3. Sensirion Core 0.7.3:接收帧与 CRC 验证
  4. Adafruit SHT40 PID4885:供电、地址、上拉和引脚
  5. 乐鑫 DevKitC-1 v1.1:固定修订排针与 N8R8 范围
  6. Arduino-ESP32 3.3.12 Wire 实现:超时与总线初始化