跳转至

温湿度传感器应用开发

概述

温湿度传感器是嵌入式系统中最常用的环境监测传感器之一,广泛应用于智能家居、气象站、农业监测、工业控制等领域。本教程将介绍常用的温湿度传感器(DHT11/DHT22和SHT3x系列),讲解其工作原理、硬件连接、软件驱动开发和实际应用。

为什么需要温湿度传感器

  1. 环境监测
  2. 室内空气质量监测
  3. 气象数据采集
  4. 温室大棚监控
  5. 仓储环境管理

  6. 设备保护

  7. 防止设备过热
  8. 避免结露损坏
  9. 优化工作环境
  10. 延长设备寿命

  11. 舒适度控制

  12. 智能空调控制
  13. 加湿器自动调节
  14. 通风系统优化
  15. 节能管理

  16. 工业应用

  17. 生产过程监控
  18. 产品质量控制
  19. 安全预警
  20. 数据记录

常用温湿度传感器对比

传感器类型对比:

┌─────────┬──────────┬──────────┬──────────┬──────────┐
│ 参数    │ DHT11    │ DHT22    │ SHT30    │ SHT31    │
├─────────┼──────────┼──────────┼──────────┼──────────┤
│ 温度范围│ 0-50°C   │ -40-80°C │ -40-125°C│ -40-125°C│
│ 温度精度│ ±2°C     │ ±0.5°C   │ ±0.3°C   │ ±0.2°C   │
│ 湿度范围│ 20-90%RH │ 0-100%RH │ 0-100%RH │ 0-100%RH │
│ 湿度精度│ ±5%RH    │ ±2%RH    │ ±2%RH    │ ±1.5%RH  │
│ 接口    │ 单总线   │ 单总线   │ I2C      │ I2C      │
│ 响应时间│ 6-15s    │ 2s       │ <8s      │ <8s      │
│ 价格    │ 低       │ 中       │ 中       │ 高       │
│ 适用场景│ 入门学习 │ 一般应用 │ 精密测量 │ 高精度   │
└─────────┴──────────┴──────────┴──────────┴──────────┘

选型建议:
- 学习入门:DHT11(便宜、简单)
- 一般应用:DHT22(性价比高)
- 精密测量:SHT30/SHT31(高精度、稳定)
- 工业应用:SHT31(最高精度、可靠性好)

第一部分:DHT11/DHT22传感器

硬件连接

DHT11/DHT22引脚定义

DHT传感器引脚(4针封装):

    ┌─────────┐
    │  DHT11  │
    │  DHT22  │
    └─┬─┬─┬─┬─┘
      1 2 3 4

引脚说明:
1. VCC  - 电源正极(3.3V-5V)
2. DATA - 数据线(单总线)
3. NC   - 不连接
4. GND  - 电源地

注意:3针封装没有NC引脚

硬件连接电路

连接方案:

MCU                    DHT传感器
                      ┌─────────┐
3.3V/5V ──────────────┤1 VCC    │
                      │         │
GPIO ─────┬───────────┤2 DATA   │
          │           │         │
         ┌┴┐          │3 NC     │
    4.7k │ │          │         │
         └┬┘          │4 GND    │
          │           └─────────┘
GND ──────┴───────────────┘

关键要点:
1. DATA引脚需要上拉电阻(4.7kΩ)
2. 电源电压:DHT11支持3.3V-5V,DHT22支持3.3V-5.5V
3. 数据线长度不宜超过20米
4. 建议在VCC和GND之间加0.1μF去耦电容

通信协议

单总线协议时序

DHT单总线通信协议:

1. 起始信号(主机发送):
   主机拉低DATA线至少18ms,然后释放

   ────┐                    ┌────
       └────────────────────┘
       <------- >18ms ------>

2. 响应信号(DHT发送):
   DHT拉低80μs,然后拉高80μs

   ────┐        ┌────────────
       └────────┘
       <-80μs-><---80μs--->

3. 数据传输(DHT发送40位数据):
   每位数据:先拉低50μs,然后拉高
   - 数据0:拉高26-28μs
   - 数据1:拉高70μs

   数据0:
   ────┐    ┌──────
       └────┘
       50μs 26-28μs

   数据1:
   ────┐    ┌──────────────
       └────┘
       50μs    70μs

4. 数据格式(40位):
   [湿度整数][湿度小数][温度整数][温度小数][校验和]
   每部分8位,共40位

   DHT11:小数部分为0
   DHT22:小数部分有效

5. 校验和计算:
   校验和 = (湿度整数 + 湿度小数 + 温度整数 + 温度小数) & 0xFF

DHT驱动代码实现

驱动头文件

/**
 * @file    dht.h
 * @brief   DHT11/DHT22温湿度传感器驱动
 * @author  嵌入式知识平台
 */

#ifndef __DHT_H
#define __DHT_H

#include "stm32f1xx_hal.h"
#include <stdbool.h>

/**
 * @brief  DHT传感器类型
 */
typedef enum {
    DHT_TYPE_DHT11 = 0,    // DHT11传感器
    DHT_TYPE_DHT22 = 1     // DHT22传感器(AM2302)
} DHT_Type;

/**
 * @brief  DHT数据结构
 */
typedef struct {
    float temperature;      // 温度(°C)
    float humidity;         // 湿度(%RH)
    bool valid;            // 数据有效标志
} DHT_Data;

/**
 * @brief  DHT传感器配置
 */
typedef struct {
    GPIO_TypeDef* gpio_port;    // GPIO端口
    uint16_t gpio_pin;          // GPIO引脚
    DHT_Type type;              // 传感器类型
} DHT_Config;

// 函数声明
void DHT_Init(DHT_Config* config);
bool DHT_Read(DHT_Config* config, DHT_Data* data);
void DHT_Delay_us(uint32_t us);

#endif /* __DHT_H */

驱动实现文件

/**
 * @file    dht.c
 * @brief   DHT11/DHT22温湿度传感器驱动实现
 */

#include "dht.h"

// 私有函数声明
static void DHT_SetPinOutput(DHT_Config* config);
static void DHT_SetPinInput(DHT_Config* config);
static void DHT_WriteBit(DHT_Config* config, uint8_t bit);
static uint8_t DHT_ReadBit(DHT_Config* config);
static bool DHT_Start(DHT_Config* config);
static bool DHT_CheckResponse(DHT_Config* config);
static uint8_t DHT_ReadByte(DHT_Config* config);

/**
 * @brief  微秒级延时(使用DWT)
 * @param  us: 延时时间(微秒)
 */
void DHT_Delay_us(uint32_t us) {
    uint32_t start = DWT->CYCCNT;
    uint32_t cycles = us * (SystemCoreClock / 1000000);

    while ((DWT->CYCCNT - start) < cycles);
}

/**
 * @brief  初始化DHT传感器
 * @param  config: 传感器配置
 */
void DHT_Init(DHT_Config* config) {
    // 使能DWT计数器(用于微秒延时)
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    DWT->CYCCNT = 0;
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

    // 配置GPIO为输出模式
    DHT_SetPinOutput(config);

    // 初始状态拉高
    HAL_GPIO_WritePin(config->gpio_port, config->gpio_pin, GPIO_PIN_SET);

    // 等待传感器稳定
    HAL_Delay(1000);
}

/**
 * @brief  设置GPIO为输出模式
 */
static void DHT_SetPinOutput(DHT_Config* config) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = config->gpio_pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

    HAL_GPIO_Init(config->gpio_port, &GPIO_InitStruct);
}

/**
 * @brief  设置GPIO为输入模式
 */
static void DHT_SetPinInput(DHT_Config* config) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = config->gpio_pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;

    HAL_GPIO_Init(config->gpio_port, &GPIO_InitStruct);
}

/**
 * @brief  发送起始信号
 * @retval true: 成功, false: 失败
 */
