零知IDE——基于STM32F103RBT6和SHT40温湿度传感器的环境监测系统

   ✔零知IDE 是一个真正属于国人自己的开源软件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!

✔访问零知实验室,获取更多实战项目和教程资源吧!

www.lingzhilab.com

目录

一、硬件系统设计

1.1 硬件清单

1.2 接线方案表

1.3 具体接线图

1.4 连接实物图

二、代码架构讲解

2.1 软件架构设计

2.2 软件I2C通信基础

2.3 数据读取完整流程

2.4 VPD计算与环境评估

2.5 项目源工程代码

三、SHT40传感器技术原理

 3.1 温湿度测量原理

 3.2 I2C通信协议详解

3.3 寄存器配置详解

四、项目结果演示

4.1 硬件SPI性能测试

4.2 界面展示详情

4.3 视频操作演示

五、常见问题解答(FAQ)

Q1:传感器读数一直显示"Sensor Error"怎么办?

Q2:VPD计算结果异常

Q3:如何进一步提升系统性能?


(1)项目概述

        本项目基于零知标准板和SHT40高精度温湿度传感器,实现了一个功能完整的智能环境监测系统。系统通过软件I2C通信协议驱动SHT40传感器,实时采集环境温湿度数据,并在240×240像素的ST7789 TFT显示屏上以图形化界面展示。系统支持四界面循环切换显示,包括温度环形图、湿度环形图、VPD(蒸气压力差)仪表盘和传感器信息界面,为用户提供直观的环境数据监测体验

(2)项目难点及解决方案

       问题描述:使用arduino-i2c-sht4x库时出现TwoWire类方法签名不匹配

解决方案:采用SoftWire软件I2C库完全规避硬件I2C依赖,通过软件模拟I2C时序确保通信稳定性

一、硬件系统设计

1.1 硬件清单

器件规格型号数量备注
零知标准板STM32F103RBT6主控1核心处理单元
零知标准板-扩展板扩展板直插1IIC直插
SHT40传感器温湿度传感器1高精度测量
ST7789显示屏240×240 TFT1图形化显示
按键开关轻触开关1界面切换
连接线束4pin卧贴座子1套传感器连接

1.2 接线方案表

根据代码引脚定义,具体接线如下:

零知标准板引脚连接器件引脚功能备注
引脚10ST7789TFT_CS片选信号
引脚8ST7789TFT_DC数据/命令选择
引脚9ST7789TFT_RST复位信号
引脚11ST7789TFT_MOSI硬件SPI数据线
引脚13ST7789TFT_SCK硬件SPI时钟线
A5SHT40SCL软件I2C时钟
A4SHT40SDA软件I2C数据
引脚3按键信号输入内部上拉
3.3VSHT40、ST7789电源正极统一供电
GNDSHT40、ST7789、按键电源地共地连接

        SHT40温湿度传感器采用线对板2.0引脚间距线束直插零知标准板-扩展板IIC连接器

1.3 具体接线图

注意:零知SHT40温湿度传感器采用LDO模块适配标准板板载电源输入1.5V-5.5V宽电压,满足芯片1.08V-3.6V供电电压

1.4 连接实物图

零知SHT40温湿度传感器详解

        优化电路布局,将LDO稳压器安装在远离传感器的位置,以降低热源对温度测量的干扰。采用4pin卧贴插座设计,实现快速接线操作。模块化结构设计使SHT40/SHT41传感器能够便捷更换

        1)电源管理设计,采用SUL6018S5-ADJ LDO稳压芯片为系统提供稳定的1.8V电源:支持1.5V-5.5V输入范围,适应多种电源环境

        2)信号电平匹配,选用TXS0102双向电平转换器:解决1.8V和3.3/5V设备间的通信兼容性问题、支持I2C总线的双向电平转换

二、代码架构讲解

2.1 软件架构设计

        代码使用零知DE框架编写,主要包含以下核心模块:

        1)传感器驱动模块--SHT40通信协议实现;2)显示控制模块--TFT屏幕图形渲染;3)用户交互模块--按键中断处理;4)数据处理模块--温湿度数据计算和VPD计算

2.2 软件I2C通信基础

// 软件I2C引脚定义
#define SCL_PIN A5
#define SDA_PIN A4

// SHT40传感器地址
#define SHT40_ADDRESS 0x44

// 创建软件I2C对象,使用引脚20(SCL)和19(SDA)
SoftWire sht40Wire(SCL_PIN, SDA_PIN, SOFT_STANDARD);

// I2C初始化配置
// SHT40初始化函数
bool sht40_init() {
  sht40Wire.begin();
  sht40Wire.setClock(100000);
  
  // 发送软复位命令
  sht40Wire.beginTransmission(SHT40_ADDRESS);
  sht40Wire.write(SHT40_SOFT_RESET);
  if (sht40Wire.endTransmission() != 0) {
    return false;
  }
  
  delay(10);
  
  // 读取序列号
  if (!sht40_read_serial()) {
    sensorModel = "SHT4x"; // 如果读取失败,使用默认型号
  }
  
  return true;
}

        SOFT_STANDARD:标准速度模式,适合大多数应用场景;setClock(100000):I2C通信频率100kHz,平衡速度和稳定性

