Skip to content

AHT20 温湿度传感器

概述

AHT20 是一款高精度、低功耗的温湿度传感器,具有 I2C 接口。它采用创新的 MEMS 技术,具有高精度、高稳定性和低功耗的特点。AHT20 的温湿度测量范围为 -40°C ~ +85°C 和 0% ~ 100% RH,分辨率分别为 0.1°C 和 0.1% RH。

  • 功耗:1.5mA(典型值)
  • 接口:I2C
  • 温度范围:-40°C ~ +85°C
  • 湿度范围:0% ~ 100% RH
  • 分辨率:0.1°C 和 0.1% RH

手册

AHT20 数据手册
Orangepi zero 3 开发板
ESP32-C3 中 I2C 驱动
ESP32_IDF I2C 知识

1. 需要的设备

Orangepi zero 3 设备,采用 python 进行编程. 使用了 I2C-3 设备,本设备默认是关闭的,需要手动打开. 参考手册说明

2. 芯片操作过程

首先,代码使用 wiringpi 库进行 I2C 通信。

  • 设置 I2C 设备为/dev/i2c-3,地址 0x38。
  • 然后是软复位命令 0xBA,
  • 接着初始化命令 0xE1 0x08 0x00。
  • 发送测量命令 0xAC 0x33 0x00,
  • 等待读取数据。

3. 数据解析过程

数据手册,AHT20 的数据格式是:

湿度数据:20 位,存储在字节 0(高 8 位)、字节 1(中间 8 位)、字节 2 的低 4 位。例如,湿度值应该是 (raw_data[0] << 12) | (raw_data[1] << 4) | (raw_data[2] >> 4)。

温度数据:20 位,存储在字节 2 的高 4 位(即 raw_data[2] & 0x0F),字节 3,字节 4。所以温度应该是 ((raw_data[2] & 0x0F) << 16) | (raw_data[3] << 8) | raw_data[4]。

转换公式是否正确?湿度是 (raw / 2^20) * 100,温度是 (raw / 2^20)*200 -50。这应该没问题。

1. 湿度计算原理

python
humidity = (humidity_raw / (2**20)) * 100
  • 原始数据范围
    AHT20 的湿度原始数据是 20 位二进制值(humidity_raw),范围为  0 ~ 2^20-1(即  0 ~ 1,048,575)。

  • 归一化处理
    将原始数据除以  2^20(即  1,048,576),将其映射到  0.0 ~ 1.0  的浮点数范围。
    公式:

    归一化湿度=220humidity_raw​

  • 转换为百分比
    将归一化后的值乘以  100,得到湿度百分比(0% ~ 100%)。
    公式:

    湿度=(220humidity_raw​)×100


2. 温度计算原理

python
temperature = (temperature_raw / (2**20)) * 200 - 50
  • 原始数据范围
    温度原始数据同样是 20 位二进制值(temperature_raw),范围为  0 ~ 2^20-1

  • 归一化处理
    将原始数据除以  2^20,映射到  0.0 ~ 1.0  的浮点数范围。
    公式:

    归一化温度=220temperature_raw​

  • 线性映射到实际温度范围
    AHT20 的温度测量范围为  -50°C ~ +150°C,总跨度为  200°C
    通过以下步骤转换:

    1. 将归一化值乘以  200,覆盖  0°C ~ 200°C  的范围。
    2. 减去  50,将范围偏移到  -50°C ~ +150°C
      公式:

    温度=(220temperature_raw​)×200−50

4. 具体的代码:

以下是 Orangepi zero 3 的 Python 代码,用于读取 AHT20 温湿度传感器数据:

运行条件是安装了 smbus2 库

bash
apt install python3.10-venv # 安装虚拟环境
python3 -m venv aht20 # 创建虚拟环境 aht20
source aht20/bin/activate # 激活虚拟环境
pip install smbus2 # 安装 smbus2 库

