从零打造高精度温湿度监测仪:STM32与SHT40的完美结合
在智能家居和工业物联网快速发展的今天,环境监测已成为许多项目的基础需求。无论是温室大棚的精准农业,还是实验室的环境控制,一个稳定可靠的温湿度监测系统都至关重要。本文将带你从零开始,使用STM32F103RBT6微控制器和SHT40高精度传感器,配合TFT显示屏,打造一个功能完善、显示直观的温湿度监测设备。
1. 硬件准备与连接
1.1 所需材料清单
构建这个项目需要以下核心组件:
- 主控芯片:STM32F103RBT6开发板(兼容零知标准板)
- 传感器模块:SHT40温湿度传感器(精度±0.2℃/±2%RH)
- 显示模块:240×240像素ST7789驱动TFT屏幕
- 其他配件:
- 轻触开关(用于界面切换)
- 4pin卧贴插座(连接传感器)
- 杜邦线若干
- 面包板或PCB(可选)
提示:SHT40传感器有多个版本,建议选择带电平转换的模块,避免3.3V与5V系统间的兼容问题。
1.2 电路连接详解
正确的硬件连接是项目成功的第一步。以下是详细的引脚对应关系:
| STM32引脚 | 连接器件 | 功能说明 | 备注 |
|---|---|---|---|
| PA5 | SHT40 SCL | 软件I2C时钟线 | 可自定义其他IO |
| PA4 | SHT40 SDA | 软件I2C数据线 | 需上拉4.7k电阻 |
| PB10 | TFT_CS | 屏幕片选信号 | SPI片选 |
| PB11 | TFT_DC | 数据/命令选择 | |
| PB12 | TFT_RST | 屏幕复位信号 | 可接MCU复位 |
| PB13 | TFT_SCK | SPI时钟线 | 硬件SPI时钟 |
| PB15 | TFT_MOSI | SPI数据输出 | 硬件SPI数据线 |
| PA0 | 按键输入 | 界面切换控制 | 内部上拉,接地触发 |
| 3.3V | 传感器电源 | 为SHT40和TFT供电 | 确保电源稳定 |
| GND | 共地连接 | 所有器件的地线连接 | 避免地环路干扰 |
接线注意事项:
- I2C总线需要上拉电阻(通常4.7kΩ)
- TFT屏幕的背光可能需要单独供电
- 长距离连接时考虑使用屏蔽线减少干扰
2. 开发环境搭建
2.1 零知IDE基础配置
零知IDE为STM32开发提供了便捷的环境:
- 下载并安装最新版零知IDE(官网提供Windows/macOS版本)
- 安装STM32支持包:
# 在零知IDE的包管理器中搜索并安装 STM32F1xx_DFP - STM32F1系列设备支持包 - 配置板卡选项:
- 选择开发板类型:STM32F103RBT6
- 设置调试接口:SWD
- 配置时钟源:8MHz外部晶振
2.2 必要库的安装
本项目需要以下关键库支持:
- 传感器驱动:Adafruit_SHT4x_Library
- 显示驱动:Adafruit_ST7789
- 图形库:Adafruit_GFX
- 软件I2C:SoftWire
安装方法:
// 在零知IDE的库管理器中搜索并安装: 1. 搜索"SHT4x" → 安装Adafruit_SHT4x 2. 搜索"ST7789" → 安装Adafruit_ST7789 3. 搜索"SoftWire" → 安装Software I2C库2.3 项目基础配置
创建新项目后,需要进行以下关键配置:
- 启用硬件SPI:
- 在
hal_conf.h中取消注释#define HAL_SPI_MODULE_ENABLED
- 在
- 设置优化级别:
- 项目属性 → C/C++构建 → 设置优化为-O2
- 配置串口调试:
- 启用USART1,波特率115200
3. 核心代码实现
3.1 传感器驱动实现
SHT40通过软件I2C通信,以下是关键代码段:
// 软件I2C配置 #define SCL_PIN PA5 #define SDA_PIN PA4 SoftWire sht40Wire(SCL_PIN, SDA_PIN); // 初始化传感器 bool initSHT40() { sht40Wire.begin(); sht40Wire.setClock(100000); // 100kHz I2C速度 // 发送复位命令 sht40Wire.beginTransmission(0x44); sht40Wire.write(0x94); // 复位命令 if(sht40Wire.endTransmission() != 0) { return false; } delay(10); return true; } // 读取温湿度数据 SHT40_Data readSHT40() { SHT40_Data data = {0, 0, false}; // 发送高精度测量命令 sht40Wire.beginTransmission(0x44); sht40Wire.write(0xFD); if(sht40Wire.endTransmission() != 0) { data.success = false; return data; } delay(10); // 等待测量完成 // 读取6字节数据 uint8_t bytes[6]; uint8_t count = sht40Wire.requestFrom(0x44, 6); if(count != 6) { data.success = false; return data; } for(int i=0; i<6; i++) { bytes[i] = sht40Wire.read(); } // CRC校验 if(crc8(bytes, 2) != bytes[2] || crc8(&bytes[3], 2) != bytes[5]) { data.success = false; return data; } // 数据转换 uint16_t tempRaw = (bytes[0] << 8) | bytes[1]; uint16_t humiRaw = (bytes[3] << 8) | bytes[4]; data.temperature = -45 + 175 * (float)tempRaw / 65535; data.humidity = -6 + 125 * (float)humiRaw / 65535; data.humidity = constrain(data.humidity, 0, 100); data.success = true; return data; }3.2 TFT显示驱动
ST7789显示屏的初始化与基本绘图功能:
// 显示屏引脚定义 #define TFT_CS PB10 #define TFT_DC PB11 #define TFT_RST PB12 Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST); // 初始化显示屏 void initTFT() { tft.init(240, 240); tft.setRotation(1); // 根据实际安装方向调整 tft.fillScreen(ST77XX_BLACK); // 设置默认字体 tft.setTextColor(ST77XX_WHITE); tft.setTextSize(1); } // 绘制温度环形图 void drawTempGauge(float temp) { int centerX = 120, centerY = 120; int radius = 80, thickness = 15; // 绘制背景 tft.drawCircle(centerX, centerY, radius, ST77XX_DARKGREY); // 计算填充角度 (0-50℃范围) float percent = constrain(temp, 0, 50) / 50.0; int endAngle = 360 * percent; // 绘制温度弧线 for(int i=0; i<=endAngle; i++) { float angle = radians(i-90); // 从顶部开始 int x = centerX + radius * cos(angle); int y = centerY + radius * sin(angle); tft.drawLine(centerX, centerY, x, y, ST77XX_CYAN); } // 显示温度值 tft.setCursor(centerX-30, centerY-10); tft.setTextSize(2); tft.print(temp, 1); tft.print("℃"); }3.3 主程序逻辑
整合传感器读取与显示更新的主循环:
// 全局变量 SHT40_Data sensorData; unsigned long lastUpdate = 0; const unsigned long UPDATE_INTERVAL = 1000; // 1秒更新一次 uint8_t currentScreen = 0; // 0-温度, 1-湿度, 2-VPD, 3-信息 void setup() { Serial.begin(115200); initTFT(); if(!initSHT40()) { Serial.println("SHT40初始化失败!"); while(1); } // 配置按键中断 pinMode(PA0, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(PA0), switchScreen, FALLING); // 显示初始界面 updateDisplay(); } void loop() { unsigned long currentMillis = millis(); // 定时读取传感器 if(currentMillis - lastUpdate >= UPDATE_INTERVAL) { sensorData = readSHT40(); if(sensorData.success) { updateDisplay(); } lastUpdate = currentMillis; } } // 更新显示内容 void updateDisplay() { tft.fillScreen(ST77XX_BLACK); switch(currentScreen) { case 0: // 温度界面 drawTempGauge(sensorData.temperature); break; case 1: // 湿度界面 drawHumiGauge(sensorData.humidity); break; case 2: // VPD界面 drawVPDScreen(calculateVPD(sensorData)); break; case 3: // 信息界面 drawInfoScreen(sensorData); break; } } // 按键中断服务函数 void switchScreen() { static unsigned long lastPress = 0; if(millis() - lastPress > 300) { // 防抖 currentScreen = (currentScreen + 1) % 4; updateDisplay(); lastPress = millis(); } }4. 高级功能实现
4.1 VPD(蒸气压差)计算
VPD是评估植物生长环境的重要指标:
float calculateVPD(SHT40_Data data) { if(!data.success) return -1; // 计算饱和蒸气压(SVP) float svp = 0.6108 * exp(17.27 * data.temperature / (data.temperature + 237.3)); // 计算实际蒸气压(AVP) float avp = data.humidity / 100.0 * svp; // 返回蒸气压差(kPa) return svp - avp; } // VPD评估函数 const char* evaluateVPD(float vpd) { if(vpd < 0.8) return "湿度过高(霉变风险)"; else if(vpd > 1.2) return "湿度过低(植物胁迫)"; else return "适宜范围"; }4.2 多界面切换设计
实现四个信息展示界面的平滑切换:
- 温度界面:环形温度计+数字显示
- 湿度界面:环形湿度计+百分比显示
- VPD界面:仪表盘+环境评估
- 信息界面:传感器参数+系统状态
// 绘制湿度界面 void drawHumiGauge(float humidity) { int centerX = 120, centerY = 120; int radius = 80; // 绘制背景 tft.drawCircle(centerX, centerY, radius, ST77XX_DARKGREY); // 计算填充角度 float percent = constrain(humidity, 0, 100) / 100.0; int endAngle = 360 * percent; // 绘制湿度弧线 for(int i=0; i<=endAngle; i++) { float angle = radians(i-90); int x = centerX + radius * cos(angle); int y = centerY + radius * sin(angle); tft.drawLine(centerX, centerY, x, y, ST77XX_BLUE); } // 显示湿度值 tft.setCursor(centerX-30, centerY-10); tft.setTextSize(2); tft.print(humidity, 1); tft.print("%"); // 显示界面标识 tft.setTextSize(1); tft.setCursor(10, 220); tft.print("湿度监测 | 按按键切换"); }4.3 数据平滑处理
为减少传感器噪声,实现数据平滑算法:
#define SAMPLE_SIZE 5 float tempHistory[SAMPLE_SIZE]; uint8_t historyIndex = 0; // 移动平均滤波 float smoothTemperature(float newTemp) { tempHistory[historyIndex] = newTemp; historyIndex = (historyIndex + 1) % SAMPLE_SIZE; float sum = 0; for(int i=0; i<SAMPLE_SIZE; i++) { sum += tempHistory[i]; } return sum / SAMPLE_SIZE; }5. 项目优化与调试
5.1 常见问题排查
问题1:传感器无响应或读数错误
解决方案:
- 检查I2C线路连接是否正确
- 确认电源电压稳定(3.3V±10%)
- 降低I2C时钟速度测试
- 添加I2C上拉电阻(4.7kΩ)
问题2:TFT显示异常
解决方案:
- 确认SPI引脚配置正确
- 检查屏幕背光供电
- 调整屏幕初始化延迟
- 尝试降低SPI时钟速度
问题3:按键响应不稳定
解决方案:
- 添加硬件消抖电路(0.1μF电容)
- 调整软件防抖时间
- 检查上拉电阻是否有效
5.2 性能优化技巧
SPI优化:
- 启用DMA传输
- 使用双缓冲机制
// 示例:SPI DMA配置 SPI_DMA_Init(); SPI_DMA_Enable();电源管理:
- 添加LC滤波电路
- 使用低噪声LDO稳压器
- 在代码中实现睡眠模式
内存优化:
- 将大字体数据放入Flash
- 使用内存池管理动态分配
// 将字体数据存入Flash const uint8_t fontData[] PROGMEM = {...};
5.3 扩展功能建议
数据记录:
- 添加SD卡模块存储历史数据
- 实现CSV格式数据导出
无线传输:
- 集成ESP8266实现WiFi上传
- 使用蓝牙模块进行手机连接
报警功能:
- 设置温湿度阈值报警
- 添加蜂鸣器和LED指示灯
云端监控:
- 对接MQTT服务器
- 开发Web监控界面
// 简单的阈值报警实现 void checkAlarm(SHT40_Data data) { static bool tempAlarm = false; static bool humiAlarm = false; if(data.temperature > 30.0 && !tempAlarm) { triggerAlarm("高温警告!"); tempAlarm = true; } else if(data.temperature <= 30.0) { tempAlarm = false; } if(data.humidity > 80.0 && !humiAlarm) { triggerAlarm("高湿警告!"); humiAlarm = true; } else if(data.humidity <= 80.0) { humiAlarm = false; } }6. 项目进阶与变种
6.1 外壳设计与安装
一个专业的项目离不开精良的外壳设计:
3D打印方案:
- 使用PLA材料打印定制外壳
- 设计散热孔和安装支架
- 考虑防水需求时可使用PETG材料
PCB设计:
- 将电路集成到单一PCB
- 添加电源管理电路
- 设计标准的传感器接口
安装建议:
- 避免阳光直射影响温度测量
- 保持传感器周围空气流通
- 远离热源和潮湿区域
6.2 多传感器融合
提升系统功能性的几种扩展方案:
- CO2传感器:SCD30或MH-Z19
- 气压传感器:BMP280
- 光照传感器:BH1750
- 空气质量传感器:SGP30
// 多传感器数据融合示例 typedef struct { float temperature; float humidity; float co2; float pressure; float lux; } EnvironmentData; EnvironmentData readAllSensors() { EnvironmentData data; // 读取各传感器 data.temperature = readSHT40().temperature; data.humidity = readSHT40().humidity; data.co2 = readSCD30().co2; data.pressure = readBMP280().pressure; data.lux = readBH1750().lux; return data; }6.3 低功耗优化
对于电池供电的应用场景:
硬件优化:
- 选择低功耗版本STM32
- 使用高效率DC-DC转换器
- 添加电源开关电路
软件优化:
- 实现深度睡眠模式
- 降低采样频率
- 优化显示刷新策略
// 低功耗模式实现 void enterSleepMode() { // 关闭外设 SPI.end(); sht40Wire.end(); // 配置唤醒源 attachInterrupt(digitalPinToInterrupt(PA0), wakeUp, LOW); // 进入停止模式 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 唤醒后重新初始化 SystemClock_Config(); initPeripherals(); } void wakeUp() { // 唤醒处理 }这个温湿度监测项目不仅展示了STM32与SHT40的完美配合,更体现了嵌入式系统开发的完整流程。从硬件连接到软件实现,从基础功能到高级优化,每个环节都蕴含着丰富的技术细节。在实际部署中,我发现传感器的安装位置对测量精度影响很大,建议通过延长线将传感器安装在最具代表性的位置,同时保持主控板在易于操作的位置。