static bool DHT_Start(DHT_Config* config) {
    // 设置为输出模式
    DHT_SetPinOutput(config);

    // 拉低至少18ms
    HAL_GPIO_WritePin(config->gpio_port, config->gpio_pin, GPIO_PIN_RESET);
    HAL_Delay(20);

    // 释放总线
    HAL_GPIO_WritePin(config->gpio_port, config->gpio_pin, GPIO_PIN_SET);
    DHT_Delay_us(30);

    // 切换为输入模式
    DHT_SetPinInput(config);

    return true;
}

/**
 * @brief  检查DHT响应
 * @retval true: 响应正常, false: 无响应
 */
static bool DHT_CheckResponse(DHT_Config* config) {
    uint8_t timeout = 0;

    // 等待DHT拉低(80μs)
    while (HAL_GPIO_ReadPin(config->gpio_port, config->gpio_pin) == GPIO_PIN_SET) {
        DHT_Delay_us(1);
        if (++timeout > 100) return false;
    }

    timeout = 0;
    // 等待DHT拉高(80μs)
    while (HAL_GPIO_ReadPin(config->gpio_port, config->gpio_pin) == GPIO_PIN_RESET) {
        DHT_Delay_us(1);
        if (++timeout > 100) return false;
    }

    timeout = 0;
    // 等待DHT准备发送数据
    while (HAL_GPIO_ReadPin(config->gpio_port, config->gpio_pin) == GPIO_PIN_SET) {
        DHT_Delay_us(1);
        if (++timeout > 100) return false;
    }

    return true;
}

/**
 * @brief  读取一个字节
 * @retval 读取的字节数据
 */
static uint8_t DHT_ReadByte(DHT_Config* config) {
    uint8_t byte = 0;

    for (int i = 0; i < 8; i++) {
        uint8_t timeout = 0;

        // 等待低电平结束(50μs)
        while (HAL_GPIO_ReadPin(config->gpio_port, config->gpio_pin) == GPIO_PIN_RESET) {
            DHT_Delay_us(1);
            if (++timeout > 100) return 0;
        }

        // 延时30μs后采样
        DHT_Delay_us(30);

        // 读取位值
        byte <<= 1;
        if (HAL_GPIO_ReadPin(config->gpio_port, config->gpio_pin) == GPIO_PIN_SET) {
            byte |= 0x01;
        }

        timeout = 0;
        // 等待高电平结束
        while (HAL_GPIO_ReadPin(config->gpio_port, config->gpio_pin) == GPIO_PIN_SET) {
            DHT_Delay_us(1);
            if (++timeout > 100) return 0;
        }
    }

    return byte;
}

/**
 * @brief  读取DHT传感器数据
 * @param  config: 传感器配置
 * @param  data: 数据存储结构
 * @retval true: 读取成功, false: 读取失败
 */
bool DHT_Read(DHT_Config* config, DHT_Data* data) {
    uint8_t buffer[5] = {0};

    // 发送起始信号
    if (!DHT_Start(config)) {
        data->valid = false;
        return false;
    }

    // 检查响应
    if (!DHT_CheckResponse(config)) {
        data->valid = false;
        return false;
    }

    // 读取40位数据(5字节)
    for (int i = 0; i < 5; i++) {
        buffer[i] = DHT_ReadByte(config);
    }

    // 切换回输出模式
    DHT_SetPinOutput(config);
    HAL_GPIO_WritePin(config->gpio_port, config->gpio_pin, GPIO_PIN_SET);

    // 校验和检查
    uint8_t checksum = (buffer[0] + buffer[1] + buffer[2] + buffer[3]) & 0xFF;
    if (checksum != buffer[4]) {
        data->valid = false;
        return false;
    }

    // 解析数据
    if (config->type == DHT_TYPE_DHT11) {
        // DHT11:整数部分有效
        data->humidity = (float)buffer[0];
        data->temperature = (float)buffer[2];
    } else {
        // DHT22:包含小数部分
        data->humidity = ((buffer[0] << 8) | buffer[1]) / 10.0f;

        // 处理负温度
        if (buffer[2] & 0x80) {
            data->temperature = -((((buffer[2] & 0x7F) << 8) | buffer[3]) / 10.0f);
        } else {
            data->temperature = (((buffer[2] << 8) | buffer[3]) / 10.0f);
        }
    }

    data->valid = true;
    return true;
}

DHT使用示例

/**
 * @file    main.c
 * @brief   DHT传感器使用示例
 */

#include "stm32f1xx_hal.h"
#include "dht.h"
#include <stdio.h>

// DHT传感器配置
DHT_Config dht_config = {
    .gpio_port = GPIOA,
    .gpio_pin = GPIO_PIN_0,
    .type = DHT_TYPE_DHT22    // 使用DHT22
};

/**
 * @brief  主函数
 */
int main(void) {
    // 系统初始化
    HAL_Init();
    SystemClock_Config();

    // 初始化UART(用于打印)
    MX_USART1_UART_Init();

    // 初始化DHT传感器
    DHT_Init(&dht_config);

    printf("DHT22温湿度传感器测试\n");
    printf("═══════════════════════════════════════\n\n");

    DHT_Data data;
    uint32_t read_count = 0;
    uint32_t error_count = 0;

    while (1) {
        // 读取传感器数据
        if (DHT_Read(&dht_config, &data)) {
            read_count++;

            // 打印数据
            printf("读取 #%lu:\n", read_count);
            printf("  温度: %.1f °C\n", data.temperature);
            printf("  湿度: %.1f %%RH\n", data.humidity);
            printf("  状态: 有效\n\n");

            // 数据范围检查
            if (data.temperature < -40 || data.temperature > 80) {
                printf("  ⚠ 警告:温度超出正常范围!\n");
            }
            if (data.humidity < 0 || data.humidity > 100) {
                printf("  ⚠ 警告:湿度超出正常范围!\n");
            }
        } else {
            error_count++;
            printf("读取失败 #%lu (总错误: %lu)\n\n", read_count + error_count, error_count);
        }

        // DHT22建议读取间隔至少2秒
        HAL_Delay(2000);
    }
}

/**
 * @brief  高级示例:数据平滑滤波
 */
void advanced_example_filtering(void) {
    #define FILTER_SIZE 5

    float temp_buffer[FILTER_SIZE] = {0};
    float humi_buffer[FILTER_SIZE] = {0};
    uint8_t buffer_index = 0;
    bool buffer_full = false;

    DHT_Data data;

    while (1) {
        if (DHT_Read(&dht_config, &data)) {
            // 存储数据到缓冲区
            temp_buffer[buffer_index] = data.temperature;
            humi_buffer[buffer_index] = data.humidity;

            buffer_index++;
            if (buffer_index >= FILTER_SIZE) {
                buffer_index = 0;
                buffer_full = true;
            }

            // 计算平均值(移动平均滤波)
            if (buffer_full) {
                float temp_avg = 0, humi_avg = 0;

                for (int i = 0; i < FILTER_SIZE; i++) {
                    temp_avg += temp_buffer[i];
                    humi_avg += humi_buffer[i];
                }

                temp_avg /= FILTER_SIZE;
                humi_avg /= FILTER_SIZE;

                printf("滤波后数据:\n");
                printf("  温度: %.1f °C (原始: %.1f °C)\n", 
                       temp_avg, data.temperature);
                printf("  湿度: %.1f %%RH (原始: %.1f %%RH)\n\n",
                       humi_avg, data.humidity);
            }
        }

        HAL_Delay(2000);
    }
}

/**
 * @brief  高级示例:温湿度报警
 */