2.3 数据读取完整流程

SHT40_Data sht40_read_data() {
  SHT40_Data data = {0, 0, false};
  uint8_t rx_bytes[6] = {0};
  
  // 发送高精度测量命令
  sht40Wire.beginTransmission(SHT40_ADDRESS);
  sht40Wire.write(SHT40_MEASURE_HIGH_PRECISION);
  if (sht40Wire.endTransmission() != 0) {
    data.success = false;
    return data;
  }
  
  delay(10); // 高精度模式测量时间约10ms
  
  // 读取6字节数据
  uint8_t bytes_read = sht40Wire.requestFrom(SHT40_ADDRESS, 6);
  if (bytes_read != 6) {
    data.success = false;
    return data;
  }
  
  for (int i = 0; i < 6; i++) {
    rx_bytes[i] = sht40Wire.read();
  }
  
  // CRC校验
  if ((sht40_crc8(rx_bytes, 2) == rx_bytes[2]) && 
      (sht40_crc8(&rx_bytes[3], 2) == rx_bytes[5])) {
    
    uint16_t t_ticks = (rx_bytes[0] << 8) | rx_bytes[1];
    uint16_t rh_ticks = (rx_bytes[3] << 8) | rx_bytes[4];
    
    data.temperature = -45.0 + 175.0 * (float)t_ticks / 65535.0;
    data.humidity = -6.0 + 125.0 * (float)rh_ticks / 65535.0;
    
    if (data.humidity > 100.0) data.humidity = 100.0;
    if (data.humidity < 0.0) data.humidity = 0.0;
    
    data.success = true;
  } else {
    data.success = false;
  }
  
  return data;
}

        温度公式:T = -45 + 175 × (ST / 65535);湿度公式:RH = -6 + 125 × (SRH / 65535)

2.4 VPD计算与环境评估

void showVPDScreen(SHT40_Data data) {
  // 计算饱和蒸气压(SVP)
  float svp = 0.6108 * exp(17.27 * data.temperature / (data.temperature + 237.3));
  // 计算实际蒸气压(AVP)
  float avp = data.humidity / 100 * svp;
  // 计算蒸气压亏缺(VPD)
  float vpd = svp - avp;
  
  // 环境状态评估
  if (vpd < 0.8) {
    // 低VPD:霉变风险
  } else if (vpd > 1.2) {
    // 高VPD:植物胁迫
  } else {
    // 适宜范围
  }
}

        <0.8 kPa:湿度过高,真菌病害风险;0.8-1.2 kPa:植物生长最佳范围;>1.2 kPa:蒸腾过强,植物水分胁迫

2.5 项目源工程代码

#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SoftWire.h>
#include <Fonts/FreeSansBold18pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Fonts/FreeSans9pt7b.h>

// ST7789显示引脚定义
#define TFT_CS   10
#define TFT_MOSI 11
#define TFT_SCK  13
#define TFT_RST  9
#define TFT_DC   8

// 按键引脚定义
#define BUTTON_PIN 3

// 软件I2C引脚定义
#define SCL_PIN A5
#define SDA_PIN A4

// SHT40传感器地址
#define SHT40_ADDRESS 0x44

// 创建SPI显示
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

// 创建软件I2C对象
SoftWire sht40Wire(SCL_PIN, SDA_PIN, SOFT_STANDARD);

// 全局变量
volatile int currentScreen = 0;
volatile bool screenChanged = false;
unsigned long lastButtonPress = 0;
unsigned long lastSensorRead = 0;
unsigned long lastDisplayUpdate = 0;
const unsigned long BUTTON_COOLDOWN = 300;
const unsigned long SENSOR_READ_INTERVAL = 500;    // 传感器读取间隔(ms)
const unsigned long DISPLAY_UPDATE_INTERVAL = 200; // 显示更新间隔(ms)

// 颜色定义
#define BACKGROUND_COLOR ST77XX_BLACK
#define PRIMARY_COLOR ST77XX_GREEN
#define SECONDARY_COLOR ST77XX_WHITE
#define TEMP_COLOR ST77XX_CYAN
#define HUMIDITY_COLOR ST77XX_BLUE
#define VPD_COLOR ST77XX_YELLOW
#define WARNING_COLOR ST77XX_RED
#define ST77XX_DARKGREY 0xAD55

// 温湿度数据结构
struct SHT40_Data {
  float temperature;
  float humidity;
  bool success;
};

// SHT40命令定义
#define SHT40_MEASURE_HIGH_PRECISION 0xFD
#define SHT40_SOFT_RESET 0x94
#define SHT40_READ_SERIAL 0x89  // 读取序列号命令

