Here is the code:
code.ino
/***************************************************************************************************/
/*
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>
#include <Wire.h>
#include “HT_SSD1306Wire.h”
static SSD1306Wire display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED); // addr , freq , i2c group , resolution , rst
uint8_t readStatus = 0;
AHT10 myAHT10(AHT10_ADDRESS_0X38);
float decimal = 11.22;
void VextON(void)
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, LOW);
}
void VextOFF(void)
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, HIGH);
}
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println();
VextON();
delay(100);
while (myAHT10.begin() != true)
{
Serial.println(F("AHT10 not connected or fail to load calibration coefficient")); //(F()) save string to flash & keeps dynamic memory free
delay(5000);
}
Serial.println(F(“AHT10 OK”));
//Wire.setClock(400000); //experimental I2C speed! 400KHz, default 100KHz
Wire.begin();
display.init();
display.clear();
display.setFont(ArialMT_Plain_10);
display.display();
}
void loop()
{
/* DEMO - 1, every temperature or humidity call will read 6 bytes over I2C, total 12 bytes */
Serial.println(F(“DEMO 1: read 12-bytes, show 255 if communication error is occurred”));
Serial.print(F(“Temperature: “)); Serial.print(myAHT10.readTemperature()); Serial.println(F(” ±0.3C”)); //by default “AHT10_FORCE_READ_DATA”
Serial.print(F(“Humidity…: “)); Serial.print(myAHT10.readHumidity()); Serial.println(F(” ±2%”)); //by default “AHT10_FORCE_READ_DATA”
display.clear();
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.drawString(0, 0, “Decimal:”);
display.drawString(50, 0, String(decimal, 2));
display.display();
delay(1000);
/* DEMO - 2, temperature call will read 6 bytes via I2C, humidity will use same 6 bytes */
//Serial.println(F(“DEMO 2: read 6 byte, show 255 if communication error is occurred”));
//Serial.print(F(“Temperature: “)); Serial.print(myAHT10.readTemperature(AHT10_FORCE_READ_DATA)); Serial.println(F(” ±0.3C”));
//Serial.print(F(“Humidity…: “)); Serial.print(myAHT10.readHumidity(AHT10_USE_READ_DATA)); Serial.println(F(” ±2%”));
/* DEMO - 3, same as demo2
but different call procedure */
//Serial.println(F(“DEMO 3: read 6-bytes, show 255 if communication error is occurred”));
//readStatus = myAHT10.readRawData(); //read 6 bytes from AHT10 over I2C
/*if (readStatus != AHT10_ERROR)
{
Serial.print(F("Temperature: ")); Serial.print(myAHT10.readTemperature(AHT10_USE_READ_DATA)); Serial.println(F(" +-0.3C"));
Serial.print(F("Humidity...: ")); Serial.print(myAHT10.readHumidity(AHT10_USE_READ_DATA)); Serial.println(F(" +-2%"));
}
else
{
Serial.print(F("Failed to read - reset: "));
Serial.println(myAHT10.softReset()); //reset 1-success, 0-failed
}*/
//delay(10000); //recomended polling frequency 8sec…30sec
}
AHT10.cpp
/***************************************************************************************************/
/*
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, ASAIR_I2C_SENSOR sensorName)
{
_address = address;
_sensorName = sensorName;
}
/**************************************************************************/
/*
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
delay(AHT10_POWER_ON_DELAY); //wait for sensor to initialize
setNormalMode(); //one measurement+sleep mode
return enableFactoryCalCoeff(); //load factory calibration coeff
}
/**************************************************************************/
/*
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); //send measurment command
Wire.write(AHT10_DATA_MEASURMENT_CMD); //send measurment parameter
Wire.write(AHT10_DATA_NOP); //send measurment parameter
#else
Wire.send(AHT10_START_MEASURMENT_CMD);
Wire.send(AHT10_DATA_MEASURMENT_CMD);
Wire.send(AHT10_DATA_NOP);
#endif
if (Wire.endTransmission(true) != 0) return AHT10_ERROR; //error handler, collision on I2C bus
if (getCalibrationBit() != 0x01) return AHT10_ERROR; //error handler, calibration coefficient turned off
if (getBusyBit(AHT10_USE_READ_DATA) != 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_USE_READ_DATA is 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 * 0.000191 - 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 * 0.000095;
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);
setNormalMode(); //reinitialize sensor registers after reset
return enableFactoryCalCoeff(); //reinitialize sensor registers after reset
}
/**************************************************************************/
/*
setNormalMode()
Set normal measurment mode
NOTE:
- one measurement & power down??? no info in datasheet!!!
*/
/**************************************************************************/
bool AHT10::setNormalMode(void)
{
Wire.beginTransmission(_address);
#if (ARDUINO) >= 100
Wire.write(AHT10_NORMAL_CMD);
Wire.write(AHT10_DATA_NOP);
Wire.write(AHT10_DATA_NOP);
#else
Wire.send(AHT10_NORMAL_CMD);
Wire.send(AHT10_DATA_NOP);
Wire.send(AHT10_DATA_NOP);
#endif
if (Wire.endTransmission(true) != 0) return false; //safety check, make sure transmission complete
delay(AHT10_CMD_DELAY);
return true;
}
/**************************************************************************/
/*
setCycleMode()
Set cycle measurment mode
NOTE:
- continuous measurement
*/
/**************************************************************************/
bool AHT10::setCycleMode(void)
{
Wire.beginTransmission(_address);
#if (ARDUINO) >= 100
if (_sensorName != AHT20_SENSOR) Wire.write(AHT10_INIT_CMD); //set command mode
else Wire.write(AHT20_INIT_CMD);
Wire.write(AHT10_INIT_CYCLE_MODE | AHT10_INIT_CAL_ENABLE); //0,[0,1],0,[1],0,0,0
Wire.write(AHT10_DATA_NOP);
#else
if (_sensorName != AHT20_SENSOR) Wire.send(AHT10_INIT_CMD);
else Wire.send(AHT20_INIT_CMD);
Wire.send(AHT10_INIT_CYCLE_MODE | AHT10_INIT_CAL_ENABLE);
Wire.send(AHT10_DATA_NOP);
#endif
if (Wire.endTransmission(true) != 0) return false; //safety check, make sure transmission complete
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
if (_sensorName != AHT20_SENSOR) Wire.write(AHT10_INIT_CMD); //set command mode
else Wire.write(AHT20_INIT_CMD);
Wire.write(AHT10_INIT_CAL_ENABLE); //0,0,0,0,[1],0,0,0
Wire.write(AHT10_DATA_NOP); //0,0,0,0,0,0,0,0
#else
if (_sensorName != AHT20_SENSOR) Wire.send(AHT10_INIT_CMD);
else Wire.send(AHT20_INIT_CMD);
Wire.send(AHT10_INIT_CAL_ENABLE);
Wire.send(AHT10_DATA_NOP);
#endif
if (Wire.endTransmission(true) != 0) return false; //safety check, make sure transmission complete
delay(AHT10_CMD_DELAY);
/*check calibration enable */
if (getCalibrationBit() == 0x01) return true;
return false;
}
/**************************************************************************/
/*
getBusyBit()
Read busy bit from status byte
NOTE:
- 0, sensor idle & sleeping
- 1, sensor busy & in measurement state
*/
/**************************************************************************/
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;
}
AHT10.h
/***************************************************************************************************/
/*
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
*/
/***************************************************************************************************/
#ifndef AHT10_h
#define AHT10_h
#if defined(ARDUINO) && ((ARDUINO) >= 100) //arduino core v1.0 or later
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
#if defined(AVR)
#include <avr/pgmspace.h> //for Arduino AVR PROGMEM support
#elif defined(ESP8266)
#include <pgmspace.h> //for Arduino ESP8266 PROGMEM support
#elif defined(VARIANT_ARDUINO_STM32)
#include <avr/pgmspace.h> //for Arduino STM32 PROGMEM support
#endif
#include <Wire.h>
#define AHT10_ADDRESS_0X38 0x38 //chip I2C address no.1 for AHT10/AHT15/AHT20, address pin connected to GND
#define AHT10_ADDRESS_0X39 0x39 //chip I2C address no.2 for AHT10 only, address pin connected to Vcc
#define AHT10_INIT_CMD 0xE1 //initialization command for AHT10/AHT15
#define AHT20_INIT_CMD 0xBE //initialization command for AHT20
#define AHT10_START_MEASURMENT_CMD 0xAC //start measurment command
#define AHT10_NORMAL_CMD 0xA8 //normal cycle mode command, no info in datasheet!!!
#define AHT10_SOFT_RESET_CMD 0xBA //soft reset command
#define AHT10_INIT_NORMAL_MODE 0x00 //enable normal mode
#define AHT10_INIT_CYCLE_MODE 0x20 //enable cycle mode
#define AHT10_INIT_CMD_MODE 0x40 //enable command mode
#define AHT10_INIT_CAL_ENABLE 0x08 //load factory calibration coeff
#define AHT10_DATA_MEASURMENT_CMD 0x33 //no info in datasheet!!! my guess it is DAC resolution, saw someone send 0x00 instead
#define AHT10_DATA_NOP 0x00 //no info in datasheet!!!
#define AHT10_MEASURMENT_DELAY 80 //at least 75 milliseconds
#define AHT10_POWER_ON_DELAY 40 //at least 20…40 milliseconds
#define AHT10_CMD_DELAY 350 //at least 300 milliseconds, no info in datasheet!!!
#define AHT10_SOFT_RESET_DELAY 20 //less than 20 milliseconds
#define AHT10_FORCE_READ_DATA true //force to read data
#define AHT10_USE_READ_DATA false //force to use data from previous read
#define AHT10_ERROR 0xFF //returns 255, if communication error is occurred
typedef enum : uint8_t
{
AHT10_SENSOR = 0x00,
AHT15_SENSOR = 0x01,
AHT20_SENSOR = 0x02
}
ASAIR_I2C_SENSOR;
class AHT10
{
public:
AHT10(uint8_t address = AHT10_ADDRESS_0X38, ASAIR_I2C_SENSOR = AHT10_SENSOR);
#if defined(ESP8266)
bool begin(uint8_t sda = SDA, uint8_t scl = SCL);
#else
bool begin();
#endif
uint8_t readRawData();
float readTemperature(bool readI2C = AHT10_FORCE_READ_DATA);
float readHumidity(bool readI2C = AHT10_FORCE_READ_DATA);
bool softReset();
bool setNormalMode();
bool setCycleMode();
private:
uint8_t _address;
ASAIR_I2C_SENSOR _sensorName;
uint8_t _rawDataBuffer[6] = {AHT10_ERROR, 0, 0, 0, 0, 0};
uint8_t readStatusByte();
uint8_t getCalibrationBit(bool readI2C = AHT10_FORCE_READ_DATA);
bool enableFactoryCalCoeff();
uint8_t getBusyBit(bool readI2C = AHT10_FORCE_READ_DATA);
};
#endif