【ESP-IDF5.x】 ESP32/ESP8266驱动SHT3x温湿度传感器

环境

  • 软件环境
    • VScode
    • ESP-IDF5.x
  • 硬件环境
    • esp32 / esp8266
    • sht3x温湿度传感器

SHT3x周围电路连接如下

下面是SHT3x与ESP32-S3的引脚接线示例,ESP32或者ESP8266根据开发板的I2C引脚进行接线

引脚接线
VDD3.3/5V
ADDRGND
SDA4
SCL5

说明

代码示例见:https://github.com/MGod-monkey/sht3x-esp-idf5x

将上面的代码下载到本地并通过VScode打开,需要先选择自己芯片的平台,然后编译

image-20240823155719287

驱动sht3x的代码封装在components\sht3x\sht3x.h,每个函数的说明如下:

  1. sht3x_init_sensor

    • 初始化一个 SHT3x 传感器。
    • 创建一个描述传感器的数据结构并初始化传感器设备。
    • 参数:
      • bus: 传感器连接的 I2C 总线。
      • addr: 传感器的 I2C 从设备地址。
    • 返回值:指向传感器数据结构的指针,初始化失败时返回 NULL。
  2. sht3x_measure

    • 高级测量函数,执行一次测量。
    • 包含三个步骤:
      1. 以高可靠性启动一次单次测量。
      2. 使用 vTaskDelay 等待测量结果可用。
      3. 返回浮点类型的传感器测量值。
    • 参数:
      • dev: 指向传感器设备数据结构的指针。
      • temperature: 返回的温度值(摄氏度)。
      • humidity: 返回的湿度值(百分比)。
    • 返回值:成功时返回 true,失败时返回 false
  3. sht3x_start_measurement

    • 启动单次测量或周期性测量。
    • 可以选择单次测量模式或周期性测量模式,以及设置测量的重复性。
    • 参数:
      • dev: 指向传感器设备数据结构的指针。
      • mode: 测量模式(单次或周期性),参见 sht3x_mode_t 类型。
      • repeat: 测量的重复性,参见 sht3x_repeat_t 类型。
    • 返回值:成功时返回 true,失败时返回 false
  4. sht3x_get_measurement_duration

    • 获取测量所需的 RTOS tick 数。
    • 返回给定重复性下执行一次测量所需的时间。
    • 用户任务可以直接使用此函数返回的持续时间来等待测量结果。
    • 参数:
      • repeat: 测量的重复性,参见 sht3x_repeat_t 类型。
    • 返回值:测量持续时间,单位为 RTOS ticks。
  5. sht3x_get_raw_data

    • 从传感器读取测量结果并存储为原始数据。
    • 读取温度和压力的测量结果,检查 CRC 校验码并存储在字节数组中。
    • 参数:
      • dev: 指向传感器设备数据结构的指针。
      • raw_data: 存储原始数据的字节数组。
    • 返回值:成功时返回 true,失败时返回 false
  6. sht3x_compute_values

    • 从原始数据计算出传感器的温度和湿度值。
    • 参数:
      • raw_data: 包含原始数据的字节数组。
      • temperature: 返回的温度值(摄氏度)。
      • humidity: 返回的湿度值(百分比)。
    • 返回值:成功时返回 true,失败时返回 false
  7. sht3x_get_results

    • 获取传感器测量结果并返回传感器值。
    • 该函数结合了 sht3x_read_raw_datasht3x_compute_values 函数,读取原始数据并计算出传感器的温度和湿度值。
    • 参数:
      • dev: 指向传感器设备数据结构的指针。
      • temperature: 返回的温度值(摄氏度)。
      • humidity: 返回的湿度值(百分比)。
    • 返回值:成功时返回 true,失败时返回 false