// 已知序列号对应的型号
#define SHT41_SERIAL 0x1513FB18
#define SHT40_SERIAL 0xF6C5E9F

// 缓存上一次成功读取的数据
SHT40_Data cachedData = {0, 0, false};
String sensorModel = "SHT4x"; // 默认型号
uint32_t sensorSerial = 0;    // 传感器序列号

// CRC8校验函数
uint8_t sht40_crc8(const uint8_t *data, uint8_t len) {
  uint8_t crc = 0xFF;
  for (uint8_t i = 0; i < len; i++) {
    crc ^= data[i];
    for (uint8_t bit = 0; bit < 8; bit++) {
      if (crc & 0x80) {
        crc = (crc << 1) ^ 0x31;
      } else {
        crc = (crc << 1);
      }
    }
  }
  return crc;
}

// 读取传感器序列号
bool sht40_read_serial() {
  uint8_t rx_bytes[6] = {0};
  
  // 发送读取序列号命令
  sht40Wire.beginTransmission(SHT40_ADDRESS);
  sht40Wire.write(SHT40_READ_SERIAL);
  if (sht40Wire.endTransmission() != 0) {
    return false;
  }
  
  delay(10);
  
  // 读取6字节数据
  uint8_t bytes_read = sht40Wire.requestFrom(SHT40_ADDRESS, 6);
  if (bytes_read != 6) {
    return false;
  }
  
  for (int i = 0; i < 6; i++) {
    rx_bytes[i] = sht40Wire.read();
  }
  
  // CRC校验
  if ((sht40_crc8(rx_bytes, 2) == rx_bytes[2]) && 
      (sht40_crc8(&rx_bytes[3], 2) == rx_bytes[5])) {
    
    // 提取序列号
    sensorSerial = (rx_bytes[0] << 24) | (rx_bytes[1] << 16) | 
                   (rx_bytes[3] << 8) | rx_bytes[4];
    
    // 根据序列号判断型号
    if (sensorSerial == SHT41_SERIAL) {
      sensorModel = "SHT41";
    } else if (sensorSerial == SHT40_SERIAL) {
      sensorModel = "SHT40";
    } else {
      sensorModel = "SHT4x"; // 未知序列号,统一设置为SHT4x
    }
    
    return true;
  }
  
  return false;
}

// SHT40初始化函数
bool sht40_init() {
  sht40Wire.begin();
  sht40Wire.setClock(100000);
  
  // 发送软复位命令
  sht40Wire.beginTransmission(SHT40_ADDRESS);
  sht40Wire.write(SHT40_SOFT_RESET);
  if (sht40Wire.endTransmission() != 0) {
    return false;
  }
  
  delay(10);
  
  // 读取序列号
  if (!sht40_read_serial()) {
    sensorModel = "SHT4x"; // 如果读取失败,使用默认型号
  }
  
  return true;
}

// 读取SHT40数据
SHT40_Data sht40_read_data() {
  SHT40_Data data = {0, 0, false};
  uint8_t rx_bytes[6] = {0};
  
  // 发送高精度测量命令
  sht40Wire.beginTransmission(SHT40_ADDRESS);
  sht40Wire.write(SHT40_MEASURE_HIGH_PRECISION);
  if (sht40Wire.endTransmission() != 0) {
    data.success = false;
    return data;
  }
  
  delay(10); // 高精度模式测量时间约10ms
  
  // 读取6字节数据
  uint8_t bytes_read = sht40Wire.requestFrom(SHT40_ADDRESS, 6);
  if (bytes_read != 6) {
    data.success = false;
    return data;
  }
  
  for (int i = 0; i < 6; i++) {
    rx_bytes[i] = sht40Wire.read();
  }
  
  // CRC校验
  if ((sht40_crc8(rx_bytes, 2) == rx_bytes[2]) && 
      (sht40_crc8(&rx_bytes[3], 2) == rx_bytes[5])) {
    
    uint16_t t_ticks = (rx_bytes[0] << 8) | rx_bytes[1];
    uint16_t rh_ticks = (rx_bytes[3] << 8) | rx_bytes[4];
    
    data.temperature = -45.0 + 175.0 * (float)t_ticks / 65535.0;
    data.humidity = -6.0 + 125.0 * (float)rh_ticks / 65535.0;
    
    if (data.humidity > 100.0) data.humidity = 100.0;
    if (data.humidity < 0.0) data.humidity = 0.0;
    
    data.success = true;
  } else {
    data.success = false;
  }
  
  return data;
}

// 中断服务函数
void buttonISR() {
  if (millis() - lastButtonPress > BUTTON_COOLDOWN) {
    currentScreen = (currentScreen + 1) % 4;
    screenChanged = true;
    lastButtonPress = millis();
  }
}

