add low level AHT20 library

This commit is contained in:
Nicolò Balzarotti 2023-03-14 22:45:55 +01:00
parent 9072b24e55
commit e5fda49f2d
2 changed files with 213 additions and 0 deletions

120
AHT20.py Normal file
View File

@ -0,0 +1,120 @@
from smbus2 import SMBus
import time
from crc8_helper import AHT20_crc8_check
def get_normalized_bit(value, bit_index):
# Return only one bit from value indicated in bit_index
return (value >> bit_index) & 1
AHT20_I2CADDR = 0x38
AHT20_CMD_SOFTRESET = [0xBA]
AHT20_CMD_INITIALIZE = [0xBE, 0x08, 0x00]
AHT20_CMD_MEASURE = [0xAC, 0x33, 0x00]
AHT20_STATUSBIT_BUSY = 7 # The 7th bit is the Busy indication bit. 1 = Busy, 0 = not.
AHT20_STATUSBIT_CALIBRATED = 3 # The 3rd bit is the CAL (calibration) Enable bit. 1 = Calibrated, 0 = not
class AHT20:
# I2C communication driver for AHT20, using only smbus2
def __init__(self, BusNum=1):
# Initialize AHT20
self.BusNum = BusNum
self.cmd_soft_reset()
# Check for calibration, if not done then do and wait 10 ms
if not self.get_status_calibrated == 1:
self.cmd_initialize()
while not self.get_status_calibrated() == 1:
time.sleep(0.01)
def cmd_soft_reset(self):
# Send the command to soft reset
with SMBus(self.BusNum) as i2c_bus:
i2c_bus.write_i2c_block_data(AHT20_I2CADDR, 0x0, AHT20_CMD_SOFTRESET)
time.sleep(0.04) # Wait 40 ms after poweron
return True
def cmd_initialize(self):
# Send the command to initialize (calibrate)
with SMBus(self.BusNum) as i2c_bus:
i2c_bus.write_i2c_block_data(AHT20_I2CADDR, 0x0 , AHT20_CMD_INITIALIZE)
return True
def cmd_measure(self):
# Send the command to measure
with SMBus(self.BusNum) as i2c_bus:
i2c_bus.write_i2c_block_data(AHT20_I2CADDR, 0, AHT20_CMD_MEASURE)
time.sleep(0.08) # Wait 80 ms after measure
return True
def get_status(self):
# Get the full status byte
with SMBus(self.BusNum) as i2c_bus:
return i2c_bus.read_i2c_block_data(AHT20_I2CADDR, 0x0, 1)[0]
return True
def get_status_calibrated(self):
# Get the calibrated bit
return get_normalized_bit(self.get_status(), AHT20_STATUSBIT_CALIBRATED)
def get_status_busy(self):
# Get the busy bit
return get_normalized_bit(self.get_status(), AHT20_STATUSBIT_BUSY)
def get_measure(self):
# Get the full measure
# Command a measure
self.cmd_measure()
# Check if busy bit = 0, otherwise wait 80 ms and retry
while self.get_status_busy() == 1:
time.sleep(0.08) # Wait 80 ns
# TODO: do CRC check
# Read data and return it
with SMBus(self.BusNum) as i2c_bus:
return i2c_bus.read_i2c_block_data(AHT20_I2CADDR, 0x0, 7)
def get_measure_CRC8(self):
"""
This function will calculate crc8 code with G(x) = x8 + x5 + x4 + 1 -> 0x131(0x31), Initial value = 0xFF. No XOROUT.
return: all_data (1 bytes status + 2.5 byes humidity + 2.5 bytes temperature + 1 bytes crc8 code), isCRC8_pass
"""
all_data = self.get_measure()
isCRC8_pass = AHT20_crc8_check(all_data)
return all_data, isCRC8_pass
def get_temperature(self):
# Get a measure, select proper bytes, return converted data
measure = self.get_measure()
measure = ((measure[3] & 0xF) << 16) | (measure[4] << 8) | measure[5]
measure = measure / (pow(2,20))*200-50
return measure
def get_temperature_crc8(self):
isCRC8Pass = False
while (not isCRC8Pass):
measure, isCRC8Pass = self.get_measure_CRC8()
time.sleep(80 * 10**-3)
measure = ((measure[3] & 0xF) << 16) | (measure[4] << 8) | measure[5]
measure = measure / (pow(2,20))*200-50
return measure
def get_humidity(self):
# Get a measure, select proper bytes, return converted data
measure = self.get_measure()
measure = (measure[1] << 12) | (measure[2] << 4) | (measure[3] >> 4)
measure = measure * 100 / pow(2,20)
return measure
def get_humidity_crc8(self):
isCRC8Pass = False
while (not isCRC8Pass):
measure, isCRC8Pass = self.get_measure_CRC8()
time.sleep(80 * 10**-3)
measure = (measure[1] << 12) | (measure[2] << 4) | (measure[3] >> 4)
measure = measure * 100 / pow(2,20)
return measure

