diff --git a/AHT20.py b/AHT20.py new file mode 100644 index 0000000..c03599e --- /dev/null +++ b/AHT20.py @@ -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 diff --git a/crc8_helper.py b/crc8_helper.py new file mode 100644 index 0000000..7bd6831 --- /dev/null +++ b/crc8_helper.py @@ -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))