├── library.properties ├── library.json ├── keywords.txt ├── README.md ├── examples ├── AHT20_Serial │ └── AHT20_Serial.ino └── AHT10_Serial │ └── AHT10_Serial.ino └── src ├── AHTxx.h └── AHTxx.cpp /library.properties: -------------------------------------------------------------------------------- 1 | name = AHT1x/AHT2x 2 | version = 1.2.0 3 | author = Enjoyneering 4 | maintainer = Enjoyneering 5 | sentence = Aosong ASAIR AHT1x/AHT2x, Temperature & Humidity Sensor 6 | paragraph = Aosong ASAIR AHT1x/AHT2x, I2C Temperature & Humidity Sensor 7 | category = Sensors 8 | url = https://github.com/enjoyneering/AHTxx 9 | architectures = * 10 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AHTxx", 3 | "keywords": "humidity, temperature, aht10, aht15, aht20, aht21, aht25, aosong, asair, i2c", 4 | "description": "AHT1x/AHT2x Digital Humidity & Temperature Sensor", 5 | "authors": 6 | { 7 | "name": "ejoyneering", 8 | "email": "enjoyneering@protonmail.com" 9 | }, 10 | "repository": 11 | { 12 | "type": "git", 13 | "url": "https://github.com/enjoyneering/AHTxx.git" 14 | }, 15 | "version": "1.2.0", 16 | "frameworks": "arduino", 17 | "platforms": "*" 18 | } 19 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map AHTxx 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ####################################### 10 | # Methods and Functions (KEYWORD2) 11 | ####################################### 12 | 13 | begin KEYWORD2 14 | readHumidity KEYWORD2 15 | readTemperature KEYWORD2 16 | setNormalMode KEYWORD2 17 | setCycleMode KEYWORD2 18 | setComandMode KEYWORD2 19 | softReset KEYWORD2 20 | getStatus KEYWORD2 21 | setType KEYWORD2 22 | 23 | 24 | ####################################### 25 | # Instances (KEYWORD2) 26 | ####################################### 27 | 28 | AHTxx KEYWORD2 29 | 30 | ####################################### 31 | # Constants (LITERAL1) 32 | ####################################### 33 | 34 | AHT1x_SENSOR LITERAL1 35 | AHT2x_SENSOR LITERAL1 36 | 37 | AHTXX_ADDRESS_X38 LITERAL1 38 | AHT10_ADDRESS_X39 LITERAL1 39 | 40 | AHTXX_FORCE_READ_DATA LITERAL1 41 | AHTXX_USE_READ_DATA LITERAL1 42 | 43 | AHTXX_NO_ERROR LITERAL1 44 | AHTXX_BUSY_ERROR LITERAL1 45 | AHTXX_ACK_ERROR LITERAL1 46 | AHTXX_DATA_ERROR LITERAL1 47 | AHTXX_CRC8_ERROR LITERAL1 48 | AHTXX_ERROR LITERAL1 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![license-badge][]][license] ![version] [![stars][]][stargazers] [![github-issues][]][issues] 2 | 3 | # Aosong ASAIR AHT1x/AHT2x 4 | 5 | This is an Arduino library for _Aosong ASAIR_ AHT10/AHT15/AHT20/AHT21/AHT25/AM2301**B**/AM2311**B** Digital Humidity & Temperature Sensor 6 | 7 | - AHT1x +1.8v..+3.6v, AHT2x +2.2v..+5.5v 8 | - AHT1x 0.25μA..320μA, AHT2x 0.25μA..980μA 9 | - temperature range -40°C..+85°C 10 | - humidity range 0%..100% 11 | - typical accuracy T ±0.3°C, RH ±2% **(1)** 12 | - typical resolution T 0.01°C, RH 0.024% 13 | - normal operating range T -20°C..+60°C, RH 10%..80% 14 | - maximum operating rage T -40°C..+80°C, RH 0%..100% 15 | - I²C bus speed 100KHz..400KHz, 10KHz recommended minimum 16 | - recommended measurement frequency 8sec..30sec **(2)** 17 | - recommended to route VDD or GND between I²C lines to reduce crosstalk between SCL & SDA 18 | - power supply pins must be decoupled with 100nF capacitor 19 | 20 | Supports all sensors features: 21 | - read humidity **(3)** 22 | - read temperature **(3)** 23 | - soft reset with sensor initialization 24 | - CRC calculation for AHT2x **(3)** 25 | 26 | Tested on: 27 | - Arduino AVR 28 | - Arduino ESP8266 29 | - Arduino ESP32 30 | - Arduino STM32 31 | - Arduino SAMD21 32 | 33 | **(1)** 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.
34 | **(2)** High frequency measurement causes the sensor to heat up, the interval must be greater than 1 second to keep self-heating below 0.1°C.
35 | **(3)** Library returns 255 if a communication error occurs, calibration coefficient is off or CRC doesn't match (for AHT2x only). 36 | 37 | [license-badge]: https://img.shields.io/badge/License-GPLv3-blue.svg 38 | [license]: https://choosealicense.com/licenses/gpl-3.0/ 39 | [version]: https://img.shields.io/badge/Version-1.2.1-green.svg 40 | [stars]: https://img.shields.io/github/stars/enjoyneering/AHTxx.svg 41 | [stargazers]: https://github.com/enjoyneering/AHTxx/stargazers 42 | [hit-count]: https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fenjoyneering%2FAHTxx&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false 43 | [github-issues]: https://img.shields.io/github/issues/enjoyneering/AHTxx.svg 44 | [issues]: https://github.com/enjoyneering/AHTxx/issues/ 45 | -------------------------------------------------------------------------------- /examples/AHT20_Serial/AHT20_Serial.ino: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************/ 2 | /* 3 | This is an Arduino example for Aosong ASAIR AHT10/AHT15/AHT20/AHT21/AHT25/AM2301B/AM2311B 4 | Digital Humidity & Temperature Sensor 5 | 6 | written by : enjoyneering 7 | sourse code: https://github.com/enjoyneering/ 8 | 9 | Aosong ASAIR AHT1x/AHT2x features: 10 | - AHT1x +1.8v..+3.6v, AHT2x +2.2v..+5.5v 11 | - AHT1x 0.25uA..320uA, AHT2x 0.25uA..980uA 12 | - temperature range -40C..+85C 13 | - humidity range 0%..100% 14 | - typical accuracy T +-0.3C, RH +-2% 15 | - typical resolution T 0.01C, RH 0.024% 16 | - normal operating range T -20C..+60C, RH 10%..80% 17 | - maximum operating rage T -40C..+80C, RH 0%..100% 18 | - response time 8..30sec* 19 | - I2C bus speed 100KHz..400KHz, 10KHz recommended minimum 20 | *measurement with high frequency leads to heating of the 21 | sensor, interval must be > 1 second to keep self-heating below 0.1C 22 | 23 | This device uses I2C bus to communicate, specials pins are required to interface 24 | Board SDA SCL Level 25 | Uno, Mini, Pro, ATmega168, ATmega328..... A4 A5 5v 26 | Mega2560................................. 20 21 5v 27 | Due, SAM3X8E............................. 20 21 3.3v 28 | MKR Zero, XIAO SAMD21, SAMD21xx.......... PA08 PA09 3.3v 29 | Leonardo, Micro, ATmega32U4.............. 2 3 5v 30 | Digistump, Trinket, Gemma, ATtiny85...... PB0/D0 PB2/D2 3.3v/5v 31 | Blue Pill*, STM32F103xxxx boards*........ PB7/PB9 PB6/PB8 3.3v/5v 32 | ESP8266 ESP-01**......................... GPIO0 GPIO2 3.3v/5v 33 | NodeMCU 1.0**, WeMos D1 Mini**........... GPIO4/D2 GPIO5/D1 3.3v/5v 34 | ESP32***................................. GPIO21/D21 GPIO22/D22 3.3v 35 | GPIO16/D16 GPIO17/D17 3.3v 36 | *hardware I2C Wire mapped to Wire1 in stm32duino 37 | see https://github.com/stm32duino/wiki/wiki/API#I2C 38 | **most boards has 10K..12K pullup-up resistor 39 | on GPIO0/D3, GPIO2/D4/LED & pullup-down on 40 | GPIO15/D8 for flash & boot 41 | ***hardware I2C Wire mapped to TwoWire(0) aka GPIO21/GPIO22 in Arduino ESP32 42 | 43 | Supported frameworks: 44 | Arduino Core - https://github.com/arduino/Arduino/tree/master/hardware 45 | ATtiny Core - https://github.com/SpenceKonde/ATTinyCore 46 | ESP8266 Core - https://github.com/esp8266/Arduino 47 | ESP32 Core - https://github.com/espressif/arduino-esp32 48 | STM32 Core - https://github.com/stm32duino/Arduino_Core_STM32 49 | SAMD Core - https://github.com/arduino/ArduinoCore-samd 50 | 51 | 52 | GNU GPL license, all text above must be included in any redistribution, 53 | see link for details - https://www.gnu.org/licenses/licenses.html 54 | */ 55 | /***************************************************************************************************/ 56 | 57 | #include 58 | #include 59 | #if defined(ESP8266) 60 | #include 61 | #endif 62 | 63 | float ahtValue; //to store T/RH result 64 | 65 | AHTxx aht20(AHTXX_ADDRESS_X38, AHT2x_SENSOR); //sensor address, sensor type 66 | 67 | 68 | 69 | /**************************************************************************/ 70 | /* 71 | setup() 72 | 73 | Main setup 74 | */ 75 | /**************************************************************************/ 76 | void setup() 77 | { 78 | #if defined(ESP8266) 79 | WiFi.persistent(false); //disable saving wifi config into SDK flash area 80 | WiFi.forceSleepBegin(); //disable AP & station by calling "WiFi.mode(WIFI_OFF)" & put modem to sleep 81 | #endif 82 | 83 | Serial.begin(115200); 84 | Serial.println(); 85 | 86 | while (aht20.begin() != true) 87 | { 88 | Serial.println(F("AHT2x not connected or fail to load calibration coefficient")); //(F()) save string to flash & keeps dynamic memory free 89 | 90 | delay(5000); 91 | } 92 | 93 | Serial.println(F("AHT20 OK")); 94 | 95 | //Wire.setClock(400000); //experimental I2C speed! 400KHz, default 100KHz 96 | } 97 | 98 | 99 | /**************************************************************************/ 100 | /* 101 | loop() 102 | 103 | Main loop 104 | */ 105 | /**************************************************************************/ 106 | void loop() 107 | { 108 | /* DEMO - 1, every temperature or humidity call will read 6-bytes over I2C, total 12-bytes */ 109 | Serial.println(); 110 | Serial.println(F("DEMO 1: read 12-bytes")); 111 | 112 | ahtValue = aht20.readTemperature(); //read 6-bytes via I2C, takes 80 milliseconds 113 | 114 | Serial.print(F("Temperature...: ")); 115 | 116 | if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs 117 | { 118 | Serial.print(ahtValue); 119 | Serial.println(F(" +-0.3C")); 120 | } 121 | else 122 | { 123 | printStatus(); //print temperature command status 124 | 125 | if (aht20.softReset() == true) Serial.println(F("reset success")); //as the last chance to make it alive 126 | else Serial.println(F("reset failed")); 127 | } 128 | 129 | delay(2000); //measurement with high frequency leads to heating of the sensor, see NOTE 130 | 131 | ahtValue = aht20.readHumidity(); //read another 6-bytes via I2C, takes 80 milliseconds 132 | 133 | Serial.print(F("Humidity......: ")); 134 | 135 | if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs 136 | { 137 | Serial.print(ahtValue); 138 | Serial.println(F(" +-2%")); 139 | } 140 | else 141 | { 142 | printStatus(); //print humidity command status 143 | } 144 | 145 | delay(2000); //measurement with high frequency leads to heating of the sensor, see NOTE 146 | 147 | /* DEMO - 2, temperature call will read 6-bytes via I2C, humidity will use same 6-bytes */ 148 | Serial.println(); 149 | Serial.println(F("DEMO 2: read 6-byte")); 150 | 151 | ahtValue = aht20.readTemperature(); //read 6-bytes via I2C, takes 80 milliseconds 152 | 153 | Serial.print(F("Temperature: ")); 154 | 155 | if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs 156 | { 157 | Serial.print(ahtValue); 158 | Serial.println(F(" +-0.3C")); 159 | } 160 | else 161 | { 162 | printStatus(); //print temperature command status 163 | } 164 | 165 | ahtValue = aht20.readHumidity(AHTXX_USE_READ_DATA); //use 6-bytes from temperature reading, takes zero milliseconds!!! 166 | 167 | Serial.print(F("Humidity...: ")); 168 | 169 | if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs 170 | { 171 | Serial.print(ahtValue); 172 | Serial.println(F(" +-2%")); 173 | } 174 | else 175 | { 176 | printStatus(); //print temperature command status not humidity!!! RH measurement use same 6-bytes from T measurement 177 | } 178 | 179 | delay(10000); //recomended polling frequency 8sec..30sec 180 | } 181 | 182 | 183 | /**************************************************************************/ 184 | /* 185 | printStatus() 186 | 187 | Print last command status 188 | */ 189 | /**************************************************************************/ 190 | void printStatus() 191 | { 192 | switch (aht20.getStatus()) 193 | { 194 | case AHTXX_NO_ERROR: 195 | Serial.println(F("no error")); 196 | break; 197 | 198 | case AHTXX_BUSY_ERROR: 199 | Serial.println(F("sensor busy, increase polling time")); 200 | break; 201 | 202 | case AHTXX_ACK_ERROR: 203 | Serial.println(F("sensor didn't return ACK, not connected, broken, long wires (reduce speed), bus locked by slave (increase stretch limit)")); 204 | break; 205 | 206 | case AHTXX_DATA_ERROR: 207 | Serial.println(F("received data smaller than expected, not connected, broken, long wires (reduce speed), bus locked by slave (increase stretch limit)")); 208 | break; 209 | 210 | case AHTXX_CRC8_ERROR: 211 | Serial.println(F("computed CRC8 not match received CRC8, this feature supported only by AHT2x sensors")); 212 | break; 213 | 214 | default: 215 | Serial.println(F("unknown status")); 216 | break; 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /examples/AHT10_Serial/AHT10_Serial.ino: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************/ 2 | /* 3 | This is an Arduino example for Aosong ASAIR AHT10/AHT15/AHT20/AHT21/AHT25/AM2301B/AM2311B 4 | Digital Humidity & Temperature Sensor 5 | 6 | written by : enjoyneering 7 | sourse code: https://github.com/enjoyneering/ 8 | 9 | Aosong ASAIR AHT1x/AHT2x features: 10 | - AHT1x +1.8v..+3.6v, AHT2x +2.2v..+5.5v 11 | - AHT1x 0.25uA..320uA, AHT2x 0.25uA..980uA 12 | - temperature range -40C..+85C 13 | - humidity range 0%..100% 14 | - typical accuracy T +-0.3C, RH +-2% 15 | - typical resolution T 0.01C, RH 0.024% 16 | - normal operating range T -20C..+60C, RH 10%..80% 17 | - maximum operating rage T -40C..+80C, RH 0%..100% 18 | - response time 8..30sec* 19 | - I2C bus speed 100KHz..400KHz, 10KHz recommended minimum 20 | *measurement with high frequency leads to heating of the 21 | sensor, interval must be > 1 second to keep self-heating below 0.1C 22 | 23 | This device uses I2C bus to communicate, specials pins are required to interface 24 | Board SDA SCL Level 25 | Uno, Mini, Pro, ATmega168, ATmega328..... A4 A5 5v 26 | Mega2560................................. 20 21 5v 27 | Due, SAM3X8E............................. 20 21 3.3v 28 | MKR Zero, XIAO SAMD21, SAMD21xx.......... PA08 PA09 3.3v 29 | Leonardo, Micro, ATmega32U4.............. 2 3 5v 30 | Digistump, Trinket, Gemma, ATtiny85...... PB0/D0 PB2/D2 3.3v/5v 31 | Blue Pill*, STM32F103xxxx boards*........ PB7/PB9 PB6/PB8 3.3v/5v 32 | ESP8266 ESP-01**......................... GPIO0 GPIO2 3.3v/5v 33 | NodeMCU 1.0**, WeMos D1 Mini**........... GPIO4/D2 GPIO5/D1 3.3v/5v 34 | ESP32***................................. GPIO21/D21 GPIO22/D22 3.3v 35 | GPIO16/D16 GPIO17/D17 3.3v 36 | *hardware I2C Wire mapped to Wire1 in stm32duino 37 | see https://github.com/stm32duino/wiki/wiki/API#I2C 38 | **most boards has 10K..12K pullup-up resistor 39 | on GPIO0/D3, GPIO2/D4/LED & pullup-down on 40 | GPIO15/D8 for flash & boot 41 | ***hardware I2C Wire mapped to TwoWire(0) aka GPIO21/GPIO22 in Arduino ESP32 42 | 43 | Supported frameworks: 44 | Arduino Core - https://github.com/arduino/Arduino/tree/master/hardware 45 | ATtiny Core - https://github.com/SpenceKonde/ATTinyCore 46 | ESP8266 Core - https://github.com/esp8266/Arduino 47 | ESP32 Core - https://github.com/espressif/arduino-esp32 48 | STM32 Core - https://github.com/stm32duino/Arduino_Core_STM32 49 | SAMD Core - https://github.com/arduino/ArduinoCore-samd 50 | 51 | 52 | GNU GPL license, all text above must be included in any redistribution, 53 | see link for details - https://www.gnu.org/licenses/licenses.html 54 | */ 55 | /***************************************************************************************************/ 56 | 57 | #include 58 | #include 59 | #if defined(ESP8266) 60 | #include 61 | #endif 62 | 63 | float ahtValue; //to store T/RH result 64 | 65 | AHTxx aht10(AHTXX_ADDRESS_X38, AHT1x_SENSOR); //sensor address, sensor type 66 | 67 | 68 | 69 | /**************************************************************************/ 70 | /* 71 | setup() 72 | 73 | Main setup 74 | */ 75 | /**************************************************************************/ 76 | void setup() 77 | { 78 | #if defined(ESP8266) 79 | WiFi.persistent(false); //disable saving wifi config into SDK flash area 80 | WiFi.forceSleepBegin(); //disable AP & station by calling "WiFi.mode(WIFI_OFF)" & put modem to sleep 81 | #endif 82 | 83 | Serial.begin(115200); 84 | Serial.println(); 85 | 86 | while (aht10.begin() != true) //for ESP-01 use aht10.begin(0, 2); 87 | { 88 | Serial.println(F("AHT1x not connected or fail to load calibration coefficient")); //(F()) save string to flash & keeps dynamic memory free 89 | 90 | delay(5000); 91 | } 92 | 93 | Serial.println(F("AHT10 OK")); 94 | 95 | //Wire.setClock(400000); //experimental I2C speed! 400KHz, default 100KHz 96 | } 97 | 98 | 99 | /**************************************************************************/ 100 | /* 101 | loop() 102 | 103 | Main loop 104 | */ 105 | /**************************************************************************/ 106 | void loop() 107 | { 108 | /* DEMO - 1, every temperature or humidity call will read 6-bytes over I2C, total 12-bytes */ 109 | Serial.println(); 110 | Serial.println(F("DEMO 1: read 12-bytes")); 111 | 112 | ahtValue = aht10.readTemperature(); //read 6-bytes via I2C, takes 80 milliseconds 113 | 114 | Serial.print(F("Temperature...: ")); 115 | 116 | if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs 117 | { 118 | Serial.print(ahtValue); 119 | Serial.println(F(" +-0.3C")); 120 | } 121 | else 122 | { 123 | printStatus(); //print temperature command status 124 | 125 | if (aht10.softReset() == true) Serial.println(F("reset success")); //as the last chance to make it alive 126 | else Serial.println(F("reset failed")); 127 | } 128 | 129 | delay(2000); //measurement with high frequency leads to heating of the sensor, see NOTE 130 | 131 | ahtValue = aht10.readHumidity(); //read another 6-bytes via I2C, takes 80 milliseconds 132 | 133 | Serial.print(F("Humidity......: ")); 134 | 135 | if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs 136 | { 137 | Serial.print(ahtValue); 138 | Serial.println(F(" +-2%")); 139 | } 140 | else 141 | { 142 | printStatus(); //print humidity command status 143 | } 144 | 145 | delay(2000); //measurement with high frequency leads to heating of the sensor, see NOTE 146 | 147 | /* DEMO - 2, temperature call will read 6-bytes via I2C, humidity will use same 6-bytes */ 148 | Serial.println(); 149 | Serial.println(F("DEMO 2: read 6-byte")); 150 | 151 | ahtValue = aht10.readTemperature(); //read 6-bytes via I2C, takes 80 milliseconds 152 | 153 | Serial.print(F("Temperature: ")); 154 | 155 | if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs 156 | { 157 | Serial.print(ahtValue); 158 | Serial.println(F(" +-0.3C")); 159 | } 160 | else 161 | { 162 | printStatus(); //print temperature command status 163 | } 164 | 165 | ahtValue = aht10.readHumidity(AHTXX_USE_READ_DATA); //use 6-bytes from temperature reading, takes zero milliseconds!!! 166 | 167 | Serial.print(F("Humidity...: ")); 168 | 169 | if (ahtValue != AHTXX_ERROR) //AHTXX_ERROR = 255, library returns 255 if error occurs 170 | { 171 | Serial.print(ahtValue); 172 | Serial.println(F(" +-2%")); 173 | } 174 | else 175 | { 176 | printStatus(); //print temperature command status not humidity!!! RH measurement use same 6-bytes from T measurement 177 | } 178 | 179 | delay(10000); //recomended polling frequency 8sec..30sec 180 | } 181 | 182 | 183 | /**************************************************************************/ 184 | /* 185 | printStatus() 186 | 187 | Print last command status 188 | */ 189 | /**************************************************************************/ 190 | void printStatus() 191 | { 192 | switch (aht10.getStatus()) 193 | { 194 | case AHTXX_NO_ERROR: 195 | Serial.println(F("no error")); 196 | break; 197 | 198 | case AHTXX_BUSY_ERROR: 199 | Serial.println(F("sensor busy, increase polling time")); 200 | break; 201 | 202 | case AHTXX_ACK_ERROR: 203 | Serial.println(F("sensor didn't return ACK, not connected, broken, long wires (reduce speed), bus locked by slave (increase stretch limit)")); 204 | break; 205 | 206 | case AHTXX_DATA_ERROR: 207 | Serial.println(F("received data smaller than expected, not connected, broken, long wires (reduce speed), bus locked by slave (increase stretch limit)")); 208 | break; 209 | 210 | case AHTXX_CRC8_ERROR: 211 | Serial.println(F("computed CRC8 not match received CRC8, this feature supported only by AHT2x sensors")); 212 | break; 213 | 214 | default: 215 | Serial.println(F("unknown status")); 216 | break; 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/AHTxx.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************/ 2 | /* 3 | This is an Arduino library for Aosong ASAIR AHT10/AHT15/AHT20/AHT21/AHT25/AM2301B/AM2311B 4 | Digital Humidity & Temperature Sensor 5 | 6 | written by : enjoyneering 7 | sourse code: https://github.com/enjoyneering/ 8 | 9 | Aosong ASAIR AHT1x/AHT2x features: 10 | - AHT1x +1.8v..+3.6v, AHT2x +2.2v..+5.5v 11 | - AHT1x 0.25uA..320uA, AHT2x 0.25uA..980uA 12 | - temperature range -40C..+85C 13 | - humidity range 0%..100% 14 | - typical accuracy T +-0.3C, RH +-2% 15 | - typical resolution T 0.01C, RH 0.024% 16 | - normal operating range T -20C..+60C, RH 10%..80% 17 | - maximum operating rage T -40C..+80C, RH 0%..100% 18 | - response time 8..30sec* 19 | - I2C bus speed 100KHz..400KHz, 10KHz recommended minimum 20 | *measurement with high frequency leads to heating of the 21 | sensor, interval must be > 1 second to keep self-heating below 0.1C 22 | 23 | This device uses I2C bus to communicate, specials pins are required to interface 24 | Board SDA SCL Level 25 | Uno, Mini, Pro, ATmega168, ATmega328..... A4 A5 5v 26 | Mega2560................................. 20 21 5v 27 | Due, SAM3X8E............................. 20 21 3.3v 28 | MKR Zero, XIAO SAMD21, SAMD21xx.......... PA08 PA09 3.3v 29 | Leonardo, Micro, ATmega32U4.............. 2 3 5v 30 | Digistump, Trinket, Gemma, ATtiny85...... PB0/D0 PB2/D2 3.3v/5v 31 | Blue Pill*, STM32F103xxxx boards*........ PB7/PB9 PB6/PB8 3.3v/5v 32 | ESP8266 ESP-01**......................... GPIO0 GPIO2 3.3v/5v 33 | NodeMCU 1.0**, WeMos D1 Mini**........... GPIO4/D2 GPIO5/D1 3.3v/5v 34 | ESP32***................................. GPIO21/D21 GPIO22/D22 3.3v 35 | GPIO16/D16 GPIO17/D17 3.3v 36 | ESP32-S3................................. GPIO8 GPIO9 3.3V 37 | *hardware I2C Wire mapped to Wire1 in stm32duino 38 | see https://github.com/stm32duino/wiki/wiki/API#I2C 39 | **most boards has 10K..12K pullup-up resistor 40 | on GPIO0/D3, GPIO2/D4/LED & pullup-down on 41 | GPIO15/D8 for flash & boot 42 | ***hardware I2C Wire mapped to TwoWire(0) aka GPIO21/GPIO22 in Arduino ESP32 43 | 44 | Supported frameworks: 45 | Arduino Core - https://github.com/arduino/Arduino/tree/master/hardware 46 | ATtiny Core - https://github.com/SpenceKonde/ATTinyCore 47 | ESP8266 Core - https://github.com/esp8266/Arduino 48 | ESP32 Core - https://github.com/espressif/arduino-esp32 49 | STM32 Core - https://github.com/stm32duino/Arduino_Core_STM32 50 | SAMD Core - https://github.com/arduino/ArduinoCore-samd 51 | 52 | 53 | GNU GPL license, all text above must be included in any redistribution, 54 | see link for details - https://www.gnu.org/licenses/licenses.html 55 | */ 56 | /***************************************************************************************************/ 57 | 58 | #ifndef AHTXX_h 59 | #define AHTXX_h 60 | 61 | 62 | #include 63 | #include 64 | 65 | #if defined (ARDUINO_ARCH_AVR) 66 | #include //for Arduino AVR PROGMEM support 67 | #elif defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32) 68 | #include //for Arduino ESP8266 PROGMEM support 69 | #elif defined (ARDUINO_ARCH_STM32) || defined (ARDUINO_ARCH_SAMD) 70 | #include //for Arduino STM32 & SAMD21 PROGMEM support 71 | #endif 72 | 73 | 74 | /* list of I2C addresses */ 75 | #define AHTXX_ADDRESS_X38 0x38 //AHT15/AHT20/AHT21/AHT25 I2C address, AHT10 I2C address if address pin to GND 76 | #define AHT10_ADDRESS_X39 0x39 //AHT10 I2C address, if address pin to Vcc 77 | 78 | /* list of command registers */ 79 | #define AHT1X_INIT_REG 0xE1 //initialization register, for AHT1x only 80 | #define AHT2X_INIT_REG 0xBE //initialization register, for AHT2x only 81 | #define AHTXX_STATUS_REG 0x71 //read status byte register 82 | #define AHTXX_START_MEASUREMENT_REG 0xAC //start measurement register 83 | #define AHTXX_SOFT_RESET_REG 0xBA //soft reset register 84 | 85 | /* calibration register controls */ 86 | #define AHT1X_INIT_CTRL_NORMAL_MODE 0x00 //normal mode on/off bit[6:5], for AHT1x only 87 | #define AHT1X_INIT_CTRL_CYCLE_MODE 0x20 //cycle mode on/off bit[6:5], for AHT1x only 88 | #define AHT1X_INIT_CTRL_CMD_MODE 0x40 //command mode on/off bit[6:5], for AHT1x only 89 | #define AHTXX_INIT_CTRL_CAL_ON 0x08 //calibration coeff on/off bit[3] 90 | #define AHTXX_INIT_CTRL_NOP 0x00 //NOP control, send after any "AHT1X_INIT_CTRL..." 91 | 92 | /* status byte register controls */ 93 | #define AHTXX_STATUS_CTRL_BUSY 0x80 //busy bit[7] 94 | #define AHT1X_STATUS_CTRL_NORMAL_MODE 0x00 //normal mode status bit[6:5], for AHT1x only 95 | #define AHT1X_STATUS_CTRL_CYCLE_MODE 0x20 //cycle mode status bit[6:5], for AHT1x only 96 | #define AHT1X_STATUS_CTRL_CMD_MODE 0x40 //command mode status bit[6:5], for AHT1x only 97 | #define AHTXX_STATUS_CTRL_CRC 0x10 //CRC8 status bit[4], no info in datasheet 98 | #define AHTXX_STATUS_CTRL_CAL_ON 0x08 //calibration coeff status bit[3] 99 | #define AHTXX_STATUS_CTRL_FIFO_ON 0x04 //FIFO on status bit[2], no info in datasheet 100 | #define AHTXX_STATUS_CTRL_FIFO_FULL 0x02 //FIFO full status bit[1], no info in datasheet 101 | #define AHTXX_STATUS_CTRL_FIFO_EMPTY 0x02 //FIFO empty status bit[1], no info in datasheet 102 | 103 | /* measurement register controls */ 104 | #define AHTXX_START_MEASUREMENT_CTRL 0x33 //measurement controls, suspect this is temperature & humidity DAC resolution 105 | #define AHTXX_START_MEASUREMENT_CTRL_NOP 0x00 //NOP control, send after any "AHTXX_START_MEASUREMENT_CTRL..." 106 | 107 | /* sensor delays */ 108 | #define AHTXX_CMD_DELAY 10 //delay between commands, in milliseconds 109 | #define AHTXX_MEASUREMENT_DELAY 80 //wait for measurement to complete, in milliseconds 110 | #define AHT1X_POWER_ON_DELAY 40 //wait for AHT1x to initialize after power-on, in milliseconds 111 | #define AHT2X_POWER_ON_DELAY 100 //wait for AHT2x to initialize after power-on, in milliseconds 112 | #define AHTXX_SOFT_RESET_DELAY 20 //less than 20 milliseconds 113 | 114 | /* misc */ 115 | #define AHTXX_I2C_SPEED_HZ 100000 //sensor I2C speed 100KHz..400KHz, in Hz 116 | #define AHTXX_I2C_STRETCH_USEC 1000 //I2C stretch time, in usec 117 | #define AHTXX_FORCE_READ_DATA true //force to read data via I2C 118 | #define AHTXX_USE_READ_DATA false //force to use data from previous read 119 | 120 | #define AHTXX_NO_ERROR 0x00 //success, no errors 121 | #define AHTXX_BUSY_ERROR 0x01 //sensor is busy 122 | #define AHTXX_ACK_ERROR 0x02 //sensor didn't return ACK (not connected, broken, long wires (reduce speed), bus locked by slave (increase stretch limit)) 123 | #define AHTXX_DATA_ERROR 0x03 //received data smaller than expected 124 | #define AHTXX_CRC8_ERROR 0x04 //computed CRC8 not match received CRC8, for AHT2x only 125 | #define AHTXX_ERROR 0xFF //other errors 126 | 127 | typedef enum : uint8_t 128 | { 129 | AHT1x_SENSOR = 0x00, 130 | AHT2x_SENSOR = 0x01, 131 | } 132 | AHTXX_I2C_SENSOR; 133 | 134 | 135 | class AHTxx 136 | { 137 | public: 138 | AHTxx(uint8_t address = AHTXX_ADDRESS_X38, AHTXX_I2C_SENSOR = AHT1x_SENSOR); 139 | 140 | #if defined (ARDUINO_ARCH_AVR) 141 | bool begin(uint32_t speed = AHTXX_I2C_SPEED_HZ, uint32_t stretch = AHTXX_I2C_STRETCH_USEC); 142 | #elif defined (ARDUINO_ARCH_ESP8266) 143 | bool begin(uint8_t sda = SDA, uint8_t scl = SCL, uint32_t speed = AHTXX_I2C_SPEED_HZ, uint32_t stretch = AHTXX_I2C_STRETCH_USEC); 144 | #elif defined (ARDUINO_ARCH_ESP32) 145 | bool begin(int32_t sda = SDA, int32_t scl = SCL, uint32_t speed = AHTXX_I2C_SPEED_HZ, uint32_t stretch = AHTXX_I2C_STRETCH_USEC); 146 | #elif defined (ARDUINO_ARCH_STM32) 147 | bool begin(uint32_t sda = SDA, uint32_t scl = SCL, uint32_t speed = AHTXX_I2C_SPEED_HZ); 148 | #elif defined (ARDUINO_ARCH_SAMD) 149 | bool begin(uint32_t speed = AHTXX_I2C_SPEED_HZ); 150 | #else 151 | bool begin(); 152 | #endif 153 | 154 | float readHumidity(bool readAHT = AHTXX_FORCE_READ_DATA); 155 | float readTemperature(bool readAHT = AHTXX_FORCE_READ_DATA); 156 | bool setNormalMode(); 157 | bool setCycleMode(); 158 | bool setComandMode(); 159 | bool softReset(); 160 | uint8_t getStatus(); 161 | void setType(AHTXX_I2C_SENSOR = AHT1x_SENSOR); 162 | 163 | 164 | private: 165 | AHTXX_I2C_SENSOR _sensorType; 166 | uint8_t _address; 167 | uint8_t _status; 168 | uint8_t _rawData[7] = {0, 0, 0, 0, 0, 0, 0}; //{status, RH, RH, RH+T, T, T, CRC}, CRC for AHT2x only 169 | 170 | bool _setInitializationRegister(uint8_t value); 171 | uint8_t _readStatusRegister(); 172 | uint8_t _getCalibration(); 173 | uint8_t _getBusy(bool readAHT = AHTXX_FORCE_READ_DATA); 174 | bool _checkCRC8(); 175 | 176 | protected: 177 | void _readMeasurement(); //TODO: IRAM_ATTR for ESP8266, ESP32 178 | }; 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /src/AHTxx.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************/ 2 | /* 3 | This is an Arduino library for Aosong ASAIR AHT10/AHT15/AHT20/AHT21/AHT25/AM2301B/AM2311B 4 | Digital Humidity & Temperature Sensor 5 | 6 | written by : enjoyneering 7 | sourse code: https://github.com/enjoyneering/ 8 | 9 | Aosong ASAIR AHT1x/AHT2x features: 10 | - AHT1x +1.8v..+3.6v, AHT2x +2.2v..+5.5v 11 | - AHT1x 0.25uA..320uA, AHT2x 0.25uA..980uA 12 | - temperature range -40C..+85C 13 | - humidity range 0%..100% 14 | - typical accuracy T +-0.3C, RH +-2% 15 | - typical resolution T 0.01C, RH 0.024% 16 | - normal operating range T -20C..+60C, RH 10%..80% 17 | - maximum operating rage T -40C..+80C, RH 0%..100% 18 | - response time 8..30sec* 19 | - I2C bus speed 100KHz..400KHz, 10KHz recommended minimum 20 | *measurement with high frequency leads to heating of the 21 | sensor, interval must be > 1 second to keep self-heating below 0.1C 22 | 23 | This device uses I2C bus to communicate, specials pins are required to interface 24 | Board SDA SCL Level 25 | Uno, Mini, Pro, ATmega168, ATmega328..... A4 A5 5v 26 | Mega2560................................. 20 21 5v 27 | Due, SAM3X8E............................. 20 21 3.3v 28 | MKR Zero, XIAO SAMD21, SAMD21xx.......... PA08 PA09 3.3v 29 | Leonardo, Micro, ATmega32U4.............. 2 3 5v 30 | Digistump, Trinket, Gemma, ATtiny85...... PB0/D0 PB2/D2 3.3v/5v 31 | Blue Pill*, STM32F103xxxx boards*........ PB7/PB9 PB6/PB8 3.3v/5v 32 | ESP8266 ESP-01**......................... GPIO0 GPIO2 3.3v/5v 33 | NodeMCU 1.0**, WeMos D1 Mini**........... GPIO4/D2 GPIO5/D1 3.3v/5v 34 | ESP32***................................. GPIO21/D21 GPIO22/D22 3.3v 35 | GPIO16/D16 GPIO17/D17 3.3v 36 | ESP32-S3................................. GPIO8 GPIO9 3.3V 37 | *hardware I2C Wire mapped to Wire1 in stm32duino 38 | see https://github.com/stm32duino/wiki/wiki/API#I2C 39 | **most boards has 10K..12K pullup-up resistor 40 | on GPIO0/D3, GPIO2/D4/LED & pullup-down on 41 | GPIO15/D8 for flash & boot 42 | ***hardware I2C Wire mapped to TwoWire(0) aka GPIO21/GPIO22 in Arduino ESP32 43 | 44 | Supported frameworks: 45 | Arduino Core - https://github.com/arduino/Arduino/tree/master/hardware 46 | ATtiny Core - https://github.com/SpenceKonde/ATTinyCore 47 | ESP8266 Core - https://github.com/esp8266/Arduino 48 | ESP32 Core - https://github.com/espressif/arduino-esp32 49 | STM32 Core - https://github.com/stm32duino/Arduino_Core_STM32 50 | SAMD Core - https://github.com/arduino/ArduinoCore-samd 51 | 52 | 53 | GNU GPL license, all text above must be included in any redistribution, 54 | see link for details - https://www.gnu.org/licenses/licenses.html 55 | */ 56 | /***************************************************************************************************/ 57 | 58 | #include "AHTxx.h" 59 | 60 | 61 | /**************************************************************************/ 62 | /* 63 | Constructor 64 | */ 65 | /**************************************************************************/ 66 | AHTxx::AHTxx(uint8_t address, AHTXX_I2C_SENSOR sensorType) 67 | { 68 | _address = address; 69 | _sensorType = sensorType; 70 | _status = AHTXX_NO_ERROR; 71 | } 72 | 73 | /**************************************************************************/ 74 | /* 75 | begin() 76 | 77 | Initialize I2C & sensor 78 | 79 | NOTE: 80 | - call this function before doing anything else!!! 81 | - speed in Hz, stretch in usec 82 | 83 | - returned value by "Wire.endTransmission()": 84 | - 0 success 85 | - 1 data too long to fit in transmit data buffer 86 | - 2 received NACK on transmit of address 87 | - 3 received NACK on transmit of data 88 | - 4 other error 89 | */ 90 | /**************************************************************************/ 91 | #if defined (ARDUINO_ARCH_AVR) 92 | bool AHTxx::begin(uint32_t speed, uint32_t stretch) 93 | { 94 | Wire.begin(); 95 | 96 | Wire.setClock(speed); //experimental! AVR I2C bus speed 31kHz..400kHz, default 100000Hz 97 | 98 | #if !defined (__AVR_ATtiny85__) //for backwards compatibility with ATtiny Core 99 | Wire.setWireTimeout(stretch, false); //experimental! default 25000usec, true=Wire hardware will be automatically reset to default on timeout 100 | #endif 101 | 102 | #elif defined (ARDUINO_ARCH_ESP8266) 103 | bool AHTxx::begin(uint8_t sda, uint8_t scl, uint32_t speed, uint32_t stretch) 104 | { 105 | Wire.begin(sda, scl); 106 | 107 | Wire.setClock(speed); //experimental! ESP8266 I2C bus speed 1kHz..400kHz, default 100000Hz 108 | 109 | Wire.setClockStretchLimit(stretch); //experimental! default 150000usec 110 | 111 | #elif defined (ARDUINO_ARCH_ESP32) 112 | bool AHTxx::begin(int32_t sda, int32_t scl, uint32_t speed, uint32_t stretch) //"int32_t" for Master SDA & SCL, "uint8_t" for Slave SDA & SCL 113 | { 114 | if (Wire.begin(sda, scl, speed) != true) {return false;} //experimental! ESP32 I2C bus speed ???kHz..400kHz, default 100000Hz 115 | 116 | Wire.setTimeout(stretch / 1000); //experimental! default 50msec 117 | 118 | #elif defined (ARDUINO_ARCH_STM32) 119 | bool AHTxx::begin(uint32_t sda, uint32_t scl, uint32_t speed) //"uint32_t" for pins only, "uint8_t" calls wrong "setSCL(PinName scl)" 120 | { 121 | Wire.begin(sda, scl); 122 | 123 | Wire.setClock(speed); //experimental! STM32 I2C bus speed ???kHz..400kHz, default 100000Hz 124 | 125 | #elif defined (ARDUINO_ARCH_SAMD) 126 | bool LiquidCrystal_I2C::begin(uint8_t columns, uint8_t rows, lcdFontSize fontSize, uint32_t speed) 127 | { 128 | Wire.begin(); 129 | 130 | Wire.setClock(speed); //experimental! SAMD21 I2C bus speed ???kHz..400kHz, default 100000Hz 131 | 132 | #else 133 | bool AHTxx::begin() 134 | { 135 | Wire.begin(); 136 | #endif 137 | 138 | delay(AHT2X_POWER_ON_DELAY); //wait for sensor to initialize 139 | 140 | return softReset(); //soft reset is recommended at start (reset, set normal mode, set calibration bit & check calibration bit) 141 | } 142 | 143 | 144 | /**************************************************************************/ 145 | /* 146 | readHumidity() 147 | 148 | Read relative humidity, in % 149 | 150 | NOTE: 151 | - relative humidity range........ 0%..100% 152 | - relative humidity resolution... 0.024% 153 | - relative humidity accuracy..... +-2% 154 | - response time............ 5..30sec 155 | - measurement with high frequency leads to heating of the 156 | sensor, must be > 2 seconds apart to keep self-heating below 0.1C 157 | - long-term exposure for 60 hours outside the normal range 158 | (humidity > 80%) can lead to a temporary drift of the 159 | signal +3%, sensor slowly returns to the calibrated state at normal 160 | operating conditions 161 | 162 | - sensors data structure: 163 | - {status, RH, RH, RH+T, T, T, CRC*}, *CRC for AHT2x only 164 | 165 | - normal operating range T -20C..+60C, RH 10%..80% 166 | - maximum operating rage T -40C..+80C, RH 0%..100% 167 | */ 168 | /**************************************************************************/ 169 | float AHTxx::readHumidity(bool readAHT) 170 | { 171 | if (readAHT == AHTXX_FORCE_READ_DATA) {_readMeasurement();} //force to read data via I2C & update "_rawData[]" buffer 172 | if (_status != AHTXX_NO_ERROR) {return AHTXX_ERROR;} //no reason to continue, call "getStatus()" for error description 173 | 174 | uint32_t humidity = _rawData[1]; //20-bit raw humidity data 175 | humidity <<= 8; 176 | humidity |= _rawData[2]; 177 | humidity <<= 4; 178 | humidity |= _rawData[3] >> 4; 179 | 180 | if (humidity > 0x100000) {humidity = 0x100000;} //check if RH>100, no need to check for RH<0 since "humidity" is "uint" 181 | 182 | return ((float)humidity / 0x100000) * 100; 183 | } 184 | 185 | 186 | /**************************************************************************/ 187 | /* 188 | readTemperature() 189 | 190 | Read temperature, in C 191 | 192 | NOTE: 193 | - temperature range........ -40C..+85C 194 | - temperature resolution... 0.01C 195 | - temperature accuracy..... +-0.3C 196 | - response time............ 5..30sec 197 | - measurement with high frequency leads to heating of the 198 | sensor, must be > 2 seconds apart to keep self-heating below 0.1C 199 | 200 | - sensors data structure: 201 | - {status, RH, RH, RH+T, T, T, CRC*}, *CRC for AHT2x only 202 | */ 203 | /**************************************************************************/ 204 | float AHTxx::readTemperature(bool readAHT) 205 | { 206 | if (readAHT == AHTXX_FORCE_READ_DATA) {_readMeasurement();} //force to read data via I2C & update "_rawData[]" buffer 207 | if (_status != AHTXX_NO_ERROR) {return AHTXX_ERROR;} //no reason to continue, call "getStatus()" for error description 208 | 209 | uint32_t temperature = _rawData[3] & 0x0F; //20-bit raw temperature data 210 | temperature <<= 8; 211 | temperature |= _rawData[4]; 212 | temperature <<= 8; 213 | temperature |= _rawData[5]; 214 | 215 | return ((float)temperature / 0x100000) * 200 - 50; 216 | } 217 | 218 | 219 | /**************************************************************************/ 220 | /* 221 | setNormalMode() 222 | 223 | Set normal measurement mode 224 | 225 | NOTE: 226 | - no info in datasheet, suspect this is one measurement & power down 227 | - true=success, false=I2C error 228 | */ 229 | /**************************************************************************/ 230 | bool AHTxx::setNormalMode() 231 | { 232 | return _setInitializationRegister(AHTXX_INIT_CTRL_CAL_ON | AHT1X_INIT_CTRL_NORMAL_MODE); 233 | } 234 | 235 | 236 | /**************************************************************************/ 237 | /* 238 | setCycleMode() 239 | 240 | Set cycle measurement mode 241 | 242 | NOTE: 243 | - no info in datasheet, suspect this is continuous measurement 244 | - true=success, false=I2C error 245 | */ 246 | /**************************************************************************/ 247 | bool AHTxx::setCycleMode() 248 | { 249 | return _setInitializationRegister(AHTXX_INIT_CTRL_CAL_ON | AHT1X_INIT_CTRL_CYCLE_MODE); 250 | } 251 | 252 | 253 | /**************************************************************************/ 254 | /* 255 | setComandMode() 256 | 257 | Set command measurement mode 258 | 259 | NOTE: 260 | - no info in datasheet 261 | - true=success, false=I2C error 262 | */ 263 | /**************************************************************************/ 264 | bool AHTxx::setComandMode() 265 | { 266 | return _setInitializationRegister(AHTXX_INIT_CTRL_CAL_ON | AHT1X_INIT_CTRL_CMD_MODE); 267 | } 268 | 269 | 270 | /**************************************************************************/ 271 | /* 272 | softReset() 273 | 274 | Restart sensor, without power off 275 | 276 | NOTE: 277 | - takes 20ms 278 | - all registers set to default 279 | */ 280 | /**************************************************************************/ 281 | bool AHTxx::softReset() 282 | { 283 | Wire.beginTransmission(_address); 284 | 285 | Wire.write(AHTXX_SOFT_RESET_REG); 286 | 287 | if (Wire.endTransmission(true) != 0) {return false;} //collision on I2C bus, sensor didn't return ACK 288 | 289 | delay(AHTXX_SOFT_RESET_DELAY); 290 | 291 | return ((setNormalMode() == true) && (_getCalibration() == AHTXX_STATUS_CTRL_CAL_ON)); //set mode & check calibration bit 292 | } 293 | 294 | 295 | /**************************************************************************/ 296 | /* 297 | getStatus() 298 | 299 | Return sensor status 300 | 301 | NOTE: 302 | - returned statuse: 303 | - AHTXX_NO_ERROR = 0x00, success, no errors 304 | - AHTXX_BUSY_ERROR = 0x01, sensor is busy 305 | - AHTXX_ACK_ERROR = 0x02, sensor didn't return ACK 306 | - AHTXX_DATA_ERROR = 0x03, received data smaller than expected 307 | - AHTXX_CRC8_ERROR = 0x04, computed CRC8 not match received CRC8, for AHT2x only 308 | */ 309 | /**************************************************************************/ 310 | uint8_t AHTxx::getStatus() 311 | { 312 | return _status; 313 | } 314 | 315 | 316 | /**************************************************************************/ 317 | /* 318 | setType() 319 | 320 | Set sensor type on the fly 321 | 322 | NOTE: 323 | - AHT1x vs AHT2x: 324 | - AHT1x +1.8v..+3.6v, AHT2x 2.2v..5.5v 325 | - AHT1x 0.25uA..320uA, AHT2x 0.25uA..980uA 326 | - AHT2x support CRC8 check 327 | */ 328 | /**************************************************************************/ 329 | void AHTxx::setType(AHTXX_I2C_SENSOR sensorType) 330 | { 331 | _sensorType = sensorType; 332 | } 333 | 334 | 335 | 336 | 337 | /**************************************************************************/ 338 | /* 339 | _setInitializationRegister() 340 | 341 | Set initialization register 342 | 343 | NOTE: 344 | - true=success, false=I2C error 345 | */ 346 | /**************************************************************************/ 347 | bool AHTxx::_setInitializationRegister(uint8_t value) 348 | { 349 | delay(AHTXX_CMD_DELAY); 350 | 351 | Wire.beginTransmission(_address); 352 | 353 | if (_sensorType == AHT1x_SENSOR) {Wire.write(AHT1X_INIT_REG);} //send initialization command, for AHT1x only 354 | else {Wire.write(AHT2X_INIT_REG);} //send initialization command, for AHT2x only 355 | 356 | Wire.write(value); //send initialization register controls 357 | Wire.write(AHTXX_INIT_CTRL_NOP); //send initialization register NOP control 358 | 359 | return (Wire.endTransmission(true) == 0); //true=success, false=I2C error 360 | } 361 | 362 | 363 | /**************************************************************************/ 364 | /* 365 | _readStatusRegister() 366 | 367 | Read status register 368 | 369 | NOTE: 370 | - AHT1x status register controls: 371 | 7 6 5 4 3 2 1 0 372 | BSY, MOD, MOD, xx, CAL, xx, xx, xx 373 | - BSY: 374 | - 1, sensor busy/measuring 375 | - 0, sensor idle/sleeping 376 | - MOD: 377 | - 00, normal mode 378 | - 01, cycle mode 379 | - 1x, comand mode 380 | - CAL: 381 | - 1, calibration on 382 | - 0, calibration off 383 | 384 | - AHT2x status register controls: 385 | 7 6 5 4 3 2 1 0 386 | BSY, xx, xx, xx, CAL, xx, xx, xx 387 | 388 | - under normal conditions status is 0x18 & 0x80 if the sensor is busy 389 | */ 390 | /**************************************************************************/ 391 | uint8_t AHTxx::_readStatusRegister() 392 | { 393 | delay(AHTXX_CMD_DELAY); 394 | 395 | Wire.beginTransmission(_address); 396 | 397 | Wire.write(AHTXX_STATUS_REG); 398 | 399 | if (Wire.endTransmission(true) != 0) {return AHTXX_ERROR;} //collision on I2C bus, sensor didn't return ACK 400 | 401 | Wire.requestFrom(_address, (uint8_t)1, (uint8_t)true); //read 1-byte to "wire.h" rxBuffer, true-send stop after transmission 402 | 403 | if (Wire.available() == 1) {return Wire.read();} //read 1-byte from "wire.h" rxBuffer 404 | return AHTXX_ERROR; //collision on I2C bus, "wire.h" rxBuffer is empty 405 | } 406 | 407 | 408 | /**************************************************************************/ 409 | /* 410 | _getCalibration() 411 | 412 | Read calibration bits from status register 413 | 414 | NOTE: 415 | - 0x08=loaded, 0x00=not loaded, 0xFF=I2C error 416 | - calibration status check should only be performed at power-up, 417 | rechecking is not required during data collection 418 | */ 419 | /**************************************************************************/ 420 | uint8_t AHTxx::_getCalibration() 421 | { 422 | uint8_t value = _readStatusRegister(); 423 | 424 | if (value != AHTXX_ERROR) {return (value & AHTXX_STATUS_CTRL_CAL_ON);} //0x08=loaded, 0x00=not loaded 425 | return AHTXX_ERROR; //collision on I2C bus, sensor didn't return ACK 426 | } 427 | 428 | 429 | /**************************************************************************/ 430 | /* 431 | _getBusy() 432 | 433 | Read/check busy bit after measurement command 434 | 435 | NOTE: 436 | - part of "readRawMeasurement()" function!!! 437 | - 0x80=busy, 0x00=measurement completed, etc 438 | */ 439 | /**************************************************************************/ 440 | uint8_t AHTxx::_getBusy(bool readAHT) 441 | { 442 | if (readAHT == AHTXX_FORCE_READ_DATA) //force to read data via I2C & update "_rawData[]" buffer 443 | { 444 | delay(AHTXX_CMD_DELAY); 445 | 446 | Wire.requestFrom(_address, (uint8_t)1, (uint8_t)true); //read 1-byte to "wire.h" rxBuffer, true-send stop after transmission 447 | 448 | if (Wire.available() != 1) {return AHTXX_DATA_ERROR;} //no reason to continue, "return" terminates the entire function & "break" just exits the loop 449 | 450 | _rawData[0] = Wire.read(); //read 1-byte from "wire.h" rxBuffer 451 | } 452 | 453 | if ((_rawData[0] & AHTXX_STATUS_CTRL_BUSY) == AHTXX_STATUS_CTRL_BUSY) {_status = AHTXX_BUSY_ERROR;} //0x80=busy, 0x00=measurement completed 454 | else {_status = AHTXX_NO_ERROR;} 455 | 456 | return _status; 457 | } 458 | 459 | 460 | /**************************************************************************/ 461 | /* 462 | _checkCRC8() 463 | 464 | Check CRC-8-Maxim of AHT2X measured data 465 | 466 | NOTE: 467 | - part of "readRawMeasurement()" function!!! 468 | - only AHT2x sensors have CRC 469 | - initial value=0xFF, polynomial=(x8 + x5 + x4 + 1) ie 0x31 CRC [7:0] = 1+X4+X5+X8 470 | */ 471 | /**************************************************************************/ 472 | bool AHTxx::_checkCRC8() 473 | { 474 | if (_sensorType == AHT2x_SENSOR) 475 | { 476 | uint8_t crc = 0xFF; //initial value 477 | 478 | for (uint8_t byteIndex = 0; byteIndex < 6; byteIndex ++) //6-bytes in data, {status, RH, RH, RH+T, T, T, CRC} 479 | { 480 | crc ^= _rawData[byteIndex]; 481 | 482 | for(uint8_t bitIndex = 8; bitIndex > 0; --bitIndex) //8-bits in byte 483 | { 484 | if (crc & 0x80) {crc = (crc << 1) ^ 0x31;} //0x31=CRC seed/polynomial 485 | else {crc = (crc << 1);} 486 | } 487 | } 488 | 489 | return (crc == _rawData[6]); 490 | } 491 | 492 | return true; 493 | } 494 | 495 | 496 | /**************************************************************************/ 497 | /* 498 | _readMeasurement() 499 | 500 | Start new measurement, read sensor data to buffer & collect errors 501 | 502 | NOTE: 503 | - sensors data structure: 504 | - {status, RH, RH, RH+T, T, T, CRC*}, *CRC for AHT2x only & for 505 | status description see "_readStatusRegister()" NOTE 506 | */ 507 | /**************************************************************************/ 508 | void AHTxx::_readMeasurement() 509 | { 510 | /* send measurement command */ 511 | Wire.beginTransmission(_address); 512 | 513 | Wire.write(AHTXX_START_MEASUREMENT_REG); //send measurement command, strat measurement 514 | Wire.write(AHTXX_START_MEASUREMENT_CTRL); //send measurement control 515 | Wire.write(AHTXX_START_MEASUREMENT_CTRL_NOP); //send measurement NOP control 516 | 517 | if (Wire.endTransmission(true) != 0) //collision on I2C bus 518 | { 519 | _status = AHTXX_ACK_ERROR; //update status byte, sensor didn't return ACK 520 | 521 | return; //no reason to continue 522 | } 523 | 524 | /* check busy bit */ 525 | _status = _getBusy(AHTXX_FORCE_READ_DATA); //update status byte, read status byte & check busy bit 526 | 527 | if (_status == AHTXX_BUSY_ERROR) {delay(AHTXX_MEASUREMENT_DELAY - AHTXX_CMD_DELAY);} 528 | else if (_status != AHTXX_NO_ERROR) {return;} //no reason to continue, received data smaller than expected 529 | 530 | /* read data from sensor */ 531 | uint8_t dataSize; 532 | 533 | if (_sensorType == AHT1x_SENSOR) {dataSize = 6;} //{status, RH, RH, RH+T, T, T, CRC*}, *CRC for AHT2x only 534 | else {dataSize = 7;} 535 | 536 | Wire.requestFrom(_address, dataSize, (uint8_t)true); //read n-byte to "wire.h" rxBuffer, true-send stop after transmission 537 | 538 | if (Wire.available() != dataSize) 539 | { 540 | _status = AHTXX_DATA_ERROR; //update status byte, received data smaller than expected 541 | 542 | return; //no reason to continue 543 | } 544 | 545 | /* read n-bytes from "wire.h" rxBuffer */ 546 | Wire.readBytes(_rawData, dataSize); //"readBytes()" from Stream Class 547 | 548 | /* check busy bit after measurement dalay */ 549 | _status = _getBusy(AHTXX_USE_READ_DATA); //update status byte, read status byte & check busy bit 550 | 551 | if (_status != AHTXX_NO_ERROR) {return;} //no reason to continue, sensor is busy 552 | 553 | /* check CRC8, for AHT2x only */ 554 | if ((_sensorType == AHT2x_SENSOR) && (_checkCRC8() != true)) {_status = AHTXX_CRC8_ERROR;} //update status byte 555 | } 556 | --------------------------------------------------------------------------------