void advanced_example_alarm(void) {
    // 报警阈值
    const float TEMP_HIGH = 30.0f;
    const float TEMP_LOW = 15.0f;
    const float HUMI_HIGH = 80.0f;
    const float HUMI_LOW = 30.0f;

    DHT_Data data;

    while (1) {
        if (DHT_Read(&dht_config, &data)) {
            printf("当前环境:\n");
            printf("  温度: %.1f °C\n", data.temperature);
            printf("  湿度: %.1f %%RH\n", data.humidity);

            // 温度报警
            if (data.temperature > TEMP_HIGH) {
                printf("  🔥 温度过高报警!\n");
                // 触发降温措施
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);  // 开启风扇
            } else if (data.temperature < TEMP_LOW) {
                printf("  ❄️  温度过低报警!\n");
                // 触发加热措施
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);  // 开启加热器
            } else {
                printf("  ✓ 温度正常\n");
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
            }

            // 湿度报警
            if (data.humidity > HUMI_HIGH) {
                printf("  💧 湿度过高报警!\n");
                // 触发除湿措施
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);  // 开启除湿器
            } else if (data.humidity < HUMI_LOW) {
                printf("  🏜️  湿度过低报警!\n");
                // 触发加湿措施
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);  // 开启加湿器
            } else {
                printf("  ✓ 湿度正常\n");
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
            }

            printf("\n");
        }

        HAL_Delay(2000);
    }
}

第二部分:SHT3x系列传感器

硬件连接

SHT3x引脚定义

SHT30/SHT31引脚(6针封装):

    ┌─────────┐
    │  SHT3x  │
    └─┬─┬─┬─┬─┬─┬─┘
      1 2 3 4 5 6

引脚说明:
1. VDD   - 电源正极(2.4V-5.5V)
2. SDA   - I2C数据线
3. SCL   - I2C时钟线
4. ADDR  - I2C地址选择(接GND或VDD)
5. ALERT - 报警输出(可选)
6. VSS   - 电源地

I2C地址:
- ADDR接GND: 0x44
- ADDR接VDD: 0x45

硬件连接电路

连接方案:

MCU                    SHT3x传感器
                      ┌─────────┐
3.3V ─────────────────┤1 VDD    │
                      │         │
SDA ──────┬───────────┤2 SDA    │
          │           │         │
         ┌┴┐          │3 SCL    │
    4.7k │ │          │         │
         └┬┘          │4 ADDR   │──┐
          │           │         │  │ 接GND或VDD
SCL ──────┼──┬────────┤5 ALERT  │  │ 选择地址
          │  │        │         │  │
         ┌┴┐ │        │6 VSS    │  │
    4.7k │ │ │        └─────────┘  │
         └┬┘ │                      │
          │  │                      │
GND ──────┴──┴──────────────────────┘

关键要点:
1. SDA和SCL需要上拉电阻(4.7kΩ)
2. 支持标准模式(100kHz)和快速模式(400kHz)
3. ADDR引脚选择I2C地址
4. ALERT引脚可用于温湿度报警(可选)

I2C通信协议

SHT3x命令集

常用命令:

1. 单次测量模式:
   - 高重复性:0x2C06
   - 中重复性:0x2C0D
   - 低重复性:0x2C10

2. 周期测量模式:
   - 0.5 mps, 高重复性:0x2032
   - 1 mps, 高重复性:0x2130
   - 2 mps, 高重复性:0x2236
   - 4 mps, 高重复性:0x2334
   - 10 mps, 高重复性:0x2737

3. 读取测量结果:
   - 读取数据:0xE000

4. 软复位:
   - 复位命令:0x30A2

5. 状态寄存器:
   - 读取状态:0xF32D
   - 清除状态:0x3041

数据格式:
[温度高字节][温度低字节][温度CRC][湿度高字节][湿度低字节][湿度CRC]

温度计算:
Temperature (°C) = -45 + 175 × (ST / 65535)

湿度计算:
Humidity (%RH) = 100 × (SRH / 65535)

SHT3x驱动代码实现

驱动头文件

/**
 * @file    sht3x.h
 * @brief   SHT30/SHT31温湿度传感器驱动
 * @author  嵌入式知识平台
 */

#ifndef __SHT3X_H
#define __SHT3X_H

#include "stm32f1xx_hal.h"
#include <stdbool.h>

/**
 * @brief  SHT3x I2C地址
 */
#define SHT3X_ADDR_LOW   0x44    // ADDR引脚接GND
#define SHT3X_ADDR_HIGH  0x45    // ADDR引脚接VDD

/**
 * @brief  SHT3x命令
 */
#define SHT3X_CMD_MEASURE_HIGH    0x2C06    // 单次测量,高重复性
#define SHT3X_CMD_MEASURE_MEDIUM  0x2C0D    // 单次测量,中重复性
#define SHT3X_CMD_MEASURE_LOW     0x2C10    // 单次测量,低重复性
#define SHT3X_CMD_READ_STATUS     0xF32D    // 读取状态寄存器
#define SHT3X_CMD_CLEAR_STATUS    0x3041    // 清除状态寄存器
#define SHT3X_CMD_SOFT_RESET      0x30A2    // 软复位
#define SHT3X_CMD_HEATER_ENABLE   0x306D    // 使能加热器
#define SHT3X_CMD_HEATER_DISABLE  0x3066    // 禁用加热器

/**
 * @brief  SHT3x数据结构
 */
typedef struct {
    float temperature;      // 温度(°C)
    float humidity;         // 湿度(%RH)
    bool valid;            // 数据有效标志
} SHT3X_Data;

/**
 * @brief  SHT3x配置
 */
typedef struct {
    I2C_HandleTypeDef* hi2c;    // I2C句柄
    uint8_t address;            // I2C地址
} SHT3X_Config;

// 函数声明
bool SHT3X_Init(SHT3X_Config* config);
bool SHT3X_Read(SHT3X_Config* config, SHT3X_Data* data);
bool SHT3X_SoftReset(SHT3X_Config* config);
bool SHT3X_ReadStatus(SHT3X_Config* config, uint16_t* status);
bool SHT3X_EnableHeater(SHT3X_Config* config, bool enable);

#endif /* __SHT3X_H */

驱动实现文件

/**
 * @file    sht3x.c
 * @brief   SHT30/SHT31温湿度传感器驱动实现
 */

#include "sht3x.h"

// 私有函数声明
static bool SHT3X_WriteCommand(SHT3X_Config* config, uint16_t cmd);
static uint8_t SHT3X_CalculateCRC(uint8_t* data, uint8_t length);
static bool SHT3X_CheckCRC(uint8_t* data, uint8_t length, uint8_t crc);

/**
 * @brief  计算CRC-8校验
 * @param  data: 数据指针
 * @param  length: 数据长度
 * @retval CRC值
 */
static uint8_t SHT3X_CalculateCRC(uint8_t* data, uint8_t length) {
    uint8_t crc = 0xFF;

    for (uint8_t i = 0; i < length; i++) {
        crc ^= data[i];

        for (uint8_t j = 0; j < 8; j++) {
            if (crc & 0x80) {
                crc = (crc << 1) ^ 0x31;
            } else {
                crc = crc << 1;
            }
        }
    }

    return crc;
}

/**
 * @brief  检查CRC校验
 * @param  data: 数据指针
 * @param  length: 数据长度
 * @param  crc: 接收到的CRC值
 * @retval true: 校验通过, false: 校验失败
 */
static bool SHT3X_CheckCRC(uint8_t* data, uint8_t length, uint8_t crc) {
    return (SHT3X_CalculateCRC(data, length) == crc);
}

/**
 * @brief  写入命令
 * @param  config: 传感器配置
 * @param  cmd: 命令
 * @retval true: 成功, false: 失败
 */
static bool SHT3X_WriteCommand(SHT3X_Config* config, uint16_t cmd) {
    uint8_t buffer[2];
    buffer[0] = (cmd >> 8) & 0xFF;
    buffer[1] = cmd & 0xFF;

    HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(
        config->hi2c,
        config->address << 1,
        buffer,
        2,
        HAL_MAX_DELAY
    );

    return (status == HAL_OK);
}

