diff --git a/src/AHT10.cpp b/src/AHT10.cpp new file mode 100644 index 0000000..dfb60ba --- /dev/null +++ b/src/AHT10.cpp @@ -0,0 +1,369 @@ +/***************************************************************************************************/ +/* + This is an Arduino library for Aosong ASAIR AHT10, AHT15 Digital Humidity & Temperature Sensor + + written by : enjoyneering79 + sourse code: https://github.com/enjoyneering/ + + + This chip uses I2C bus to communicate, specials pins are required to interface + Board: SDA SCL Level + Uno, Mini, Pro, ATmega168, ATmega328..... A4 A5 5v + Mega2560................................. 20 21 5v + Due, SAM3X8E............................. 20 21 3.3v + Leonardo, Micro, ATmega32U4.............. 2 3 5v + Digistump, Trinket, ATtiny85............. 0/physical pin no.5 2/physical pin no.7 5v + Blue Pill, STM32F103xxxx boards.......... PB7 PB6 3.3v/5v + ESP8266 ESP-01........................... GPIO0/D5 GPIO2/D3 3.3v/5v + NodeMCU 1.0, WeMos D1 Mini............... GPIO4/D2 GPIO5/D1 3.3v/5v + ESP32.................................... GPIO21/D21 GPIO22/D22 3.3v + + Frameworks & Libraries: + ATtiny Core - https://github.com/SpenceKonde/ATTinyCore + ESP32 Core - https://github.com/espressif/arduino-esp32 + ESP8266 Core - https://github.com/esp8266/Arduino + STM32 Core - https://github.com/stm32duino/Arduino_Core_STM32 + - https://github.com/rogerclarkmelbourne/Arduino_STM32 + + GNU GPL license, all text above must be included in any redistribution, + see link for details - https://www.gnu.org/licenses/licenses.html +*/ +/***************************************************************************************************/ + +#include "AHT10.h" + + +/**************************************************************************/ +/* + Constructor +*/ +/**************************************************************************/ +AHT10::AHT10(uint8_t address) +{ + _address = address; +} + +/**************************************************************************/ +/* + begin() + + Initialize I2C & configure the sensor, call this function before + doing anything else + + NOTE: + - Wire.endTransmission() returned value: + - 0 success + - 1 data too long to fit in transmit data16 + - 2 received NACK on transmit of address + - 3 received NACK on transmit of data + - 4 other error +*/ +/**************************************************************************/ +#if defined(ESP8266) +bool AHT10::begin(uint8_t sda, uint8_t scl) +{ + Wire.begin(sda, scl); + Wire.setClock(100000); //experimental! ESP8266 I2C bus speed: 50kHz..400kHz/50000..400000, default 100000 + Wire.setClockStretchLimit(230); //experimental! default 230usec +#else +bool AHT10::begin(void) +{ + Wire.begin(); + Wire.setClock(100000); //experimental! AVR I2C bus speed: 31kHz..400kHz/31000..400000, default 100000 +#endif + + /* sensor initialization, load factory calibration coeff */ + return enableFactoryCalCoeff(); +} + + +/**************************************************************************/ +/* + readRawData() + + Read raw measurment data from sensor over I2C +*/ +/**************************************************************************/ +uint8_t AHT10::readRawData() +{ + /* send measurment command */ + Wire.beginTransmission(_address); + #if ARDUINO >= 100 + Wire.write(AHT10_START_MEASURMENT_CMD); + #else + Wire.send(AHT10_START_MEASURMENT_CMD); + #endif + if (Wire.endTransmission(true) != 0) return AHT10_ERROR; //error handler, collision on I2C bus + + if (getBusyBit() != 0x00) delay(AHT10_MEASURMENT_DELAY); //measurement delay + + /* read 6-bytes from sensor */ + #if defined(_VARIANT_ARDUINO_STM32_) + Wire.requestFrom(_address, 6); + #else + Wire.requestFrom(_address, 6, true); //true - send stop after transmission & release I2C bus + #endif + if (Wire.available() != 6) + { + _rawDataBuffer[0] = AHT10_ERROR; //for condition when AHT10_FORCE_READ_DATA is not used + return AHT10_ERROR; //check rxBuffer & error handler, collision on the i2c bus + } + + /* read 6 bytes from "wire.h" rxBuffer */ + #if ARDUINO >= 100 + for (uint8_t i = 0; i < 6 ; i++) + { + _rawDataBuffer[i] = Wire.read(); + } + #else + for (uint8_t i = 0; i < 6 ; i++) + { + _rawDataBuffer[i] = Wire.receive(); + } + #endif + + return true; +} + + +/**************************************************************************/ +/* + readTemperature() + + Read temperature, °C + + NOTE: + - temperature range -40°C..+80°C + - temperature resolution 0.01°C + - temperature accuracy ±0.3°C +*/ +/**************************************************************************/ +float AHT10::readTemperature(bool readI2C) +{ + if (readI2C == AHT10_FORCE_READ_DATA) + { + if (readRawData() == AHT10_ERROR) return AHT10_ERROR; //force to read data to _rawDataBuffer & error handler + } + + if (_rawDataBuffer[0] == AHT10_ERROR) return AHT10_ERROR; //error handler, collision on I2C bus + + uint32_t temperature = ((uint32_t)(_rawDataBuffer[3] & 0x0F) << 16) | ((uint16_t)_rawDataBuffer[4] << 8) | _rawDataBuffer[5]; //20-bit raw temperature data + + return (float)temperature * 200 / 1048576 - 50; +} + + +/**************************************************************************/ +/* + readHumidity() + + Read relative humidity, % + + NOTE: + - Prolonged exposure for 60 hours at humidity > 80% can lead to a + temporary drift of the signal +3%. Sensor slowly returns to the + calibrated state at normal operating conditions. + - relative humidity range 0%..100% + - relative humidity resolution 0.024% + - relative humidity accuracy ±2% +*/ +/**************************************************************************/ +float AHT10::readHumidity(bool readI2C) +{ + if (readI2C == AHT10_FORCE_READ_DATA) + { + if (readRawData() == AHT10_ERROR) return AHT10_ERROR; //force to read data to _rawDataBuffer & error handler + } + + if (_rawDataBuffer[0] == AHT10_ERROR) return AHT10_ERROR; //error handler, collision on I2C bus + + uint32_t rawData = (((uint32_t)_rawDataBuffer[1] << 16) | ((uint16_t)_rawDataBuffer[2] << 8) | (_rawDataBuffer[3])) >> 4; //20-bit raw humidity data + + float humidity = (float)rawData * 100 / 1048576; + + if (humidity < 0) return 0; + if (humidity > 100) return 100; + return humidity; +} + + +/**************************************************************************/ +/* + softReset() + + Restart sensor, without power off + + NOTE: + - takes ~20ms + - all registers restores to default +*/ +/**************************************************************************/ +bool AHT10::softReset(void) +{ + Wire.beginTransmission(_address); + + #if ARDUINO >= 100 + Wire.write(AHT10_SOFT_RESET_CMD); + #else + Wire.send(AHT10_SOFT_RESET_CMD); + #endif + + if (Wire.endTransmission(true) != 0) return false; //safety check, make sure sensor reset + + delay(AHT10_SOFT_RESET_DELAY); + + return enableFactoryCalCoeff(); //reinitialize sensor registers after reset +} + + +/**************************************************************************/ +/* + setNormalMode() + + Set normal measurment mode + + NOTE: + - one measurement & power down +*/ +/**************************************************************************/ +bool AHT10::setNormalMode(void) +{ + Wire.beginTransmission(_address); + + #if ARDUINO >= 100 + Wire.write(AHT10_INIT_CMD); + Wire.write(AHT10_INIT_CAL_ENABLE); + #else + Wire.send(AHT10_INIT_CAL_ENABLE); + #endif + + if (Wire.endTransmission(true) != 0) return false; //safety check, make sure transmission complete + + delay(AHT10_COMMAND_DELAY); + + return true; +} + + +/**************************************************************************/ +/* + setCycleMode() + + Set cycle measurment mode + + NOTE: + - continuous measurement +*/ +/**************************************************************************/ +bool AHT10::setCycleMode(void) +{ + Wire.beginTransmission(_address); + + #if ARDUINO >= 100 + Wire.write(AHT10_INIT_CMD); + Wire.write(AHT10_INIT_CAL_ENABLE | AHT10_INIT_CYCLE_MODE); + #else + Wire.send(AHT10_INIT_CAL_ENABLE | AHT10_INIT_CYCLE_MODE); + #endif + + if (Wire.endTransmission(true) != 0) return false; //safety check, make sure transmission complete + + delay(AHT10_COMMAND_DELAY); + + return true; +} + + +/**************************************************************************/ +/* + readStatusByte() + + Read status byte from sensor over I2C +*/ +/**************************************************************************/ +uint8_t AHT10::readStatusByte() +{ + #if defined(_VARIANT_ARDUINO_STM32_) + Wire.requestFrom(_address, 1); + #else + Wire.requestFrom(_address, 1, true); //true - send stop after transmission & release I2C bus + #endif + if (Wire.available() != 1) return AHT10_ERROR; //check rxBuffer & error handler, collision on I2C bus + + /* read byte from "wire.h" rxBuffer */ + #if ARDUINO >= 100 + return Wire.read(); + #else + return Wire.receive(); + #endif +} + + +/**************************************************************************/ +/* + getCalibrationBit() + + Read Calibration bit from status byte + + NOTE: + - 0, factory calibration coeff disabled + - 1, factory calibration coeff loaded +*/ +/**************************************************************************/ +uint8_t AHT10::getCalibrationBit(bool readI2C) +{ + if (readI2C == AHT10_FORCE_READ_DATA) _rawDataBuffer[0] = readStatusByte(); //force to read status byte + + if (_rawDataBuffer[0] != AHT10_ERROR) return bitRead(_rawDataBuffer[0], 3); //get 3-rd bit + return AHT10_ERROR; +} + + +/**************************************************************************/ +/* + enableFactoryCalCoeff() + + Load factory calibration coefficients +*/ +/**************************************************************************/ +bool AHT10::enableFactoryCalCoeff() +{ + /* load factory calibration coeff */ + Wire.beginTransmission(_address); + + #if ARDUINO >= 100 + Wire.write(AHT10_INIT_CMD); + Wire.write(AHT10_INIT_CAL_ENABLE); + #else + Wire.send(AHT10_INIT_CMD); + Wire.send(AHT10_INIT_CAL_ENABLE); + #endif + + if (Wire.endTransmission(true) != 0) return false; //safety check, make sure transmission complete + + delay(AHT10_COMMAND_DELAY); + + /*check calibration enable */ + if (getCalibrationBit(AHT10_FORCE_READ_DATA) == 0x01) return true; + return false; +} + + +/**************************************************************************/ +/* + getBusyBit() + + Read busy bit from status byte + + NOTE: + - 0, sensor idle + - 1, sensor busy +*/ +/**************************************************************************/ +uint8_t AHT10::getBusyBit(bool readI2C) +{ + if (readI2C == AHT10_FORCE_READ_DATA) _rawDataBuffer[0] = readStatusByte(); //force to read status byte + + if (_rawDataBuffer[0] != AHT10_ERROR) return bitRead(_rawDataBuffer[0], 7); //get 7-th bit + return AHT10_ERROR; +}