void setup() {
  Serial.begin(115200);
  
  // 初始化按键中断
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
  
  // 初始化显示屏
  tft.init(240, 240);
  tft.setRotation(1);
  tft.fillScreen(BACKGROUND_COLOR);
  
  // 显示启动画面
  showSplashScreen();
  delay(2000);
  
  // 初始化传感器
  if (!sht40_init()) {
    showError("SHT4x not found!");
    while (1) delay(1000);
  }
  Serial.println("Found SHT4x sensor");
  Serial.print("Model: ");
  Serial.println(sensorModel);
  Serial.print("Serial: 0x");
  Serial.println(sensorSerial, HEX);
  
  // 显示初始化信息
  showInitInfo();
  delay(3000);
  
  // 清屏准备主界面
  tft.fillScreen(BACKGROUND_COLOR);
}

void loop() {
  unsigned long currentTime = millis();
  
  // 定时读取传感器数据
  if (currentTime - lastSensorRead >= SENSOR_READ_INTERVAL) {
    SHT40_Data sensor_data = sht40_read_data();
    
    if (sensor_data.success) {
      cachedData = sensor_data; // 缓存成功读取的数据
      
      // 串口打印数据
      static unsigned long lastSerialPrint = 0;
      if (currentTime - lastSerialPrint >= 1000) {
        Serial.print("Temp:");
        Serial.print(sensor_data.temperature, 2);
        Serial.print("℃ Humi:");
        Serial.print(sensor_data.humidity, 2);
        Serial.println(" %RH");
        lastSerialPrint = currentTime;
      }
    } else {
      Serial.println("Read SHT4x failed!");
    }
    lastSensorRead = currentTime;
  }
  
  // 检查是否需要切换界面
  if (screenChanged) {
    tft.fillScreen(BACKGROUND_COLOR);
    screenChanged = false;
    lastDisplayUpdate = 0; // 强制立即更新显示
  }
  
  // 定时更新显示
  if (currentTime - lastDisplayUpdate >= DISPLAY_UPDATE_INTERVAL) {
    // 根据当前界面显示相应内容
    switch (currentScreen) {
      case 0:
        showTemperatureScreen(cachedData);
        break;
      case 1:
        showHumidityScreen(cachedData);
        break;
      case 2:
        showVPDScreen(cachedData);
        break;
      case 3:
        showInfoScreen(cachedData);
        break;
    }
    lastDisplayUpdate = currentTime;
  }
}

void showSplashScreen() {
  tft.fillScreen(BACKGROUND_COLOR);
  
  // 标题
  tft.setFont(&FreeSansBold18pt7b);
  tft.setTextColor(PRIMARY_COLOR);
  tft.setCursor(40, 80);
  tft.print("SHT4x");
  
  tft.setFont(&FreeSansBold12pt7b);
  tft.setCursor(50, 120);
  tft.print("Sensor");
  tft.setCursor(60, 150);
  tft.print("Monitor");
}

void showInitInfo() {
  tft.fillScreen(BACKGROUND_COLOR);
  
  tft.setFont(&FreeSansBold12pt7b);
  tft.setTextColor(PRIMARY_COLOR);
  tft.setCursor(20, 40);
  tft.print("Initialization");
  
  tft.drawLine(20, 50, 220, 50, PRIMARY_COLOR);
  
  tft.setFont(&FreeSans9pt7b);
  tft.setTextColor(SECONDARY_COLOR);
  
  // 显示传感器型号
  tft.setCursor(20, 80);
  tft.print("Model: ");
  tft.setTextColor(PRIMARY_COLOR);
  tft.print(sensorModel);
  
  // 显示软件I2C地址
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 105);
  tft.print("Address: 0x");
  tft.setTextColor(PRIMARY_COLOR);
  tft.print(SHT40_ADDRESS, HEX);
  
  // 显示引脚信息
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 130);
  tft.print("SCL Pin: ");
  tft.setTextColor(PRIMARY_COLOR);
  tft.print(SCL_PIN);
  
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 155);
  tft.print("SDA Pin: ");
  tft.setTextColor(PRIMARY_COLOR);
  tft.print(SDA_PIN);
  
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 180);
  tft.print("Mode: High Precision");
  
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 200);
  tft.print("Press button to start");
}

void showTemperatureScreen(SHT40_Data data) {
  if (!data.success) {
    showError("Sensor Error!");
    return;
  }
  
  // 绘制标题
  tft.setFont(&FreeSansBold12pt7b);
  tft.setTextColor(TEMP_COLOR);
  tft.setCursor(50, 30);
  tft.print("Temperature");
  
  // 绘制温度圆环 (0-50°C范围)
  int centerX = 120;
  int centerY = 110;
  int outerRadius = 70;
  int innerRadius = 50;
  
  // 绘制背景圆环
  drawRing(centerX, centerY, innerRadius, outerRadius, 0, 360, ST77XX_DARKGREY);
  
  // 绘制温度填充圆环
  float tempPercent = constrain(data.temperature, 0, 50) / 50.0;
  int endAngle = 360 * tempPercent;
  drawRing(centerX, centerY, innerRadius, outerRadius, 0, endAngle, TEMP_COLOR);
  
  // 绘制中心数值
  tft.setFont(&FreeSansBold18pt7b);
  tft.setTextColor(TEMP_COLOR);
  
  char tempStr[8];
  dtostrf(data.temperature, 5, 1, tempStr);
  
  // 清除中心区域
  tft.fillCircle(centerX, centerY, innerRadius - 5, BACKGROUND_COLOR);
  
  // 显示温度值
  tft.setCursor(centerX - 50, centerY + 10);
  tft.print(tempStr);
  
  tft.setFont(&FreeSans9pt7b);
  tft.setCursor(centerX + 30, centerY + 15);
  tft.print("C");
  
  // 显示底部状态
  tft.setFont(&FreeSans9pt7b);
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(60, 220);
  tft.print("Screen: 1/4");
}