执行了以上的命令后,就可以使用 aht20 虚拟环境了。并可以正常运行下面的代码:

使用python 进行编程

python
import smbus2
import time

# AHT20 I2C 地址
AHT20_ADDR = 0x38

def init_i2c(bus_number):
    try:
        bus = smbus2.SMBus(bus_number)
        return bus
    except Exception as e:
        print(f"初始化I2C失败: {e}")
        exit(1)

def aht20_soft_reset(bus):
    try:
        bus.write_byte(AHT20_ADDR, 0xBA)
        time.sleep(0.02)  # 延长复位时间
    except Exception as e:
        print(f"软复位失败: {e}")

def aht20_init(bus):
    try:
        # 发送初始化命令(0xE1后跟0x08和0x00)
        bus.write_i2c_block_data(AHT20_ADDR, 0xE1, [0x08, 0x00])
        time.sleep(0.5)  # 确保校准完成
    except Exception as e:
        print(f"初始化失败: {e}")

def measure_aht20(bus):
    try:
        # 发送测量命令(0xAC后跟0x33和0x00)
        bus.write_i2c_block_data(AHT20_ADDR, 0xAC, [0x33, 0x00])
        time.sleep(0.08)  # 等待测量完成

        # 读取6字节数据(包括状态位)
        data = bus.read_i2c_block_data(AHT20_ADDR, 0x00, 6)
        raw_data = bytes(data)
        # print(f"原始数据: {list(raw_data)}")

        # 检查状态位(最高位是否为0)
        if (raw_data[0] & 0x80) != 0:
            print("传感器忙,数据未就绪")
            return None, None

        # 解析湿度数据(20位)
        humidity_raw = (raw_data[1] << 12) | (raw_data[2] << 4) | (raw_data[3] >> 4)
        # 解析温度数据(20位)
        temperature_raw = ((raw_data[3] & 0x0F) << 16) | (raw_data[4] << 8) | raw_data[5]

        # 计算实际值
        humidity = (humidity_raw / (2**20)) * 100
        temperature = (temperature_raw / (2**20)) * 200 - 50

        return round(humidity, 2), round(temperature, 2)
    except Exception as e:
        print(f"读取数据失败: {e}")
        return None, None

def main():
    bus_number = 3  # 对应 /dev/i2c-3
    bus = init_i2c(bus_number)

    # 软复位并初始化传感器
    aht20_soft_reset(bus)
    time.sleep(0.1)
    aht20_init(bus)

    try:
        while True:
            humidity, temperature = measure_aht20(bus)
            if humidity is not None and temperature is not None:
                print(f"湿度: {humidity}% 温度: {temperature}°C")
            time.sleep(2)
    except KeyboardInterrupt:
        print("程序终止")
    finally:
        bus.close()

if __name__ == "__main__":
    main()

ESP32-C3 第1版本

以下是使用 ESP32-C3 的 ESP_IDF 框架开发的代码,用于读取 AHT20 温湿度传感器数据:

c
#include <stdio.h>
#include "driver/i2c.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define I2C_MASTER_SCL_IO 8 /* 根据实际接线修改GPIO号 */
#define I2C_MASTER_SDA_IO 9 /* 根据实际接线修改GPIO号 */
#define I2C_MASTER_NUM I2C_NUM_0
#define I2C_MASTER_FREQ_HZ 100000 /* I2C时钟频率 */
#define AHT20_ADDR 0x38           /* AHT20 I2C地址 */

static const char *TAG = "AHT20";

// I2C初始化
static esp_err_t i2c_master_init()
{
    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,
    };
    esp_err_t ret = i2c_param_config(I2C_MASTER_NUM, &conf);
    ret |= i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0);
    return ret;
}