/**
 * @brief  初始化SHT3x传感器
 * @param  config: 传感器配置
 * @retval true: 成功, false: 失败
 */
bool SHT3X_Init(SHT3X_Config* config) {
    // 软复位
    if (!SHT3X_SoftReset(config)) {
        return false;
    }

    // 等待复位完成
    HAL_Delay(10);

    // 读取状态寄存器验证通信
    uint16_t status;
    if (!SHT3X_ReadStatus(config, &status)) {
        return false;
    }

    return true;
}

/**
 * @brief  软复位
 * @param  config: 传感器配置
 * @retval true: 成功, false: 失败
 */
bool SHT3X_SoftReset(SHT3X_Config* config) {
    return SHT3X_WriteCommand(config, SHT3X_CMD_SOFT_RESET);
}

/**
 * @brief  读取状态寄存器
 * @param  config: 传感器配置
 * @param  status: 状态值指针
 * @retval true: 成功, false: 失败
 */
bool SHT3X_ReadStatus(SHT3X_Config* config, uint16_t* status) {
    uint8_t buffer[3];

    // 发送读取状态命令
    if (!SHT3X_WriteCommand(config, SHT3X_CMD_READ_STATUS)) {
        return false;
    }

    // 读取状态数据
    HAL_StatusTypeDef hal_status = HAL_I2C_Master_Receive(
        config->hi2c,
        config->address << 1,
        buffer,
        3,
        HAL_MAX_DELAY
    );

    if (hal_status != HAL_OK) {
        return false;
    }

    // 检查CRC
    if (!SHT3X_CheckCRC(buffer, 2, buffer[2])) {
        return false;
    }

    *status = (buffer[0] << 8) | buffer[1];
    return true;
}

/**
 * @brief  控制加热器
 * @param  config: 传感器配置
 * @param  enable: true-使能, false-禁用
 * @retval true: 成功, false: 失败
 */
bool SHT3X_EnableHeater(SHT3X_Config* config, bool enable) {
    uint16_t cmd = enable ? SHT3X_CMD_HEATER_ENABLE : SHT3X_CMD_HEATER_DISABLE;
    return SHT3X_WriteCommand(config, cmd);
}

/**
 * @brief  读取温湿度数据
 * @param  config: 传感器配置
 * @param  data: 数据存储结构
 * @retval true: 成功, false: 失败
 */
bool SHT3X_Read(SHT3X_Config* config, SHT3X_Data* data) {
    uint8_t buffer[6];

    // 发送测量命令(高重复性)
    if (!SHT3X_WriteCommand(config, SHT3X_CMD_MEASURE_HIGH)) {
        data->valid = false;
        return false;
    }

    // 等待测量完成(高重复性需要15ms)
    HAL_Delay(20);

    // 读取测量数据
    HAL_StatusTypeDef status = HAL_I2C_Master_Receive(
        config->hi2c,
        config->address << 1,
        buffer,
        6,
        HAL_MAX_DELAY
    );

    if (status != HAL_OK) {
        data->valid = false;
        return false;
    }

    // 检查温度CRC
    if (!SHT3X_CheckCRC(&buffer[0], 2, buffer[2])) {
        data->valid = false;
        return false;
    }

    // 检查湿度CRC
    if (!SHT3X_CheckCRC(&buffer[3], 2, buffer[5])) {
        data->valid = false;
        return false;
    }

    // 计算温度
    uint16_t temp_raw = (buffer[0] << 8) | buffer[1];
    data->temperature = -45.0f + 175.0f * ((float)temp_raw / 65535.0f);

    // 计算湿度
    uint16_t humi_raw = (buffer[3] << 8) | buffer[4];
    data->humidity = 100.0f * ((float)humi_raw / 65535.0f);

    data->valid = true;
    return true;
}

SHT3x使用示例

/**
 * @file    main.c
 * @brief   SHT3x传感器使用示例
 */

#include "stm32f1xx_hal.h"
#include "sht3x.h"
#include <stdio.h>

// I2C句柄(需要在CubeMX中配置)
extern I2C_HandleTypeDef hi2c1;

// SHT3x传感器配置
SHT3X_Config sht3x_config = {
    .hi2c = &hi2c1,
    .address = SHT3X_ADDR_LOW    // ADDR引脚接GND
};

/**
 * @brief  主函数
 */
int main(void) {
    // 系统初始化
    HAL_Init();
    SystemClock_Config();

    // 初始化I2C
    MX_I2C1_Init();

    // 初始化UART(用于打印)
    MX_USART1_UART_Init();

    printf("SHT3x温湿度传感器测试\n");
    printf("═══════════════════════════════════════\n\n");

    // 初始化SHT3x传感器
    if (!SHT3X_Init(&sht3x_config)) {
        printf("❌ SHT3x初始化失败!\n");
        printf("请检查:\n");
        printf("  1. I2C连接是否正确\n");
        printf("  2. 上拉电阻是否存在\n");
        printf("  3. 传感器地址是否正确\n");
        while (1);
    }

    printf("✓ SHT3x初始化成功\n\n");

    // 读取状态寄存器
    uint16_t status;
    if (SHT3X_ReadStatus(&sht3x_config, &status)) {
        printf("状态寄存器: 0x%04X\n", status);
        printf("  校验和状态: %s\n", (status & 0x0001) ? "失败" : "正常");
        printf("  命令状态: %s\n", (status & 0x0002) ? "失败" : "正常");
        printf("  复位检测: %s\n", (status & 0x0010) ? "是" : "否");
        printf("  温度报警: %s\n", (status & 0x0400) ? "触发" : "正常");
        printf("  湿度报警: %s\n", (status & 0x0800) ? "触发" : "正常");
        printf("  加热器状态: %s\n\n", (status & 0x2000) ? "开启" : "关闭");
    }

    SHT3X_Data data;
    uint32_t read_count = 0;
    uint32_t error_count = 0;

    while (1) {
        // 读取传感器数据
        if (SHT3X_Read(&sht3x_config, &data)) {
            read_count++;

            // 打印数据
            printf("读取 #%lu:\n", read_count);
            printf("  温度: %.2f °C\n", data.temperature);
            printf("  湿度: %.2f %%RH\n", data.humidity);
            printf("  状态: 有效\n\n");

            // 数据范围检查
            if (data.temperature < -40 || data.temperature > 125) {
                printf("  ⚠ 警告:温度超出正常范围!\n");
            }
            if (data.humidity < 0 || data.humidity > 100) {
                printf("  ⚠ 警告:湿度超出正常范围!\n");
            }
        } else {
            error_count++;
            printf("读取失败 #%lu (总错误: %lu)\n\n", 
                   read_count + error_count, error_count);
        }

        // SHT3x可以快速读取,但建议间隔至少1秒
        HAL_Delay(1000);
    }
}

/**
 * @brief  高级示例:加热器除湿功能
 */
void advanced_example_heater(void) {
    SHT3X_Data data;

    printf("加热器除湿测试\n");
    printf("═══════════════════════════════════════\n\n");

    // 读取初始湿度
    if (SHT3X_Read(&sht3x_config, &data)) {
        printf("初始湿度: %.2f %%RH\n\n", data.humidity);
    }

    // 如果湿度过高,启动加热器
    if (data.humidity > 80.0f) {
        printf("湿度过高,启动加热器...\n");

        // 使能加热器
        SHT3X_EnableHeater(&sht3x_config, true);

        // 加热30秒
        for (int i = 0; i < 30; i++) {
            HAL_Delay(1000);

            if (SHT3X_Read(&sht3x_config, &data)) {
                printf("  %2ds: 温度=%.2f°C, 湿度=%.2f%%RH\n",
                       i+1, data.temperature, data.humidity);
            }
        }

        // 禁用加热器
        SHT3X_EnableHeater(&sht3x_config, false);
        printf("\n加热器已关闭\n");

        // 等待冷却
        HAL_Delay(5000);

        // 读取最终湿度
        if (SHT3X_Read(&sht3x_config, &data)) {
            printf("最终湿度: %.2f %%RH\n", data.humidity);
        }
    }
}