void showHumidityScreen(SHT40_Data data) {
  if (!data.success) {
    showError("Sensor Error!");
    return;
  }
  
  // 绘制标题
  tft.setFont(&FreeSansBold12pt7b);
  tft.setTextColor(HUMIDITY_COLOR);
  tft.setCursor(65, 30);
  tft.print("Humidity");
  
  // 绘制湿度圆环 (0-100%范围)
  int centerX = 120;
  int centerY = 110;
  int outerRadius = 70;
  int innerRadius = 50;
  
  // 绘制背景圆环
  drawRing(centerX, centerY, innerRadius, outerRadius, 0, 360, ST77XX_DARKGREY);
  
  // 绘制湿度填充圆环
  float humidityPercent = constrain(data.humidity, 0, 100) / 100.0;
  int endAngle = 360 * humidityPercent;
  drawRing(centerX, centerY, innerRadius, outerRadius, 0, endAngle, HUMIDITY_COLOR);
  
  // 绘制中心数值
  tft.setFont(&FreeSansBold18pt7b);
  tft.setTextColor(HUMIDITY_COLOR);
  
  char humidityStr[8];
  dtostrf(data.humidity, 5, 1, humidityStr);
  
  // 清除中心区域
  tft.fillCircle(centerX, centerY, innerRadius - 5, BACKGROUND_COLOR);
  
  // 显示湿度值
  tft.setCursor(centerX - 50, centerY + 10);
  tft.print(humidityStr);
  
  tft.setFont(&FreeSans9pt7b);
  tft.setCursor(centerX + 25, centerY + 10);
  tft.print("%");
  
  // 显示底部状态
  tft.setFont(&FreeSans9pt7b);
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(60, 220);
  tft.print("Screen: 2/4");
}

void showVPDScreen(SHT40_Data data) {
  if (!data.success) {
    showError("Sensor Error!");
    return;
  }
  
  // 计算VPD
  float svp = 0.6108 * exp(17.27 * data.temperature / (data.temperature + 237.3));
  float avp = data.humidity / 100 * svp;
  float vpd = svp - avp;
  
  // 绘制标题
  tft.setFont(&FreeSansBold12pt7b);
  tft.setTextColor(VPD_COLOR);
  tft.setCursor(95, 30);
  tft.print("VPD");
  
  // 绘制VPD仪表
  int centerX = 120;
  int centerY = 110;
  int radius = 60;
  int innerRadius = 50;
  
  // 绘制仪表背景
  tft.drawCircle(centerX, centerY, radius, ST77XX_DARKGREY);
  tft.drawCircle(centerX, centerY, radius + 1, ST77XX_DARKGREY);
  
  // 根据VPD值选择颜色 (正常范围: 0.8-1.2 kPa)
  uint16_t vpdColor;
  if (vpd >= 0.8 && vpd <= 1.2) {
    vpdColor = VPD_COLOR;
  } else {
    vpdColor = WARNING_COLOR;
  }
  
  // 绘制VPD值弧线
  float vpdMapped = constrain(vpd, 0, 2.0) / 2.0; // 映射到0-2kPa范围
  int vpdAngle = 270 * vpdMapped; // 270度弧线
  
  for (int i = 0; i <= vpdAngle; i += 5) {
    float angle = i * PI / 180;
    int x1 = centerX + (radius - 10) * cos(angle);
    int y1 = centerY + (radius - 10) * sin(angle);
    int x2 = centerX + radius * cos(angle);
    int y2 = centerY + radius * sin(angle);
    tft.drawLine(x1, y1, x2, y2, vpdColor);
  }

  // 清除中心区域
  tft.fillCircle(centerX, centerY, innerRadius - 5, BACKGROUND_COLOR);
  
  // 显示VPD数值
  tft.setFont(&FreeSansBold18pt7b);
  tft.setTextColor(vpdColor);
  
  char vpdStr[8];
  dtostrf(vpd, 4, 2, vpdStr);
  
  tft.setCursor(centerX - 40, centerY + 10);
  tft.print(vpdStr);
  tft.setFont(&FreeSans9pt7b);
  tft.setCursor(centerX + 5, centerY + 30);
  tft.print("kPa");

  // 清除状态显示
  tft.fillRect(30, 180, 210, 20, BACKGROUND_COLOR);
  
  // 显示VPD状态
  tft.setFont(&FreeSans9pt7b);
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(30, 195);
  
  if (vpd < 0.8) {
    tft.print("Low VPD - Risk of mold");
  } else if (vpd > 1.2) {
    tft.print("High VPD - Plant stress");
  } else {
    tft.print("Optimal VPD range");
  }
  
  // 显示底部状态
  tft.setCursor(60, 220);
  tft.print("Screen: 3/4");
}