// AHT20初始化
static esp_err_t aht20_init()
{
    uint8_t cmd[3] = {0xBE, 0x08, 0x00}; // 初始化命令
    esp_err_t ret = i2c_master_write_to_device(I2C_MASTER_NUM, AHT20_ADDR, cmd, 3, pdMS_TO_TICKS(1000));
    vTaskDelay(pdMS_TO_TICKS(300)); // 等待校准完成
    return ret;
}

// 读取温湿度
static esp_err_t aht20_read_data(float *temperature, float *humidity)
{
    uint8_t cmd[3] = {0xAC, 0x33, 0x00}; // 触发测量命令
    uint8_t data[6] = {0};

    // 发送测量命令
    ESP_ERROR_CHECK(i2c_master_write_to_device(I2C_MASTER_NUM, AHT20_ADDR, cmd, 3, pdMS_TO_TICKS(1000)));
    vTaskDelay(pdMS_TO_TICKS(80)); // 等待测量完成

    // 读取数据
    ESP_ERROR_CHECK(i2c_master_read_from_device(I2C_MASTER_NUM, AHT20_ADDR, data, 6, pdMS_TO_TICKS(1000)));

    // 校验状态位
    if ((data[0] & 0x80) != 0)
    {
        ESP_LOGE(TAG, "传感器数据错误");
        return ESP_ERR_INVALID_STATE;
    }

    // 数据转换
    uint32_t raw_hum = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
    uint32_t raw_temp = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];

    *humidity = (raw_hum * 100.0) / 1048576.0;
    *temperature = (raw_temp * 200.0) / 1048576.0 - 50.0;

    return ESP_OK;
}

void app_main(void)
{
    ESP_ERROR_CHECK(i2c_master_init());
    ESP_LOGI(TAG, "I2C 初始化成功");

    ESP_ERROR_CHECK(aht20_init());
    ESP_LOGI(TAG, "AHT20 初始化成功");

    while (1)
    {
        float temp, humi;
        if (aht20_read_data(&temp, &humi) == ESP_OK)
        {
            ESP_LOGI(TAG, "温度: %.2f°C, 湿度: %.2f%%", temp, humi);
        }
        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

ESP32-C3 第2版本

这一版是使用ESP-IDF 5.5.0版本编写的代码,使用了ESP-IDF的I2C驱动程序。

c

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/i2c_master.h"

#define I2C_MASTER_NUM I2C_NUM_0
#define I2C_MASTER_SCL_IO 8
#define I2C_MASTER_SDA_IO 9
#define I2C_MASTER_TIMEOUT_MS 1000

#define ATH20_ADDR_LEN I2C_ADDR_BIT_LEN_7
#define ATH20_ADDR 0x38


void ath20_init(i2c_master_dev_handle_t *dev_handle)
{
    // TODO: Implement initialization code for the ATH20 sensor
    uint8_t cmd[3] = {0xBE, 0x08, 0x00}; // 初始化命令
    i2c_master_transmit(*dev_handle, cmd, 3, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
    vTaskDelay(pdMS_TO_TICKS(300)); // 等待校准完成
}

esp_err_t ath20_read(i2c_master_dev_handle_t *dev_handle, float *temperature, float *humidity)
{
    // TODO: Implement reading code for the ATH20 sensor
    uint8_t cmd[3] = {0xAC, 0x33, 0x00}; // 读取数据命令
    uint8_t data[6]; // 存储读取的数据
    // i2c_master_transmit_receive(*dev_handle, cmd, 1, data, 6, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
    i2c_master_transmit(*dev_handle, cmd, 3, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
    vTaskDelay(pdMS_TO_TICKS(80)); // 等待测量完成
    i2c_master_receive(*dev_handle, data, 6, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
    // 解析数据
    // 校验状态位
    if ((data[0] & 0x80) != 0)
    {
        ESP_LOGE("ATH20_DEMO", "传感器数据错误");
        return ESP_ERR_INVALID_STATE;
    }

    // 数据转换
    uint32_t raw_hum = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
    uint32_t raw_temp = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];

    *humidity = (raw_hum * 100.0) / 1048576.0;
    *temperature = (raw_temp * 200.0) / 1048576.0 - 50.0;

    return ESP_OK;
}



void app_main(void)
{

    i2c_master_bus_config_t i2c_mst_config = {
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .i2c_port = I2C_MASTER_NUM,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .glitch_ignore_cnt = 7,
        .flags.enable_internal_pullup = true,
    };

    i2c_master_bus_handle_t bus_handle;
    ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));

    i2c_device_config_t dev_cfg = {
        .dev_addr_length = ATH20_ADDR_LEN,
        .device_address = ATH20_ADDR,
        .scl_speed_hz = 100000,
    };

    i2c_master_dev_handle_t dev_handle;
    ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));


    ath20_init(&dev_handle);

    while (1)
    {
        float temp, humi;
        if(ath20_read(&dev_handle, &temp, &humi) == ESP_OK)
        {
            printf("温度: %.2f C, 湿度: %.2f %%\n", temp, humi);
        }
        vTaskDelay(pdMS_TO_TICKS(1000)); // 等待1秒
    }
    
}