/**
 * @brief  高级示例:数据记录
 */
void advanced_example_data_logging(void) {
    #define LOG_SIZE 100

    typedef struct {
        uint32_t timestamp;
        float temperature;
        float humidity;
    } DataLog;

    DataLog logs[LOG_SIZE];
    uint16_t log_index = 0;

    SHT3X_Data data;

    printf("开始数据记录(%d条)\n", LOG_SIZE);
    printf("═══════════════════════════════════════\n\n");

    // 记录数据
    while (log_index < LOG_SIZE) {
        if (SHT3X_Read(&sht3x_config, &data)) {
            logs[log_index].timestamp = HAL_GetTick();
            logs[log_index].temperature = data.temperature;
            logs[log_index].humidity = data.humidity;

            printf("记录 #%d: T=%.2f°C, H=%.2f%%RH\n",
                   log_index + 1, data.temperature, data.humidity);

            log_index++;
        }

        HAL_Delay(1000);  // 每秒记录一次
    }

    // 数据分析
    printf("\n数据分析\n");
    printf("═══════════════════════════════════════\n");

    float temp_min = logs[0].temperature;
    float temp_max = logs[0].temperature;
    float temp_avg = 0;
    float humi_min = logs[0].humidity;
    float humi_max = logs[0].humidity;
    float humi_avg = 0;

    for (int i = 0; i < LOG_SIZE; i++) {
        // 温度统计
        if (logs[i].temperature < temp_min) temp_min = logs[i].temperature;
        if (logs[i].temperature > temp_max) temp_max = logs[i].temperature;
        temp_avg += logs[i].temperature;

        // 湿度统计
        if (logs[i].humidity < humi_min) humi_min = logs[i].humidity;
        if (logs[i].humidity > humi_max) humi_max = logs[i].humidity;
        humi_avg += logs[i].humidity;
    }

    temp_avg /= LOG_SIZE;
    humi_avg /= LOG_SIZE;

    printf("温度统计:\n");
    printf("  最小值: %.2f °C\n", temp_min);
    printf("  最大值: %.2f °C\n", temp_max);
    printf("  平均值: %.2f °C\n", temp_avg);
    printf("  变化范围: %.2f °C\n\n", temp_max - temp_min);

    printf("湿度统计:\n");
    printf("  最小值: %.2f %%RH\n", humi_min);
    printf("  最大值: %.2f %%RH\n", humi_max);
    printf("  平均值: %.2f %%RH\n", humi_avg);
    printf("  变化范围: %.2f %%RH\n", humi_max - humi_min);
}

第三部分:传感器校准

为什么需要校准

温湿度传感器在使用过程中可能出现漂移,导致测量误差增大。定期校准可以:

  1. 提高测量精度:消除系统误差
  2. 延长使用寿命:及时发现传感器老化
  3. 保证数据可靠性:满足应用要求
  4. 符合标准要求:通过认证测试

校准方法

方法1:两点校准法

/**
 * @brief  两点校准数据结构
 */
typedef struct {
    float ref_low;      // 低点参考值
    float meas_low;     // 低点测量值
    float ref_high;     // 高点参考值
    float meas_high;    // 高点测量值
    float k;            // 校准系数
    float b;            // 校准偏移
} CalibrationData;

/**
 * @brief  计算校准参数
 * @param  calib: 校准数据
 */
void calculate_calibration(CalibrationData* calib) {
    // 计算线性校准参数
    // y = k * x + b
    // 其中 y 是真实值,x 是测量值

    calib->k = (calib->ref_high - calib->ref_low) / 
               (calib->meas_high - calib->meas_low);

    calib->b = calib->ref_low - calib->k * calib->meas_low;

    printf("校准参数计算完成:\n");
    printf("  k = %.4f\n", calib->k);
    printf("  b = %.4f\n", calib->b);
}

/**
 * @brief  应用校准
 * @param  measured: 测量值
 * @param  calib: 校准数据
 * @retval 校准后的值
 */
float apply_calibration(float measured, CalibrationData* calib) {
    return calib->k * measured + calib->b;
}

/**
 * @brief  温度校准示例
 */
void temperature_calibration_example(void) {
    CalibrationData temp_calib;

    printf("温度传感器校准\n");
    printf("═══════════════════════════════════════\n\n");

    // 步骤1:低温点校准(例如:冰水混合物 0°C)
    printf("步骤1:将传感器放入冰水混合物中\n");
    printf("等待温度稳定...\n");
    HAL_Delay(60000);  // 等待1分钟

    SHT3X_Data data;
    float temp_sum = 0;
    for (int i = 0; i < 10; i++) {
        SHT3X_Read(&sht3x_config, &data);
        temp_sum += data.temperature;
        HAL_Delay(1000);
    }

    temp_calib.ref_low = 0.0f;  // 参考值:0°C
    temp_calib.meas_low = temp_sum / 10.0f;  // 测量平均值
    printf("低温点测量值: %.2f °C\n\n", temp_calib.meas_low);

    // 步骤2:高温点校准(例如:沸水 100°C,需根据气压调整)
    printf("步骤2:将传感器放入沸水中(注意安全!)\n");
    printf("等待温度稳定...\n");
    HAL_Delay(60000);  // 等待1分钟

    temp_sum = 0;
    for (int i = 0; i < 10; i++) {
        SHT3X_Read(&sht3x_config, &data);
        temp_sum += data.temperature;
        HAL_Delay(1000);
    }

    temp_calib.ref_high = 100.0f;  // 参考值:100°C(标准大气压)
    temp_calib.meas_high = temp_sum / 10.0f;  // 测量平均值
    printf("高温点测量值: %.2f °C\n\n", temp_calib.meas_high);

    // 计算校准参数
    calculate_calibration(&temp_calib);

    // 验证校准
    printf("\n校准验证:\n");
    printf("测量值 -> 校准后\n");
    for (float t = 0; t <= 100; t += 10) {
        float calibrated = apply_calibration(t, &temp_calib);
        printf("%.1f°C -> %.2f°C\n", t, calibrated);
    }
}

/**
 * @brief  湿度校准示例(使用饱和盐溶液)
 */
void humidity_calibration_example(void) {
    CalibrationData humi_calib;

    printf("湿度传感器校准\n");
    printf("═══════════════════════════════════════\n\n");

    printf("使用饱和盐溶液法校准\n");
    printf("常用饱和盐溶液相对湿度(25°C):\n");
    printf("  LiCl:  11.3%%RH\n");
    printf("  MgCl2: 33.0%%RH\n");
    printf("  NaCl:  75.3%%RH\n");
    printf("  KCl:   84.3%%RH\n\n");

    // 步骤1:低湿度点(使用MgCl2饱和盐溶液)
    printf("步骤1:将传感器放入MgCl2饱和盐溶液密闭容器中\n");
    printf("等待湿度稳定(至少8小时)...\n");
    printf("按任意键继续...\n");
    // 等待用户输入

    SHT3X_Data data;
    float humi_sum = 0;
    for (int i = 0; i < 10; i++) {
        SHT3X_Read(&sht3x_config, &data);
        humi_sum += data.humidity;
        HAL_Delay(1000);
    }

    humi_calib.ref_low = 33.0f;  // MgCl2参考值:33%RH
    humi_calib.meas_low = humi_sum / 10.0f;
    printf("低湿度点测量值: %.2f %%RH\n\n", humi_calib.meas_low);

    // 步骤2:高湿度点(使用KCl饱和盐溶液)
    printf("步骤2:将传感器放入KCl饱和盐溶液密闭容器中\n");
    printf("等待湿度稳定(至少8小时)...\n");
    printf("按任意键继续...\n");
    // 等待用户输入

    humi_sum = 0;
    for (int i = 0; i < 10; i++) {
        SHT3X_Read(&sht3x_config, &data);
        humi_sum += data.humidity;
        HAL_Delay(1000);
    }

    humi_calib.ref_high = 84.3f;  // KCl参考值:84.3%RH
    humi_calib.meas_high = humi_sum / 10.0f;
    printf("高湿度点测量值: %.2f %%RH\n\n", humi_calib.meas_high);

    // 计算校准参数
    calculate_calibration(&humi_calib);

    // 保存校准参数到Flash
    // save_calibration_to_flash(&humi_calib);
}