void showInfoScreen(SHT40_Data data) {
  float vpd = 0;
  if (data.success) {
    float svp = 0.6108 * exp(17.27 * data.temperature / (data.temperature + 237.3));
    float avp = data.humidity / 100 * svp;
    vpd = svp - avp;
  }
  
  // 绘制标题
  tft.setFont(&FreeSansBold12pt7b);
  tft.setTextColor(PRIMARY_COLOR);
  tft.setCursor(50, 30);
  tft.print("Sensor Info");
  
  tft.drawLine(20, 45, 220, 45, PRIMARY_COLOR);
  
  tft.setFont(&FreeSans9pt7b);
  
  // 显示传感器型号
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 70);
  tft.print("Model: ");
  tft.setTextColor(PRIMARY_COLOR);
  tft.setCursor(100, 70);
  tft.print(sensorModel);
  
  // 显示软件I2C地址
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 95);
  tft.print("Address: ");
  tft.setTextColor(PRIMARY_COLOR);
  tft.setCursor(100, 95);
  tft.print("0x");
  tft.print(SHT40_ADDRESS, HEX);
  
  // 显示引脚信息
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 120);
  tft.print("SCL Pin: ");
  tft.setTextColor(PRIMARY_COLOR);
  tft.setCursor(100, 120);
  tft.print(SCL_PIN);
  
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 145);
  tft.print("SDA Pin: ");
  tft.setTextColor(PRIMARY_COLOR);
  tft.setCursor(100, 145);
  tft.print(SDA_PIN);
  
  // 显示当前读数
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 170);
  tft.print("Temperature:");
  tft.fillRect(140, 150, 80, 20, BACKGROUND_COLOR);
  if (data.success) {
    tft.setTextColor(TEMP_COLOR);
    tft.setCursor(140, 170);
    tft.print(data.temperature, 1);
    tft.print(" C");
  } else {
    tft.setTextColor(WARNING_COLOR);
    tft.setCursor(140, 170);
    tft.print("Error");
  }
  
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 195);
  tft.print("Humidity:");
  tft.fillRect(140, 175, 80, 20, BACKGROUND_COLOR);
  if (data.success) {
    tft.setTextColor(HUMIDITY_COLOR);
    tft.setCursor(140, 195);
    tft.print(data.humidity, 1);
    tft.print(" %");
  } else {
    tft.setTextColor(WARNING_COLOR);
    tft.setCursor(140, 195);
    tft.print("Error");
  }
  
  // 显示底部状态
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(60, 220);
  tft.print("Screen: 4/4");
}

void drawRing(int x, int y, int innerRadius, int outerRadius, int startAngle, int endAngle, uint16_t color) {
  for (int r = innerRadius; r <= outerRadius; r++) {
    drawArc(x, y, r, startAngle, endAngle, color);
  }
}

void drawArc(int x, int y, int radius, int startAngle, int endAngle, uint16_t color) {
  for (int i = startAngle; i <= endAngle; i++) {
    float angle = i * PI / 180;
    int xp = x + radius * cos(angle);
    int yp = y + radius * sin(angle);
    tft.drawPixel(xp, yp, color);
  }
}

void showError(const char* message) {
  tft.fillScreen(BACKGROUND_COLOR);
  tft.setFont(&FreeSansBold12pt7b);
  tft.setTextColor(WARNING_COLOR);
  tft.setCursor(40, 100);
  tft.print("ERROR");
  
  tft.setFont(&FreeSans9pt7b);
  tft.setTextColor(SECONDARY_COLOR);
  tft.setCursor(20, 140);
  tft.print(message);
}

软件I2C库技术

        1)SoftWire库核心特性:纯软件实现,不依赖硬件I2C外设;支持标准模式(100kHz)和快速模式(400kHz);开漏输出模拟

        2)通信时序关键点:

// 起始条件:SCL高电平时SDA下降沿
void i2c_start() {
    set_sda(LOW);
    set_scl(LOW);
}

// 停止条件:SCL高电平时SDA上升沿  
void i2c_stop() {
    set_sda(LOW);
    set_scl(HIGH);
    set_sda(HIGH);
}

软件I2C起始和停止调节

CRC8校验算法解析

        CRC校验原理:

        0x31,对应 x⁸ + x⁵ + x⁴ + 1;

        0xFF,确保全零数据也有非零校验和