STC8G 第1版

需要先引用 i2c的头文件. 转至stc8g I2C模块

c
#include "stc8g.h"
#include "i2c.h"


#define AHT20_ADDR 0x38  // AHT20的I2C地址

static void DelayMs(unsigned int ms) {
    unsigned int i, j;
    for(i = 0; i < ms; i++) {
        for(j = 0; j < 1000; j++) {
            _nop_();
        }
    }
}

void ATH20_Init(void)
{
	I2C_Init();
}


// AHT20读取温湿度
bit AHT20_Read(float *temperature, float *humidity) {
    unsigned char buf[6];
    unsigned long temp_raw = 0, humi_raw = 0;
    
    // 发送触发测量命令
    I2C_Start();
    if(!I2C_WriteByte(AHT20_ADDR << 1)) { // AHT20地址 + 写模式
        I2C_Stop();
        return 0;
    }
    if(!I2C_WriteByte(0xAC)) {      // 触发测量命令
        I2C_Stop();
        return 0;
    }
    if(!I2C_WriteByte(0x33)) {      // 参数1
        I2C_Stop();
        return 0;
    }
    if(!I2C_WriteByte(0x00)) {      // 参数2
        I2C_Stop();
        return 0;
    }
    I2C_Stop();
    
    DelayMs(100); // 等待测量完成
    
    // 读取数据
    I2C_Start();
    if(!I2C_WriteByte(AHT20_ADDR << 1 | 0x01)) { // AHT20地址 + 读模式
        I2C_Stop();
        return 0;
    }
    buf[0] = I2C_ReadByte(1); // 状态字
    buf[1] = I2C_ReadByte(1); // 湿度高字节
    buf[2] = I2C_ReadByte(1); // 湿度低字节
    buf[3] = I2C_ReadByte(1); // 湿度/温度混合字节
    buf[4] = I2C_ReadByte(1); // 温度高字节
    buf[5] = I2C_ReadByte(0); // 温度低字节
    I2C_Stop();
    
    // 计算温湿度值
    humi_raw = ((unsigned long)buf[1] << 12) | ((unsigned long)buf[2] << 4) | (buf[3] >> 4);
    temp_raw = ((unsigned long)(buf[3] & 0x0F) << 16) | ((unsigned long)buf[4] << 8) | buf[5];
    
    *humidity = (float)humi_raw * 100 / 1048576;
    *temperature = (float)temp_raw * 200 / 1048576 - 50;
    return 1;
}

5. 开发事项

本程序需要用到 smbus2 模块, 这个模块需要事先进行安装

  1. 更新包列表:sudo apt-get update
  2. 安装 python3-pip:sudo apt-get install python3-pip
  3. 使用 pip3 安装 smbus2:sudo pip3 install smbus2