方法2:与标准仪器对比校准

/**
 * @brief  与标准仪器对比校准
 */
void calibration_with_reference(void) {
    printf("与标准仪器对比校准\n");
    printf("═══════════════════════════════════════\n\n");

    printf("将传感器与标准温湿度计放置在同一环境中\n");
    printf("等待环境稳定...\n");
    HAL_Delay(300000);  // 等待5分钟

    SHT3X_Data data;

    // 读取多次取平均
    float temp_sum = 0, humi_sum = 0;
    const int samples = 20;

    for (int i = 0; i < samples; i++) {
        if (SHT3X_Read(&sht3x_config, &data)) {
            temp_sum += data.temperature;
            humi_sum += data.humidity;
        }
        HAL_Delay(1000);
    }

    float temp_measured = temp_sum / samples;
    float humi_measured = humi_sum / samples;

    printf("测量平均值:\n");
    printf("  温度: %.2f °C\n", temp_measured);
    printf("  湿度: %.2f %%RH\n\n", humi_measured);

    printf("请输入标准仪器读数:\n");
    printf("  温度: ");
    float temp_reference;
    scanf("%f", &temp_reference);

    printf("  湿度: ");
    float humi_reference;
    scanf("%f", &humi_reference);

    // 计算误差
    float temp_error = temp_measured - temp_reference;
    float humi_error = humi_measured - humi_reference;

    printf("\n测量误差:\n");
    printf("  温度误差: %.2f °C\n", temp_error);
    printf("  湿度误差: %.2f %%RH\n", humi_error);

    // 简单偏移校准
    printf("\n校准偏移量:\n");
    printf("  温度偏移: %.2f °C\n", -temp_error);
    printf("  湿度偏移: %.2f %%RH\n", -humi_error);
}

第四部分:实际应用案例

案例1:智能温室监控系统

/**
 * @brief  智能温室监控系统
 */

// 温室环境参数
typedef struct {
    float temp_target;      // 目标温度
    float temp_tolerance;   // 温度容差
    float humi_target;      // 目标湿度
    float humi_tolerance;   // 湿度容差
} GreenhouseConfig;

// 控制输出
typedef struct {
    bool heater;           // 加热器
    bool cooler;           // 冷却器(风扇)
    bool humidifier;       // 加湿器
    bool dehumidifier;     // 除湿器
} GreenhouseControl;

/**
 * @brief  温室控制逻辑
 */
void greenhouse_control(SHT3X_Data* sensor, 
                       GreenhouseConfig* config,
                       GreenhouseControl* control) {
    // 温度控制
    if (sensor->temperature < config->temp_target - config->temp_tolerance) {
        // 温度过低,开启加热器
        control->heater = true;
        control->cooler = false;
        printf("🔥 开启加热器\n");
    } else if (sensor->temperature > config->temp_target + config->temp_tolerance) {
        // 温度过高,开启冷却器
        control->heater = false;
        control->cooler = true;
        printf("❄️  开启冷却器\n");
    } else {
        // 温度正常
        control->heater = false;
        control->cooler = false;
        printf("✓ 温度正常\n");
    }

    // 湿度控制
    if (sensor->humidity < config->humi_target - config->humi_tolerance) {
        // 湿度过低,开启加湿器
        control->humidifier = true;
        control->dehumidifier = false;
        printf("💧 开启加湿器\n");
    } else if (sensor->humidity > config->humi_target + config->humi_tolerance) {
        // 湿度过高,开启除湿器
        control->humidifier = false;
        control->dehumidifier = true;
        printf("🌵 开启除湿器\n");
    } else {
        // 湿度正常
        control->humidifier = false;
        control->dehumidifier = false;
        printf("✓ 湿度正常\n");
    }
}

/**
 * @brief  温室监控主循环
 */
void greenhouse_monitoring(void) {
    // 配置目标参数
    GreenhouseConfig config = {
        .temp_target = 25.0f,      // 目标温度25°C
        .temp_tolerance = 2.0f,    // 容差±2°C
        .humi_target = 70.0f,      // 目标湿度70%RH
        .humi_tolerance = 5.0f     // 容差±5%RH
    };

    GreenhouseControl control = {0};
    SHT3X_Data sensor_data;

    printf("智能温室监控系统启动\n");
    printf("═══════════════════════════════════════\n");
    printf("目标温度: %.1f°C (±%.1f°C)\n", 
           config.temp_target, config.temp_tolerance);
    printf("目标湿度: %.1f%%RH (±%.1f%%RH)\n\n",
           config.humi_target, config.humi_tolerance);

    while (1) {
        // 读取传感器数据
        if (SHT3X_Read(&sht3x_config, &sensor_data)) {
            printf("当前环境:\n");
            printf("  温度: %.2f °C\n", sensor_data.temperature);
            printf("  湿度: %.2f %%RH\n", sensor_data.humidity);

            // 执行控制逻辑
            greenhouse_control(&sensor_data, &config, &control);

            // 输出控制信号
            HAL_GPIO_WritePin(HEATER_GPIO_Port, HEATER_Pin, 
                            control.heater ? GPIO_PIN_SET : GPIO_PIN_RESET);
            HAL_GPIO_WritePin(COOLER_GPIO_Port, COOLER_Pin,
                            control.cooler ? GPIO_PIN_SET : GPIO_PIN_RESET);
            HAL_GPIO_WritePin(HUMIDIFIER_GPIO_Port, HUMIDIFIER_Pin,
                            control.humidifier ? GPIO_PIN_SET : GPIO_PIN_RESET);
            HAL_GPIO_WritePin(DEHUMIDIFIER_GPIO_Port, DEHUMIDIFIER_Pin,
                            control.dehumidifier ? GPIO_PIN_SET : GPIO_PIN_RESET);

            printf("\n");
        }

        // 每30秒检测一次
        HAL_Delay(30000);
    }
}

案例2:舒适度指数计算

/**
 * @brief  计算热指数(Heat Index)
 * @param  temp: 温度(°C)
 * @param  humi: 相对湿度(%)
 * @retval 热指数(°C)
 */
float calculate_heat_index(float temp, float humi) {
    // 转换为华氏度
    float T = temp * 9.0f / 5.0f + 32.0f;
    float RH = humi;

    // Rothfusz回归方程
    float HI = -42.379f + 
               2.04901523f * T + 
               10.14333127f * RH - 
               0.22475541f * T * RH - 
               0.00683783f * T * T - 
               0.05481717f * RH * RH + 
               0.00122874f * T * T * RH + 
               0.00085282f * T * RH * RH - 
               0.00000199f * T * T * RH * RH;

    // 转换回摄氏度
    return (HI - 32.0f) * 5.0f / 9.0f;
}

/**
 * @brief  计算露点温度
 * @param  temp: 温度(°C)
 * @param  humi: 相对湿度(%)
 * @retval 露点温度(°C)
 */