uint8_t sht40_crc8(const uint8_t *data, uint8_t len) {
  uint8_t crc = 0xFF;  // 初始值
  for (uint8_t i = 0; i < len; i++) {
    crc ^= data[i];    // 异或操作
    for (uint8_t bit = 0; bit < 8; bit++) {
      if (crc & 0x80) {
        crc = (crc << 1) ^ 0x31;  // 多项式 0x31 (x^8 + x^5 + x^4 + 1)
      } else {
        crc = (crc << 1);
      }
    }
  }
  return crc;
}
名称生成多项式简记式
CRC-4x4+x+10x03
CRC-8x8+x5+x4+10x31
CRC-8x8+x2+x1+1 0x07
CRC-16x16+x15+x2+1 0x8005
CRC-32x32+x26+x23+...+x2+x+10x04C11DB7

        选定生成多项式→准备数据→模二除法→附加校验码

三、SHT40传感器技术原理

        SHT40采用Sensirion先进的CMOSens®技术,将温湿度传感元件、信号放大、模数转换、数字处理和I2C接口集成在1.5×1.5mm的DFN封装内

 3.1 温湿度测量原理

        SHT4X采用带隙温度传感器原理:VBE = VG0 - (kT/q) × ln(ATγ/IC)

        VG0:外推带隙电压、k:玻尔兹曼常数、T:绝对温度、q:电子电荷

        利用半导体PN结的正向电压与温度的线性关系、内置两个不同电流偏置的晶体管,测量其基极-发射极电压差、通过ΔVBE电压与绝对温度成正比的关系计算温度值

        湿度测量基于电容式聚合物传感器:C = ε₀εr(A/d)

        εr:聚合物相对介电常数(湿度函数)、A:电极面积、d:聚合物厚度

 3.2 I2C通信协议详解

        SHT4X使用标准的I2C通信协议,设备地址为0x44(7位地址)

通信时序

        a. 启动条件 → 设备地址(写) → 命令字节 → 停止条件
        b. 启动条件 → 设备地址(读) → 数据字节1 → ACK → 数据字节2 → NACK → 停止条件

重要命令集

        0xFD → 高精度温度湿度测量、0x89 → 读取32位唯一序列号、0x94 → 软件复位传感器

数据格式

        16位温度和湿度原始值,通过公式转换为实际温湿度值

CRC检验和

3.3 寄存器配置详解

        SHT4X通过单次命令进行操作,无需寄存器配置,但支持多种测量模式:

命令精度模式测量时间温度精度湿度精度
0xFD高精度10ms±0.2℃±2%RH
0xF6中精度5ms±0.3℃±3%RH
0xE0低精度2ms±0.5℃±4%RH

四、项目结果演示

4.1 硬件SPI性能测试

        使用逻辑分析仪捕捉SPI时序,SPI时钟速率高,温度测量响应时间<1秒

4.2 界面展示详情

        >界面1 温度显示,环形温度计平滑动画显示、实时温度数值居中显示,刷新无闪烁

        >界面2 湿度显示,环形湿度计动态渐变效果、湿度百分比精确显示,刷新流畅

        >界面3 VPD显示,弧形刻度仪表平滑旋转动画、颜色编码状态指示,切换无卡顿

         >界面4 传感器信息,设备型号和地址显示、实时数据监控,刷新迅速、系统状态汇总

4.3 视频操作演示

零知标准版驱动SHT40温湿度传感器

系统在实际环境中的温度变化时的实时响应、不同湿度条件下的显示效果,以及VPD状态改变

五、常见问题解答(FAQ)

Q1:传感器读数一直显示"Sensor Error"怎么办?

A:按以下步骤排查:

        检查电源电压是否为3.3V;确认SCL(A5)、SDA(A4)引脚连接正确;使用逻辑分析仪检查I2C通信波形;尝试降低I2C通信频率

Q2:VPD计算结果异常

A: 排查步骤:

        验证温湿度传感器数据准确性、检查计算公式参数是否正确、确认温湿度单位换算无误、验证环境条件在传感器量程内

Q3:如何进一步提升系统性能?

A:高级优化策略:

        SPI双缓冲:实现显示数据的双缓冲机制、传感器轮询优化:采用事件驱动代替定时轮询、内存优化:使用PROGMEM存储字体数据

 项目资源

        软件I2C 驱动库:SoftWire_Library

        SHT40 库文件: Adafruit_SHT4x_Library

        SHT40 数据手册: Datasheet_SHT4x

