add low level AHT20 library
This commit is contained in:
parent
9072b24e55
commit
e5fda49f2d
|
@ -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
|
|
@ -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))
|
Loading…
Reference in New Issue