获取SHT3x温湿度传感器的示例代码如下:

  • 测量模式选择:SINGLE_SHOT_HIGH_LEVEL、SINGLE_SHOT_LOW_LEVEL或周期模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    // #define SINGLE_SHOT_LOW_LEVEL
    // #define SINGLE_SHOT_HIGH_LEVEL

    #if defined(SINGLE_SHOT_HIGH_LEVEL)
    void user_task (void *pvParameters)
    {
    float temperature;
    float humidity;

    TickType_t last_wakeup = xTaskGetTickCount();

    while (1)
    {
    // perform one measurement and do something with the results
    if (sht3x_measure (sensor, &temperature, &humidity))
    printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
    (double)sdk_system_get_time()*1e-3, temperature, humidity);

    // wait until 5 seconds are over
    vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
    }
    }
    #elif defined(SINGLE_SHOT_LOW_LEVEL)
    void user_task (void *pvParameters)
    {
    float temperature;
    float humidity;

    TickType_t last_wakeup = xTaskGetTickCount();

    // get the measurement duration for high repeatability;
    uint8_t duration = sht3x_get_measurement_duration(sht3x_high);

    while (1)
    {
    // Trigger one measurement in single shot mode with high repeatability.
    sht3x_start_measurement (sensor, sht3x_single_shot, sht3x_high);

    // Wait until measurement is ready (constant time of at least 30 ms
    // or the duration returned from *sht3x_get_measurement_duration*).
    vTaskDelay (duration);

    // retrieve the values and do something with them
    if (sht3x_get_results (sensor, &temperature, &humidity))
    printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
    (double)sdk_system_get_time()*1e-3, temperature, humidity);

    // wait until 5 seconds are over
    vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
    }
    }
    #else // PERIODIC MODE
    void user_task (void *pvParameters)
    {
    float temperature;
    float humidity;

    // Start periodic measurements with 1 measurement per second.
    sht3x_start_measurement (sensor, sht3x_periodic_1mps, sht3x_high);

    // Wait until first measurement is ready (constant time of at least 30 ms
    // or the duration returned from *sht3x_get_measurement_duration*).
    vTaskDelay (sht3x_get_measurement_duration(sht3x_high));

    TickType_t last_wakeup = xTaskGetTickCount();

    while (1)
    {
    // Get the values and do something with them.
    if (sht3x_get_results (sensor, &temperature, &humidity))
    printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
    (double)sdk_system_get_time()*1e-3, temperature, humidity);
    // Wait until 2 seconds (cycle time) are over.
    vTaskDelayUntil(&last_wakeup, 2000 / portTICK_PERIOD_MS);
    }
    }
    #endif
    • 单次测量模式

      • 在单次测量模式下,一条测量命令会触发精确采集一个数据对。每个数据对包括 16 位十进制的温度和湿度值。由于测量持续时间长达 15 毫秒,因此测量过程被分成多个步骤,以避免在测量过程中阻塞用户任务:

        1. 使用函数 sht3x_start_measurement 触发传感器,执行一次测量。

        2. 使用函数 vTaskDelay 等待测量持续时间,直到获得测量结果。使用至少 30 毫秒的恒定持续时间或函数 sht3x_get_measurement_duration 返回的以 RTOS ticks 为单位的持续时间进行等待。

        3. 使用函数 sht3x_get_results 或函数 sht3x_get_raw_data 获取浮点传感器值或原始数据。

      • 在单次模式下,每次需要新的传感器值时,用户任务都必须执行所有步骤。

    为方便起见,一个高级函数 sht3x_measure(sht3x_measure)只需一个函数即可完成上述三个步骤的测量。该函数是使用传感器的最简单方法。它最适合不想控制传感器细节的用户。
    这种模式的优点是,传感器可以在连续测量之间切换到睡眠模式,从而更加节能。当测量速率小于每秒1次测量时,这种模式尤其有用。

    • 周期模式

      • 在这种模式下,发出的一条测量命令会产生一个数据对流。每个数据对由 16 位十进制的温度和湿度值组成。测量命令一经发送至传感器,传感器就会自动以每秒 0.5、1、2、4 或 10 次的测量速率定期执行测量。数据对可以以相同或更低的速率获取。与单次测量模式一样,测量过程分为以下几个步骤:

        1. 使用函数 sht3x_start_measurement,以给定的速率触发传感器,开始周期性测量。

        2. 使用函数 vTaskDelay 等待测量持续时间,直到获得第一个结果。使用至少 30 毫秒的恒定持续时间或函数 sht3x_get_measurement_duration 返回的以 RTOS ticks 为单位的持续时间进行等待。