<think>嗯,用户这次的问题是关于NANOF103开发板(使用RBT6芯片)在温湿度监控系统中的适用性。用户似乎正在评估硬件选型方案,可能是个嵌入式工程师或物联网项目开发者。注意到用户引用了两份材料:RT-ThreadNano移植文档OneOS介绍,但实际需求更聚焦在具体开发板的适配性。用户上次的问题未被记录,所以需要完全基于当前上下文分析。从技术角度看,这个问题需要拆解三个层面:首先是MCU基础性能(STM32F103RBT6),然后是操作系统支持(RT-ThreadNano),最后是传感器接口可行性。让我逐一核验关键参数:1)温湿度监控典型需求:DHT22/BME280等传感器常用单总线或I2C协议,数据量极小(<1kbps)。RBT664KFLASH20KRAM对于基础监控逻辑绰绰有余——即使加上RTOS开销(Nano内核<3KB),内存余量仍超过50%。2)通信接口匹配性值得强调:板载USART可直接转485总线,片上I2C正好对接多数数字温湿度计。不过需要提醒用户注意引脚复用——如果同时需要显示模块,可能要用软件模拟I2C。3)操作系统层面很有趣:用户引用的移植文档显示该板已适配RT-ThreadNano,这意味着可以直接使用其传感器驱动框架。文档里提到FinSH组件特别实用,能远程查看实时数据,这对调试很有帮助。突然想到个潜在风险:RBT6的-40℃~+85℃工业级温度范围对温控系统足够,但如果部署环境存在冷凝水,可能需要补充说明硬件防护措施。在回复中应该暗示这个设计要点。用户可能还隐含对比需求,所以补充了OneOS方案作为可选项——它自带传感器管理模块,但需要评估8KB内存占用是否可接受。有趣的是中国移动这个系统居然支持MicroPython,对快速原型开发很有利。最后组织回复结构时,故意把适用性结论放在开头(用户最关心的答案),技术细节展开在后。特别标出硬件扩展方案,因为用户提到“方案”这个词,暗示可能需要系统级设计参考。</think> 基于STM32F103RBT6(以下简称RBT6)的NANO F103开发板**完全适用于温湿度监控系统方案**,以下是详细分析技术适配说明: --- ### 一、核心硬件适配性 1. **MCU性能** - RBT6芯片(Cortex-M3内核,72MHz主频,64KB Flash,20KB RAM)**足以处理**温湿度数据的采集、存储传输。 - 典型温湿度传感器(如DHT11/SHT30)数据量极小(单次读取约4~6字节),系统资源占用率低于15%。 2. **外设接口支持** - **传感器接口**:板载GPIO支持单总线(DHT系列)、I²C(SHT30/BME280)等常用协议。 - **通信能力**:USART可接WiFi/4G模块(如ESP8266/A7670C)实现云端上传;SPI支持本地SD卡存储数据。 - **功耗控制**:RBT6支持休眠模式(最低功耗2μA),适合电池供电场景。 --- ### 二、操作系统适配(RT-Thread Nano) 1. **实时性保障** RT-Thread Nano(内核仅2.7KB ROM占用)提供任务调度、定时器管理等功能,满足数据**定时采集需求**(例如每10秒采样1次)[^1]。 2. **开发便捷性** - 基于STM32CubeMX配置(引用[^1]方)可快速完成: ```c // 示例:配置I²C驱动温湿度传感器 SHT30_Init(); // 传感器初始化 while(1) { float temp = SHT30_Read_Temp(); // 读取温度 float humi = SHT30_Read_Humi(); // 读取湿度 rt_thread_mdelay(10000); // 10秒间隔 } ``` - 通过FinSH组件实现**命令行调试**,实时查看传感器数据[^1]。 3. **资源优化** 最小配置下系统仅占用5KB RAM,剩余资源可扩展警报、LCD显示等功能。 --- ### 三、系统方案设计 | 模块 | 实现方案 | |---------------|--------------------------------------------------------------------------| | **传感器层** | DHT22(单总线)或SHT35(I²C)<br>精度±0.3℃/±2%RH | | **数据传输** | WiFi方案:ESP-01S(AT指令+USART)<br>4G方案:移远EC200N(支持MQTT协议) | | **云端平台** | 阿里云IoT/AWS IoT,通过MQTT上传数据 | | **本地扩展** | 0.96寸OLED(I²C接口)实时显示,蜂鸣器超阈值报警 | > ⚡ **低功耗方案建议**: > 使用RTC唤醒(采样间隔>1分钟) + 传感器轮询供电,可使系统待机电流<1mA。 --- ### 四、对比替代方案(OneOS) 若需更强功能(如远程OTA更新、安全加密),可考虑中国移动OneOS[^2]: ```markdown 优势: - 内置传感器管理框架(简化驱动开发) - 支持TCP/IP协议栈(无需AT指令) - 图形化IDE OneStudio提升效率 劣势: - 内存占用略高(>10KB RAM),对RBT6资源较紧张 ``` --- ### 五、结论 **NANO F103开发板+RBT6芯片是温湿度监控系统的理想选择**: 1. ✅ 硬件接口匹配传感器与通信模块 2. ✅ RT-Thread Nano满足实时性与低资源需求 3. ✅ 开发周期短(基于CubeMX移植仅需1-2天[^1]) 4. ⚠️ 如需企业级功能(安全/远程管理),建议评估OneOS方案[^2] 建议优先采用 **"RBT6 + RT-Thread Nano + SHT35 + ESP-01S"组合**,系统成本可控制在¥80以内。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值