float calculate_dew_point(float temp, float humi) {
    float a = 17.27f;
    float b = 237.7f;

    float alpha = ((a * temp) / (b + temp)) + logf(humi / 100.0f);
    float dew_point = (b * alpha) / (a - alpha);

    return dew_point;
}

/**
 * @brief  评估舒适度等级
 */
const char* evaluate_comfort(float temp, float humi) {
    // 舒适度评估标准
    if (temp < 18.0f) {
        return "偏冷";
    } else if (temp > 26.0f) {
        if (humi > 70.0f) {
            return "闷热";
        } else {
            return "偏热";
        }
    } else {
        // 18-26°C
        if (humi < 30.0f) {
            return "干燥";
        } else if (humi > 70.0f) {
            return "潮湿";
        } else {
            return "舒适";
        }
    }
}

/**
 * @brief  舒适度监测示例
 */
void comfort_monitoring(void) {
    SHT3X_Data data;

    printf("室内舒适度监测\n");
    printf("═══════════════════════════════════════\n\n");

    while (1) {
        if (SHT3X_Read(&sht3x_config, &data)) {
            // 计算衍生参数
            float heat_index = calculate_heat_index(data.temperature, data.humidity);
            float dew_point = calculate_dew_point(data.temperature, data.humidity);
            const char* comfort = evaluate_comfort(data.temperature, data.humidity);

            // 显示结果
            printf("环境参数:\n");
            printf("  温度: %.2f °C\n", data.temperature);
            printf("  湿度: %.2f %%RH\n", data.humidity);
            printf("  热指数: %.2f °C\n", heat_index);
            printf("  露点: %.2f °C\n", dew_point);
            printf("  舒适度: %s\n", comfort);

            // 建议
            printf("\n建议:\n");
            if (strcmp(comfort, "偏冷") == 0) {
                printf("  💡 建议开启暖气或增加衣物\n");
            } else if (strcmp(comfort, "偏热") == 0) {
                printf("  💡 建议开启空调或增加通风\n");
            } else if (strcmp(comfort, "闷热") == 0) {
                printf("  💡 建议开启空调除湿模式\n");
            } else if (strcmp(comfort, "干燥") == 0) {
                printf("  💡 建议开启加湿器\n");
            } else if (strcmp(comfort, "潮湿") == 0) {
                printf("  💡 建议开启除湿器或增加通风\n");
            } else {
                printf("  ✓ 环境舒适,无需调整\n");
            }

            printf("\n");
        }

        HAL_Delay(60000);  // 每分钟更新一次
    }
}

案例3:数据上传到云平台

/**
 * @brief  数据上传到云平台(示例:MQTT)
 */

#include "mqtt_client.h"  // MQTT客户端库
#include <cJSON.h>        // JSON库

/**
 * @brief  构建JSON数据
 */
char* build_json_data(SHT3X_Data* data) {
    cJSON* root = cJSON_CreateObject();

    // 添加设备信息
    cJSON_AddStringToObject(root, "device_id", "greenhouse_001");
    cJSON_AddNumberToObject(root, "timestamp", HAL_GetTick());

    // 添加传感器数据
    cJSON* sensor = cJSON_CreateObject();
    cJSON_AddNumberToObject(sensor, "temperature", data->temperature);
    cJSON_AddNumberToObject(sensor, "humidity", data->humidity);
    cJSON_AddItemToObject(root, "sensor_data", sensor);

    // 转换为字符串
    char* json_string = cJSON_Print(root);

    // 释放内存
    cJSON_Delete(root);

    return json_string;
}

/**
 * @brief  上传数据到云平台
 */
void upload_to_cloud(void) {
    // MQTT配置
    mqtt_client_config_t mqtt_cfg = {
        .host = "mqtt.example.com",
        .port = 1883,
        .client_id = "greenhouse_001",
        .username = "user",
        .password = "pass"
    };

    // 连接MQTT服务器
    mqtt_client_handle_t client = mqtt_client_init(&mqtt_cfg);
    mqtt_client_start(client);

    printf("云平台数据上传\n");
    printf("═══════════════════════════════════════\n\n");

    SHT3X_Data data;

    while (1) {
        // 读取传感器数据
        if (SHT3X_Read(&sht3x_config, &data)) {
            // 构建JSON数据
            char* json_data = build_json_data(&data);

            printf("上传数据: %s\n", json_data);

            // 发布到MQTT主题
            mqtt_client_publish(client, 
                              "greenhouse/sensor/data",
                              json_data,
                              strlen(json_data),
                              1,  // QoS
                              0); // retain

            // 释放内存
            free(json_data);

            printf("✓ 数据上传成功\n\n");
        }

        // 每5分钟上传一次
        HAL_Delay(300000);
    }
}

常见问题与解决方案

DHT传感器常见问题

问题1:读取失败或数据不稳定

可能原因:
1. 上拉电阻缺失或阻值不合适
   解决:添加4.7kΩ上拉电阻

2. 数据线过长
   解决:缩短数据线长度(<20米)

3. 电源不稳定
   解决:添加去耦电容(0.1μF)

4. 时序不准确
   解决:使用DWT精确延时,避免使用HAL_Delay

5. 读取频率过高
   解决:DHT11至少间隔1秒,DHT22至少间隔2秒

问题2:校验和错误

/**
 * @brief  校验和错误诊断
 */
void diagnose_checksum_error(void) {
    printf("校验和错误诊断\n");
    printf("═══════════════════════════════════════\n\n");

    // 测试多次读取
    int success = 0;
    int checksum_error = 0;
    int timeout_error = 0;

    for (int i = 0; i < 100; i++) {
        DHT_Data data;
        if (DHT_Read(&dht_config, &data)) {
            success++;
        } else {
            // 分析错误类型
            // 这里需要修改DHT_Read函数返回详细错误码
            checksum_error++;
        }
        HAL_Delay(2000);
    }

    printf("测试结果(100次):\n");
    printf("  成功: %d\n", success);
    printf("  校验和错误: %d\n", checksum_error);
    printf("  超时错误: %d\n", timeout_error);
    printf("  成功率: %.1f%%\n", success / 100.0f * 100);

    if (checksum_error > 10) {
        printf("\n⚠ 校验和错误率过高!\n");
        printf("建议检查:\n");
        printf("  1. 数据线是否有干扰\n");
        printf("  2. 上拉电阻是否合适\n");
        printf("  3. 电源是否稳定\n");
        printf("  4. 传感器是否损坏\n");
    }
}

问题3:温度或湿度读数异常

异常表现:
1. 读数为0或固定值
   - 检查传感器连接
   - 检查传感器是否损坏

2. 读数跳变剧烈
   - 增加软件滤波
   - 检查电源稳定性
   - 远离干扰源

3. 读数偏差大
   - 进行校准
   - 检查环境是否稳定
   - 等待传感器预热

解决方案:
/**
 * @brief  数据有效性检查
 */
bool validate_sensor_data(DHT_Data* data) {
    // 温度范围检查
    if (data->temperature < -40 || data->temperature > 80) {
        printf("⚠ 温度超出范围: %.2f°C\n", data->temperature);
        return false;
    }

    // 湿度范围检查
    if (data->humidity < 0 || data->humidity > 100) {
        printf("⚠ 湿度超出范围: %.2f%%RH\n", data->humidity);
        return false;
    }

    // 变化率检查(与上次读数对比)
    static float last_temp = 25.0f;
    static float last_humi = 50.0f;

    float temp_change = fabsf(data->temperature - last_temp);
    float humi_change = fabsf(data->humidity - last_humi);

    if (temp_change > 10.0f) {
        printf("⚠ 温度变化过大: %.2f°C\n", temp_change);
        return false;
    }

    if (humi_change > 20.0f) {
        printf("⚠ 湿度变化过大: %.2f%%RH\n", humi_change);
        return false;
    }

    // 更新历史值
    last_temp = data->temperature;
    last_humi = data->humidity;

    return true;
}