93
crc8_helper.py Normal file
View File

@ -0,0 +1,93 @@
# Usage: AHT20 crc8 checker.
# A total of 6 * 8 bits data need to check. G(x) = x8 + x5 + x4 + 1 -> 0x131(0x31), Initial value = 0xFF. No XOROUT.
# Author: XU Zifeng.
# Email: zifeng.xu@foxmail.com
N_DATA = 6
# 1 * 8 bits CRC
N_CRC = 1
# Initial value. Equal to bit negation the first data (status of AHT20)
INIT = 0xFF
# Useful value to help calculate
LAST_8_bit = 0xFF
# Devide number retrieve from CRC-8 MAXIM G(x) = x8 + x5 + x4 + 1
CRC_DEVIDE_NUMBER = 0x131
# Data and CRC taken from AHT20, use this for testing?
TEST_DATA = [[28, 184, 245, 165, 156, 208, 163], [28, 185, 16, 149, 156, 83, 112], [
28, 184, 249, 85, 156, 114, 213], [28, 185, 9, 53, 156, 54, 45], [28, 185, 70, 117, 156, 189, 33], [28, 185, 64, 165, 156, 61, 209]]
def mod2_division_8bits(a, b, number_of_bytes, init_value):
"calculate mod2 division in 8 bits. a mod b. init_value is for crc8 init value."
head_of_a = 0x80
# Processiong a
a = a << 8
# Preprocessing head_of_a
for i in range(0, number_of_bytes):
head_of_a = head_of_a << 8
b = b << 8
init_value = init_value << 8
a = a ^ init_value
while (head_of_a > 0x80):
# Find a 1
if (head_of_a & a):
head_of_a = head_of_a >> 1
b = b >> 1
a = a ^ b
else:
head_of_a = head_of_a >> 1
b = b >> 1
# This will show calculate the remainder
# print("a:{0}\thead of a:{1}\tb:{2}".format(
# bin(a), bin(head_of_a), bin(b)))
return a
def AHT20_crc8_calculate(all_data_int):
init_value = INIT
# Preprocess all the data and CRCCode from AHT20
data_from_AHT20 = 0x00
# Preprocessing the first data (status)
# print(bin(data_from_AHT20))
for i_data in range(0, len(all_data_int)):
data_from_AHT20 = (data_from_AHT20 << 8) | all_data_int[i_data]
# print(bin(data_from_AHT20))
mod_value = mod2_division_8bits(
data_from_AHT20, CRC_DEVIDE_NUMBER, len(all_data_int), init_value)
# print(mod_value)
return mod_value
def AHT20_crc8_check(all_data_int):
"""
The input data shoule be:
Status Humidity0 Humidity1 Humidity2|Temperature0 Temperature1 Temperature2 CRCCode.
In python's int64.
"""
mod_value = AHT20_crc8_calculate(all_data_int[:-1])
if (mod_value == all_data_int[-1]):
return True
else:
return False
def CRC8_check(all_data_int, init_value=0x00):
divider = 0x107
DATA_FOR_CHECK = all_data_int[0]
for data in all_data_int[1:-1]:
DATA_FOR_CHECK = (DATA_FOR_CHECK << 8) | data
remainder = mod2_division_8bits(
DATA_FOR_CHECK, divider, len(all_data_int) - 1, init_value)
if (remainder == all_data_int[-1]):
return True
else:
return False
if __name__ == "__main__":
print(CRC8_check([0x66, 0x44, 0x33, 0x22, 0x24], 0))
for data in TEST_DATA:
print(AHT20_crc8_check(data))