使用函数 sht3x_get_results 或函数 sht3x_get_raw_data 获取浮点传感器值或原始数据。
与单次测量模式不同的是,步骤1和2只需执行一次。一旦开始测量,用户任务只需定期获取数据即可,但传感器在整个过程中一直保持活跃状态,因此能耗较高

注:获取测量结果的速率不得大于传感器的周期性测量速率。为避免因传感器的定时容差造成冲突,应小于该速率。

  • 初试化I2C并读取SHT3x温湿度的值

    注:如果SHT3x的ADDR引脚接的不是GND,而是VCC,则需要将地址改为SHT3x_ADDR_2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    #define I2C_MASTER_SCL_IO           GPIO_NUM_5      /*!< GPIO number used for I2C master clock */
    #define I2C_MASTER_SDA_IO GPIO_NUM_4 /*!< GPIO number used for I2C master data */
    #define I2C_MASTER_NUM 0 /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
    #define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency */
    #define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
    #define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
    #define I2C_MASTER_TIMEOUT_MS 1000 /*!< I2C master timeout in milliseconds */

    void app_main(void)
    {
    // Set UART Parameter.
    uart_set_baud(0, 115200);
    // Give the UART some time to settle
    vTaskDelay(1);

    // Init I2C bus interfaces at which SHT3x sensors are connected
    int i2c_master_port = I2C_MASTER_NUM;

    i2c_config_t conf = {
    .mode = I2C_MODE_MASTER,
    .sda_io_num = I2C_MASTER_SDA_IO,
    .scl_io_num = I2C_MASTER_SCL_IO,
    .sda_pullup_en = GPIO_PULLUP_ENABLE,
    .scl_pullup_en = GPIO_PULLUP_ENABLE,
    .master.clk_speed = I2C_MASTER_FREQ_HZ,
    };

    i2c_param_config(i2c_master_port, &conf);

    i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);

    // Create the sensors, multiple sensors are possible.
    if ((sensor = sht3x_init_sensor(I2C_MASTER_NUM, SHT3x_ADDR_1)))
    {
    // Create a user task that uses the sensors.
    xTaskCreate(user_task, "user_task", TASK_STACK_DEPTH, NULL, 2, 0);
    }
    else
    printf("Could not initialize SHT3x sensor\n");
    }

如果串口输出的结果为Could not initialize SHT3x sensor,可以通过下面的程序来扫描I2C设备来确保SHT3x是否连接正常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "driver/i2c.h"
#include <stdio.h>

#define I2C_MASTER_SCL_IO GPIO_NUM_5 /*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO GPIO_NUM_4 /*!< GPIO number used for I2C master data */
#define I2C_MASTER_NUM 0 /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
#define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS 1000 /*!< I2C master timeout in milliseconds */

void i2c_scanner()
{
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};

i2c_param_config(I2C_MASTER_NUM, &conf);
i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);

printf("I2C scanner. Scanning...\n");

for (int address = 1; address < 127; address++) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);

if (ret == ESP_OK) {
printf("I2C device found at address 0x%02x\n", address);
}
}

printf("Scan completed.\n");
}

void app_main(void)
{
i2c_scanner();
}

输出结果中I2C地址有0x44或者0x45则说明SHT3x连接正常

image-20240823162335559

最终的结果如下:

image-20240823161656056