SHT3x传感器常见问题

问题1:I2C通信失败

可能原因:
1. I2C地址错误
   解决:检查ADDR引脚连接,确认地址(0x44或0x45)

2. 上拉电阻缺失
   解决:添加4.7kΩ上拉电阻到SDA和SCL

3. I2C速度过快
   解决:降低I2C时钟频率(100kHz或400kHz)

4. 总线冲突
   解决:检查总线上是否有其他设备冲突

5. 电源问题
   解决:确保电源电压在2.4V-5.5V范围内

问题2:CRC校验失败

/**
 * @brief  CRC错误诊断
 */
void diagnose_crc_error(void) {
    printf("CRC错误诊断\n");
    printf("═══════════════════════════════════════\n\n");

    int success = 0;
    int crc_error = 0;
    int i2c_error = 0;

    for (int i = 0; i < 100; i++) {
        SHT3X_Data data;
        if (SHT3X_Read(&sht3x_config, &data)) {
            success++;
        } else {
            // 需要修改SHT3X_Read返回详细错误码
            crc_error++;
        }
        HAL_Delay(1000);
    }

    printf("测试结果(100次):\n");
    printf("  成功: %d\n", success);
    printf("  CRC错误: %d\n", crc_error);
    printf("  I2C错误: %d\n", i2c_error);
    printf("  成功率: %.1f%%\n", success / 100.0f * 100);

    if (crc_error > 5) {
        printf("\n⚠ CRC错误率过高!\n");
        printf("建议检查:\n");
        printf("  1. I2C信号质量\n");
        printf("  2. 上拉电阻值\n");
        printf("  3. 线缆长度和质量\n");
        printf("  4. 电磁干扰\n");
    }
}

问题3:测量值漂移

原因分析:
1. 传感器老化
   - 定期校准
   - 更换传感器

2. 环境污染
   - 使用加热器清洁(SHT3x内置)
   - 避免接触化学物质

3. 长期高湿度
   - 定期使用加热器除湿
   - 改善使用环境

解决方案:
/**
 * @brief  传感器自清洁(使用内置加热器)
 */
void sensor_self_cleaning(void) {
    printf("传感器自清洁程序\n");
    printf("═══════════════════════════════════════\n\n");

    // 读取清洁前数据
    SHT3X_Data data_before;
    SHT3X_Read(&sht3x_config, &data_before);
    printf("清洁前: T=%.2f°C, H=%.2f%%RH\n", 
           data_before.temperature, data_before.humidity);

    // 启动加热器
    printf("\n启动加热器清洁...\n");
    SHT3X_EnableHeater(&sht3x_config, true);

    // 加热10秒
    for (int i = 0; i < 10; i++) {
        HAL_Delay(1000);
        printf(".");
    }
    printf("\n");

    // 关闭加热器
    SHT3X_EnableHeater(&sht3x_config, false);

    // 等待冷却
    printf("等待冷却...\n");
    HAL_Delay(30000);

    // 读取清洁后数据
    SHT3X_Data data_after;
    SHT3X_Read(&sht3x_config, &data_after);
    printf("清洁后: T=%.2f°C, H=%.2f%%RH\n",
           data_after.temperature, data_after.humidity);

    printf("\n✓ 清洁完成\n");
    printf("建议每月执行一次自清洁程序\n");
}

性能优化建议

降低功耗

/**
 * @brief  低功耗模式示例
 */
void low_power_example(void) {
    SHT3X_Data data;

    while (1) {
        // 唤醒传感器并读取数据
        SHT3X_Read(&sht3x_config, &data);

        // 处理数据
        process_sensor_data(&data);

        // 关闭I2C外设
        __HAL_RCC_I2C1_CLK_DISABLE();

        // 进入低功耗模式
        HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

        // 唤醒后恢复时钟
        SystemClock_Config();
        __HAL_RCC_I2C1_CLK_ENABLE();

        // 下次测量间隔(例如:每10分钟)
        // 实际由RTC或定时器唤醒
    }
}

提高可靠性

/**
 * @brief  带重试机制的读取
 */
bool reliable_read(SHT3X_Config* config, SHT3X_Data* data, uint8_t max_retry) {
    for (uint8_t i = 0; i < max_retry; i++) {
        if (SHT3X_Read(config, data)) {
            // 数据有效性检查
            if (validate_sensor_data(data)) {
                return true;
            }
        }

        // 失败后延时重试
        HAL_Delay(100);
    }

    return false;
}

/**
 * @brief  多传感器冗余
 */
void multi_sensor_redundancy(void) {
    SHT3X_Config sensor1 = {.hi2c = &hi2c1, .address = SHT3X_ADDR_LOW};
    SHT3X_Config sensor2 = {.hi2c = &hi2c1, .address = SHT3X_ADDR_HIGH};

    SHT3X_Data data1, data2;

    bool read1 = SHT3X_Read(&sensor1, &data1);
    bool read2 = SHT3X_Read(&sensor2, &data2);

    if (read1 && read2) {
        // 两个传感器都正常,取平均值
        float temp_avg = (data1.temperature + data2.temperature) / 2.0f;
        float humi_avg = (data1.humidity + data2.humidity) / 2.0f;

        printf("平均值: T=%.2f°C, H=%.2f%%RH\n", temp_avg, humi_avg);

        // 检查差异
        float temp_diff = fabsf(data1.temperature - data2.temperature);
        float humi_diff = fabsf(data1.humidity - data2.humidity);

        if (temp_diff > 2.0f || humi_diff > 5.0f) {
            printf("⚠ 警告:传感器读数差异较大!\n");
            printf("  传感器1: T=%.2f°C, H=%.2f%%RH\n",
                   data1.temperature, data1.humidity);
            printf("  传感器2: T=%.2f°C, H=%.2f%%RH\n",
                   data2.temperature, data2.humidity);
        }
    } else if (read1) {
        // 只有传感器1正常
        printf("使用传感器1数据\n");
    } else if (read2) {
        // 只有传感器2正常
        printf("使用传感器2数据\n");
    } else {
        // 两个传感器都失败
        printf("❌ 所有传感器读取失败!\n");
    }
}

总结

关键要点

  1. 传感器选型
  2. 入门学习:DHT11(便宜、简单)
  3. 一般应用:DHT22(性价比高)
  4. 精密测量:SHT30/SHT31(高精度)

  5. 硬件设计

  6. 必须添加上拉电阻(4.7kΩ)
  7. 电源去耦电容(0.1μF)
  8. 注意数据线长度
  9. 合理选择I2C地址

  10. 软件实现

  11. DHT:使用精确的微秒延时
  12. SHT3x:正确实现CRC校验
  13. 添加数据有效性检查
  14. 实现重试机制

  15. 校准维护

  16. 定期校准提高精度
  17. 使用标准参考源
  18. 记录校准参数
  19. 定期清洁传感器

  20. 实际应用

  21. 合理设置采样间隔
  22. 添加数据滤波
  23. 实现报警功能
  24. 考虑低功耗设计

下一步学习

完成本教程后,建议继续学习:

  • 加速度计与陀螺仪基础:学习运动传感器
  • 光照与气压传感器应用:扩展环境监测能力
  • 传感器数据融合技术:多传感器协同工作

参考资源

数据手册: - DHT11 Datasheet - DHT22/AM2302 Datasheet - SHT30 Datasheet - SHT31 Datasheet

应用笔记: - Sensirion - Handling Instructions for SHT3x - Sensirion - Calibration Guide - DHT Sensor Application Notes

开发工具: - STM32CubeMX - Sensirion SHT3x Evaluation Kit - I2C分析仪

在线资源: - Sensirion官方网站 - Adafruit DHT传感器教程 - Arduino温湿度传感器库


作者: 嵌入式知识平台内容团队
最后更新: 2026-03-07
版本: 1.0.0