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. 湿度计算原理
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. 温度计算原理
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
。
通过以下步骤转换:- 将归一化值乘以
200
,覆盖0°C ~ 200°C
的范围。 - 减去
50
,将范围偏移到-50°C ~ +150°C
。
公式:
温度=(220temperature_raw)×200−50
- 将归一化值乘以
4. 具体的代码:
以下是 Orangepi zero 3 的 Python 代码,用于读取 AHT20 温湿度传感器数据:
运行条件是安装了 smbus2 库
apt install python3.10-venv # 安装虚拟环境
python3 -m venv aht20 # 创建虚拟环境 aht20
source aht20/bin/activate # 激活虚拟环境
pip install smbus2 # 安装 smbus2 库
执行了以上的命令后,就可以使用 aht20 虚拟环境了。并可以正常运行下面的代码:
使用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 温湿度传感器数据:
#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驱动程序。
#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模块
#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 模块, 这个模块需要事先进行安装
- 更新包列表:
sudo apt-get update
- 安装 python3-pip:
sudo apt-get install python3-pip
- 使用 pip3 安装 smbus2:
sudo pip3 install smbus2