├── .arduino-ci.yml ├── .github ├── FUNDING.yml └── workflows │ ├── arduino-lint.yml │ ├── arduino_test_runner.yml │ └── jsoncheck.yml ├── AGS02MA.cpp ├── AGS02MA.h ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples ├── AGS02MA_PPB │ └── AGS02MA_PPB.ino ├── AGS02MA_PPB_TIMING │ ├── AGS02MA_PPB_TIMING.ino │ ├── performance_0.3.0.txt │ ├── performance_0.3.1_10khz.txt │ └── performance_0.3.1_25khz.txt ├── AGS02MA_PPM │ └── AGS02MA_PPM.ino ├── AGS02MA_UGM3 │ └── AGS02MA_UGM3.ino ├── AGS02MA_calibrate │ └── AGS02MA_calibrate.ino ├── AGS02MA_calibrate_manual │ └── AGS02MA_calibrate_manual.ino ├── AGS02MA_get_registers │ ├── .arduino-ci.yml │ └── AGS02MA_get_registers.ino ├── AGS02MA_minimal │ └── AGS02MA_minimal.ino ├── AGS02MA_minimal_plotter │ └── AGS02MA_minimal_plotter.ino ├── AGS02MA_readRegister │ └── AGS02MA_readRegister.ino ├── AGS02MA_setAddress │ └── AGS02MA_setAddress.ino ├── AGS02MA_test │ └── AGS02MA_test.ino ├── AGS02MA_test_CRC8 │ ├── .arduino-ci.yml │ └── AGS02MA_test_CRC8.ino ├── issue │ └── issue.ino └── test_CRC8 │ ├── .arduino-ci.yml │ └── test_CRC8.ino ├── keywords.txt ├── library.json ├── library.properties └── test └── unit_test_001.cpp /.arduino-ci.yml: -------------------------------------------------------------------------------- 1 | platforms: 2 | rpipico: 3 | board: rp2040:rp2040:rpipico 4 | package: rp2040:rp2040 5 | gcc: 6 | features: 7 | defines: 8 | - ARDUINO_ARCH_RP2040 9 | warnings: 10 | flags: 11 | 12 | packages: 13 | rp2040:rp2040: 14 | url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 15 | 16 | compile: 17 | # Choosing to run compilation tests on 2 different Arduino platforms 18 | platforms: 19 | - uno 20 | # - due 21 | # - zero 22 | # - leonardo 23 | - m4 24 | - esp32 25 | - esp8266 26 | # - mega2560 27 | - rpipico 28 | 29 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: RobTillaart 4 | custom: "https://www.paypal.me/robtillaart" 5 | -------------------------------------------------------------------------------- /.github/workflows/arduino-lint.yml: -------------------------------------------------------------------------------- 1 | name: Arduino-lint 2 | 3 | on: [push, pull_request] 4 | jobs: 5 | lint: 6 | runs-on: ubuntu-latest 7 | timeout-minutes: 5 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: arduino/arduino-lint-action@v1 11 | with: 12 | library-manager: update 13 | compliance: strict -------------------------------------------------------------------------------- /.github/workflows/arduino_test_runner.yml: -------------------------------------------------------------------------------- 1 | name: Arduino CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | runTest: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 20 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: ruby/setup-ruby@v1 13 | with: 14 | ruby-version: 2.6 15 | - run: | 16 | gem install arduino_ci 17 | arduino_ci.rb 18 | -------------------------------------------------------------------------------- /.github/workflows/jsoncheck.yml: -------------------------------------------------------------------------------- 1 | name: JSON check 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.json' 7 | pull_request: 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 5 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: json-syntax-check 16 | uses: limitusus/json-syntax-check@v2 17 | with: 18 | pattern: "\\.json$" -------------------------------------------------------------------------------- /AGS02MA.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA.cpp 3 | // AUTHOR: Rob Tillaart, Viktor Balint, Beanow 4 | // DATE: 2021-08-12 5 | // VERSION: 0.4.3 6 | // PURPOSE: Arduino library for AGS02MA TVOC sensor 7 | // URL: https://github.com/RobTillaart/AGS02MA 8 | 9 | 10 | #include "AGS02MA.h" 11 | 12 | 13 | // REGISTERS 14 | #define AGS02MA_DATA 0x00 15 | #define AGS02MA_CALIBRATION 0x01 16 | #define AGS02MA_VERSION 0x11 17 | #define AGS02MA_SLAVE_ADDRESS 0x21 18 | 19 | 20 | 21 | AGS02MA::AGS02MA(const uint8_t deviceAddress, TwoWire *wire) 22 | { 23 | _address = deviceAddress; 24 | _wire = wire; 25 | reset(); 26 | } 27 | 28 | 29 | bool AGS02MA::begin() 30 | { 31 | _startTime = millis(); // PREHEAT TIMING 32 | return isConnected(); 33 | } 34 | 35 | 36 | bool AGS02MA::isConnected() 37 | { 38 | _setI2CLowSpeed(); 39 | _wire->beginTransmission(_address); 40 | bool rv = ( _wire->endTransmission(true) == 0); 41 | _setI2CHighSpeed(); 42 | return rv; 43 | } 44 | 45 | 46 | void AGS02MA::reset() 47 | { 48 | _I2CResetSpeed = 100000; 49 | _startTime = millis(); 50 | _lastRead = 0; 51 | _lastPPB = 0; 52 | _mode = 255; 53 | _status = AGS02MA_OK; 54 | _error = AGS02MA_OK; 55 | } 56 | 57 | 58 | bool AGS02MA::setAddress(const uint8_t deviceAddress) 59 | { 60 | if ((deviceAddress < 10) or (deviceAddress > 119)) return false; 61 | _buffer[2] = _buffer[0] = deviceAddress; 62 | _buffer[3] = _buffer[1] = 0xFF ^ deviceAddress; 63 | _buffer[4] = _CRC8(_buffer, 4); 64 | if (_writeRegister(AGS02MA_SLAVE_ADDRESS)) 65 | { 66 | _address = deviceAddress; 67 | } 68 | return isConnected(); 69 | } 70 | 71 | 72 | uint8_t AGS02MA::getSensorVersion() 73 | { 74 | uint8_t version = 0xFF; 75 | if (_readRegister(AGS02MA_VERSION)) 76 | { 77 | // for (int i = 0; i < 5; i++) 78 | // { 79 | // Serial.print(_buffer[i]); 80 | // Serial.print('\t'); 81 | // } 82 | // Serial.println(); 83 | // unclear what the other bytes have for information. 84 | // datasheet names these 3 bytes as KEEP. 85 | // BUFFER VALUE MEANING 86 | // buffer [0] == 20 year ? 87 | // buffer [1] == 07 month ? 88 | // buffer [2] == 28 day ? 89 | // buffer [3] == 117 VERSION 90 | // buffer [4] == CRC 91 | version = _buffer[3]; 92 | if (_CRC8(_buffer, 5) != 0) 93 | { 94 | _error = AGS02MA_ERROR_CRC; 95 | } 96 | } 97 | return version; 98 | } 99 | 100 | 101 | uint32_t AGS02MA::getSensorDate() 102 | { 103 | uint32_t date = 0xFFFFFFFF; 104 | if (_readRegister(AGS02MA_VERSION)) 105 | { 106 | date = 0x20; 107 | date <<= 8; 108 | date += _bin2bcd(_buffer[0]); 109 | date <<= 8; 110 | date += _bin2bcd(_buffer[1]); 111 | date <<= 8; 112 | date += _bin2bcd(_buffer[2]); 113 | // version = _buffer[3]; 114 | if (_CRC8(_buffer, 5) != 0) 115 | { 116 | _error = AGS02MA_ERROR_CRC; 117 | } 118 | } 119 | return date; 120 | } 121 | 122 | 123 | bool AGS02MA::setPPBMode() 124 | { 125 | _buffer[0] = 0x00; 126 | _buffer[1] = 0xFF; 127 | _buffer[2] = 0x00; 128 | _buffer[3] = 0xFF; 129 | _buffer[4] = 0x30; 130 | if (_writeRegister(AGS02MA_DATA)) 131 | { 132 | _mode = 0; 133 | return true; 134 | } 135 | return false; 136 | } 137 | 138 | 139 | bool AGS02MA::setUGM3Mode() 140 | { 141 | _buffer[0] = 0x02; 142 | _buffer[1] = 0xFD; 143 | _buffer[2] = 0x02; 144 | _buffer[3] = 0xFD; 145 | _buffer[4] = 0x00; 146 | if (_writeRegister(AGS02MA_DATA)) 147 | { 148 | _mode = 1; 149 | return true; 150 | } 151 | return false; 152 | } 153 | 154 | 155 | uint32_t AGS02MA::readPPB() 156 | { 157 | uint32_t value = _readSensor(); 158 | if (_error == AGS02MA_OK) 159 | { 160 | _lastRead = millis(); 161 | _lastPPB = value; 162 | } 163 | else 164 | { 165 | value = _lastPPB; 166 | } 167 | return value; 168 | } 169 | 170 | 171 | uint32_t AGS02MA::readUGM3() 172 | { 173 | uint32_t value = _readSensor(); 174 | if (_error == AGS02MA_OK) 175 | { 176 | _lastRead = millis(); 177 | _lastUGM3 = value; 178 | } 179 | else 180 | { 181 | value = _lastUGM3; 182 | } 183 | return value; 184 | } 185 | 186 | 187 | bool AGS02MA::manualZeroCalibration(uint16_t value) 188 | { 189 | _buffer[0] = 0x00; 190 | _buffer[1] = 0x0C; 191 | _buffer[2] = (uint8_t) (value >> 8); 192 | _buffer[3] = (uint8_t) (value & 0x00FF); 193 | _buffer[4] = _CRC8(_buffer, 4); 194 | return _writeRegister(AGS02MA_CALIBRATION); 195 | } 196 | 197 | 198 | bool AGS02MA::getZeroCalibrationData(AGS02MA::ZeroCalibrationData &data) { 199 | if (!_readRegister(AGS02MA_CALIBRATION)) 200 | { 201 | return false; 202 | } 203 | 204 | if (_CRC8(_buffer, 5) != 0) 205 | { 206 | _error = AGS02MA_ERROR_CRC; 207 | return false; 208 | } 209 | 210 | _error = AGS02MA_OK; 211 | // Don't pollute the struct given to us, until we've handled all error cases. 212 | data.status = _getDataMSB(); 213 | data.value = _getDataLSB(); 214 | return true; 215 | } 216 | 217 | 218 | int AGS02MA::lastError() 219 | { 220 | int e = _error; 221 | _error = AGS02MA_OK; // reset error after read 222 | return e; 223 | } 224 | 225 | 226 | bool AGS02MA::readRegister(uint8_t address, AGS02MA::RegisterData ®) { 227 | if (!_readRegister(address)) 228 | { 229 | return false; 230 | } 231 | 232 | if (_CRC8(_buffer, 5) != 0) 233 | { 234 | _error = AGS02MA_ERROR_CRC; 235 | return false; 236 | } 237 | 238 | _error = AGS02MA_OK; 239 | // Don't pollute the struct given to us, until we've handled all error cases. 240 | reg.data[0] = _buffer[0]; 241 | reg.data[1] = _buffer[1]; 242 | reg.data[2] = _buffer[2]; 243 | reg.data[3] = _buffer[3]; 244 | reg.crc = _buffer[4]; 245 | reg.crcValid = true; // checked above. 246 | return true; 247 | } 248 | 249 | 250 | ///////////////////////////////////////////////////////// 251 | // 252 | // PRIVATE 253 | // 254 | uint32_t AGS02MA::_readSensor() 255 | { 256 | uint32_t value = 0; 257 | if (_readRegister(AGS02MA_DATA)) 258 | { 259 | _error = AGS02MA_OK; 260 | _status = _buffer[0]; 261 | if (_status & 0x01) 262 | { 263 | _error = AGS02MA_ERROR_NOT_READY; 264 | } 265 | value = _buffer[1] * 65536UL; 266 | value += _buffer[2] * 256; 267 | value += _buffer[3]; 268 | if (_CRC8(_buffer, 5) != 0) 269 | { 270 | _error = AGS02MA_ERROR_CRC; 271 | } 272 | } 273 | return value; 274 | } 275 | 276 | 277 | bool AGS02MA::_readRegister(uint8_t reg) 278 | { 279 | while (millis() - _lastRegTime < 30) yield(); 280 | 281 | _setI2CLowSpeed(); 282 | 283 | _wire->beginTransmission(_address); 284 | _wire->write(reg); 285 | _error = _wire->endTransmission(true); 286 | if (_error != 0) 287 | { 288 | // _error will be I2C error code 289 | _setI2CHighSpeed(); 290 | return false; 291 | } 292 | // TODO investigate async interface 293 | delay(30); 294 | 295 | if (_wire->requestFrom(_address, (uint8_t)5) != 5) 296 | { 297 | _error = AGS02MA_ERROR_READ; 298 | _setI2CHighSpeed(); 299 | return false; 300 | } 301 | for (uint8_t i = 0; i < 5; i++) 302 | { 303 | _buffer[i] = _wire->read(); 304 | } 305 | _error = AGS02MA_OK; 306 | _setI2CHighSpeed(); 307 | return true; 308 | } 309 | 310 | 311 | bool AGS02MA::_writeRegister(uint8_t reg) 312 | { 313 | while (millis() - _lastRegTime < 30) yield(); 314 | _lastRegTime = millis(); 315 | 316 | _setI2CLowSpeed(); 317 | 318 | _wire->beginTransmission(_address); 319 | _wire->write(reg); 320 | for (uint8_t i = 0; i < 5; i++) 321 | { 322 | _wire->write(_buffer[i]); 323 | } 324 | _error = _wire->endTransmission(true); 325 | _setI2CHighSpeed(); 326 | return (_error == 0); 327 | } 328 | 329 | 330 | void AGS02MA::_setI2CLowSpeed() 331 | { 332 | #if defined (__AVR__) 333 | // TWBR = 255; // == 30.4 KHz with TWSR = 0x00 334 | TWBR = 78; // == 25.0 KHZ 335 | TWSR = 0x01; // pre-scaler = 4 336 | #else 337 | _wire->setClock(AGS02MA_I2C_CLOCK); 338 | #endif 339 | } 340 | 341 | 342 | void AGS02MA::_setI2CHighSpeed() 343 | { 344 | #if defined (__AVR__) 345 | TWSR = 0x00; 346 | #endif 347 | _wire->setClock(_I2CResetSpeed); 348 | } 349 | 350 | 351 | uint16_t AGS02MA::_getDataMSB() 352 | { 353 | return (_buffer[0] << 8) + _buffer[1]; 354 | } 355 | 356 | 357 | uint16_t AGS02MA::_getDataLSB() 358 | { 359 | return (_buffer[2] << 8) + _buffer[3]; 360 | } 361 | 362 | 363 | uint8_t AGS02MA::_CRC8(uint8_t * buf, uint8_t size) 364 | { 365 | uint8_t crc = 0xFF; // start value 366 | for (uint8_t b = 0; b < size; b++) 367 | { 368 | crc ^= buf[b]; 369 | for (uint8_t i = 0; i < 8; i++) 370 | { 371 | if (crc & 0x80) crc = (crc << 1) ^ 0x31; 372 | else crc = (crc << 1); 373 | } 374 | } 375 | return crc; 376 | } 377 | 378 | 379 | uint8_t AGS02MA::_bin2bcd (uint8_t value) 380 | { 381 | return value + 6 * (value / 10); 382 | } 383 | 384 | 385 | // -- END OF FILE -- 386 | 387 | -------------------------------------------------------------------------------- /AGS02MA.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | // FILE: AGS02MA.h 4 | // AUTHOR: Rob Tillaart, Viktor Balint, Beanow 5 | // DATE: 2021-08-12 6 | // VERSION: 0.4.3 7 | // PURPOSE: Arduino library for AGS02MA TVOC sensor 8 | // URL: https://github.com/RobTillaart/AGS02MA 9 | 10 | 11 | #include "Arduino.h" 12 | #include "Wire.h" 13 | 14 | 15 | #define AGS02MA_LIB_VERSION (F("0.4.3")) 16 | 17 | #define AGS02MA_OK 0 18 | #define AGS02MA_ERROR -10 19 | #define AGS02MA_ERROR_CRC -11 20 | #define AGS02MA_ERROR_READ -12 21 | #define AGS02MA_ERROR_NOT_READY -13 22 | #define AGS02MA_ERROR_REQUEST -14 23 | 24 | 25 | #define AGS02MA_I2C_CLOCK 25000 // max 30000 26 | 27 | 28 | class AGS02MA 29 | { 30 | public: 31 | struct RegisterData 32 | { 33 | uint8_t data[4]; 34 | uint8_t crc; 35 | bool crcValid; 36 | }; 37 | 38 | struct ZeroCalibrationData 39 | { 40 | /** 41 | * Warning, the exact meaning of this status is not fully documented. 42 | * It seems like it's a bit mask: 43 | * 0000 1100 | 0x0C | 12 | Typical value 44 | * 0000 1101 | 0x0D | 13 | Sometimes seen on v117 45 | * 0111 1101 | 0x7D | 125 | Seen on v118, after power-off (gives different data than 12!) 46 | */ 47 | uint16_t status; 48 | uint16_t value; 49 | }; 50 | 51 | // address 26 = 0x1A 52 | explicit AGS02MA(const uint8_t deviceAddress = 26, TwoWire *wire = &Wire); 53 | 54 | bool begin(); 55 | bool isConnected(); 56 | void reset(); 57 | 58 | bool isHeated() { return (millis() - _startTime) > 120000UL; }; 59 | 60 | 61 | // CONFIGURATION 62 | bool setAddress(const uint8_t deviceAddress); 63 | uint8_t getAddress() { return _address; }; 64 | uint8_t getSensorVersion(); 65 | uint32_t getSensorDate(); 66 | 67 | // to set the speed the I2C bus should return to 68 | // as the device operates at very low bus speed of 30 kHz. 69 | void setI2CResetSpeed(uint32_t speed) { _I2CResetSpeed = speed; }; 70 | uint32_t getI2CResetSpeed() { return _I2CResetSpeed; }; 71 | 72 | // to be called after at least 5 minutes in fresh air. 73 | bool zeroCalibration() { return manualZeroCalibration(0); }; 74 | 75 | /** 76 | * Set the zero calibration value manually. 77 | * To be called after at least 5 minutes in fresh air. 78 | * For v117: 0-65535 = automatic calibration. 79 | * For v118: 0 = automatic calibration, 1-65535 manual calibration. 80 | */ 81 | bool manualZeroCalibration(uint16_t value = 0); 82 | bool getZeroCalibrationData(ZeroCalibrationData &data); 83 | 84 | 85 | // MODE 86 | bool setPPBMode(); 87 | bool setUGM3Mode(); 88 | uint8_t getMode() { return _mode; }; 89 | 90 | 91 | // READ functions 92 | uint32_t readPPB(); // parts per billion 10^9 93 | uint32_t readUGM3(); // microgram per cubic meter 94 | 95 | // derived read functions 96 | float readPPM() { return readPPB() * 0.001; }; // parts per million 97 | float readMGM3() { return readUGM3() * 0.001; }; // milligram per cubic meter 98 | float readUGF3() { return readUGM3() * 0.0283168466; }; // microgram per cubic feet 99 | 100 | float lastPPM() { return _lastPPB * 0.001; }; 101 | uint32_t lastPPB() { return _lastPPB; }; // fetch last PPB measurement 102 | uint32_t lastUGM3() { return _lastUGM3; }; // fetch last UGM3 measurement 103 | 104 | 105 | // STATUS 106 | uint32_t lastRead() { return _lastRead; }; // timestamp last measurement 107 | int lastError(); 108 | uint8_t lastStatus() { return _status; }; 109 | uint8_t dataReady() { return _status & 0x01; }; 110 | 111 | // Reading registers 112 | bool readRegister(uint8_t address, RegisterData ®); 113 | 114 | 115 | private: 116 | uint32_t _readSensor(); 117 | bool _readRegister(uint8_t reg); 118 | bool _writeRegister(uint8_t reg); 119 | 120 | uint32_t _I2CResetSpeed = 100000; 121 | uint32_t _startTime = 0; 122 | uint32_t _lastRead = 0; 123 | uint32_t _lastRegTime = 0; 124 | uint32_t _lastPPB = 0; 125 | uint32_t _lastUGM3 = 0; 126 | uint8_t _address = 0; 127 | uint8_t _mode = 255; 128 | uint8_t _status = 0; 129 | uint8_t _buffer[5]; 130 | 131 | void _setI2CLowSpeed(); 132 | void _setI2CHighSpeed(); 133 | uint16_t _getDataMSB(); 134 | uint16_t _getDataLSB(); 135 | uint8_t _CRC8(uint8_t * buf, uint8_t size); 136 | uint8_t _bin2bcd(uint8_t val); 137 | 138 | int _error = AGS02MA_OK; 139 | 140 | TwoWire* _wire; 141 | }; 142 | 143 | 144 | // -- END OF FILE -- 145 | 146 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log AGS02MA 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | 9 | 10 | ## [0.4.3] - 2025-08-15 11 | - update readme.md 12 | - update license 13 | - minor edits 14 | 15 | ## [0.4.2] - 2024-02-03 16 | - update readme.md 17 | - added multiplexer section 18 | - extended PPB "health" table 19 | - clean up examples 20 | - refactor conditional code 21 | - added **setI2CLowSpeed()** and **setI2CHighSpeed()** 22 | - improved error handling a bit. 23 | - added **AGS02MA_ERROR_REQUEST** 24 | - redo **readRegister()** 25 | - minor edits 26 | 27 | ## [0.4.1] - 2023-12-10 28 | - fix #26, error in readme. 29 | 30 | ## [0.4.0] - 2023-12-06 31 | - refactor API, begin() 32 | - update readme.md 33 | 34 | ---- 35 | 36 | ## [0.3.4] - 2023-09-25 37 | - add Wire1 support for ESP32 38 | - update readme.md 39 | - minor edits 40 | 41 | ## [0.3.3] - 2023-01-21 42 | - update GitHub actions 43 | - update license 2023 44 | - update keywords 45 | - minor edit readme.md 46 | - minor edit code 47 | - add CHANGELOG.md (for real) 48 | 49 | ## [0.3.2] - 2022-10-26 50 | - add CHANGELOG.md 51 | - add RP2040 in build 52 | 53 | ---- 54 | 55 | ## no info 56 | 57 | - 0.3.1 58 | - 0.3.0 59 | - 0.2.0 60 | - 0.1.4 61 | - 0.1.3 62 | - 0.1.2 63 | - 0.1.1 64 | - 0.1.0 65 | 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2025 Rob Tillaart 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Arduino CI](https://github.com/RobTillaart/AGS02MA/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) 3 | [![Arduino-lint](https://github.com/RobTillaart/AGS02MA/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/AGS02MA/actions/workflows/arduino-lint.yml) 4 | [![JSON check](https://github.com/RobTillaart/AGS02MA/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/AGS02MA/actions/workflows/jsoncheck.yml) 5 | [![GitHub issues](https://img.shields.io/github/issues/RobTillaart/AGS02MA.svg)](https://github.com/RobTillaart/AGS02MA/issues) 6 | 7 | [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/AGS02MA/blob/master/LICENSE) 8 | [![GitHub release](https://img.shields.io/github/release/RobTillaart/AGS02MA.svg?maxAge=3600)](https://github.com/RobTillaart/AGS02MA/releases) 9 | [![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/AGS02MA.svg)](https://registry.platformio.org/libraries/robtillaart/AGS02MA) 10 | 11 | 12 | # AGS02MA 13 | 14 | Arduino library for AGS02MA TVOC sensor. 15 | 16 | 17 | ### Description 18 | 19 | **Experimental** 20 | 21 | This library is experimental, so please use with care. 22 | 23 | The AGS02MA is a sensor that measures the TVOC = Total Volatile Organic Compounds 24 | in the air. It does not measure a specific gas, but several. 25 | 26 | 27 | Note the warning about the I2C low speed, the device works at max 30 KHz. 28 | Since 0.3.1 this library uses 25 KHz. 29 | 30 | Feedback as always, is welcome. Please open an issue. 31 | 32 | Note this library is **not** meant to replace professional monitoring systems. 33 | 34 | 35 | ### 0.4.0 Breaking change 36 | 37 | Version 0.4.0 introduced a breaking change. 38 | You cannot set the pins in **begin()** any more. 39 | This reduces the dependency of processor dependent Wire implementations. 40 | The user has to call **Wire.begin()** and can optionally set the Wire pins 41 | before calling **begin()**. 42 | 43 | 44 | ### Related 45 | 46 | - https://github.com/RobTillaart/AGS02MA TVOC sensor 47 | - https://github.com/RobTillaart/AGS2616 H2 sensor 48 | - https://github.com/RobTillaart/AGS3870 CH4 sensor 49 | - https://github.com/RobTillaart/AGS3871 CO sensor 50 | - https://www.renesas.com/us/en/document/whp/overview-tvoc-and-indoor-air-quality 51 | - https://github.com/RobTillaart/SGP30 (experimental) 52 | - https://github.com/RobTillaart/AtomicWeight (determine Mass from chemical formula) 53 | - https://github.com/RobTillaart/map2colour 54 | 55 | 56 | ## I2C 57 | 58 | ### Pin layout from left to right 59 | 60 | Always check datasheet! 61 | 62 | | Front L->R | Description | 63 | |:------------:|:--------------| 64 | | pin 1 | VDD +5V | 65 | | pin 2 | SDA data | 66 | | pin 3 | GND | 67 | | pin 4 | SCL clock | 68 | 69 | 70 | ### Address 71 | 72 | The device has a fixed address of 26 or 0x1A. 73 | 74 | The device works at 100 kHz I2C bus speed (datasheet). 75 | 76 | Note: several AGS devices use the same I2C address 0x1A. 77 | Known are the AGS2616 (H2), AGS3870 (CH4), AGS3871 (CO), AGS02MA (TVOC). 78 | If you want to use them on one I2C bus, you need multiplexing. 79 | See section below. 80 | 81 | 82 | ### WARNING - LOW SPEED 83 | 84 | The sensor uses I2C at very low speed <= 30 KHz. 85 | For an Arduino UNO the lowest speed supported is about 30.4KHz (TWBR = 255) which works 86 | in my testruns so far. 87 | First runs with Arduino UNO indicate 2 failed reads in > 500 Reads, so less than 1% 88 | failure rate. 89 | 90 | Tests with ESP32 / ESP8266 at 30 KHz look good, 91 | tests with ESP32 at lower clock speeds are to be done but expected to work. 92 | 93 | The library sets the clock speed to 30 KHz (for non AVR) during operation 94 | and resets the I2C clock speed default to 100 KHz after operation. 95 | This is done to minimize interference with the communication of other devices. 96 | The "reset clock speed" can be changed with **setI2CResetSpeed(speed)** e.g. to 200 or 400 KHz. 97 | 98 | 99 | ### 0.3.1 fix. 100 | 101 | Version 0.3.1 sets the **I2C prescaler TWSR** register of the Arduino UNO to 4 so the lowest 102 | speed possible is reduced to about 8 KHz. 103 | A test run 4 hours with 6000++ reads on an UNO at 25 KHz gave 0 errors. 104 | So the communication speed will be set to 25 KHz, also for other boards, for stability. 105 | After communication the I2C clock (+ prescaler) is reset again as before. 106 | 107 | 108 | ### I2C multiplexing 109 | 110 | Sometimes you need to control more devices than possible with the default 111 | address range the device provides. 112 | This is possible with an I2C multiplexer e.g. TCA9548 which creates up 113 | to eight channels (think of it as I2C subnets) which can use the complete 114 | address range of the device. 115 | 116 | Drawback of using a multiplexer is that it takes more administration in 117 | your code e.g. which device is on which channel. 118 | This will slow down the access, which must be taken into account when 119 | deciding which devices are on which channel. 120 | Also note that switching between channels will slow down other devices 121 | too if they are behind the multiplexer. 122 | 123 | - https://github.com/RobTillaart/TCA9548 124 | 125 | 126 | ## Version 118 problems 127 | 128 | The library can request the version with **getSensorVersion()**. 129 | My devices all report version 117 and this version is used to develop / test this library. 130 | There are devices reported with version 118 which behave differently. 131 | 132 | 133 | ### ugM3 not supported 134 | 135 | See - https://github.com/RobTillaart/AGS02MA/issues/11 136 | 137 | The version 118 seems only to support the **PPB** and not the **ugM3** mode. 138 | It is unclear if this is an incident, bug or a structural change in the firmware. 139 | 140 | If you encounter similar problems with setting the mode (any version), please let me know. 141 | That will help indicating if this is a "structural change" or incident. 142 | 143 | 144 | ### Calibrate problem! 145 | 146 | See - https://github.com/RobTillaart/AGS02MA/issues/13 147 | 148 | In this issue a problem is reported with a version 118 sensor. 149 | The problem exposed itself after running the calibration sketch (command). 150 | The problem has been confirmed by a 2nd version 118 sensor. 151 | Additional calibration runs did not fix the problem. 152 | Version 117 seem to have no problems with calibration. 153 | 154 | **Advice**: do **NOT** calibrate a version 118. 155 | 156 | Note: the version 0.2.0 determines the version in the calibration function so 157 | it won't calibrate any non 117 version. 158 | 159 | 160 | ### Please report your experiences. 161 | 162 | If you have a AGS20MA device, version 117 or 118 or other, 163 | please let me know your experiences 164 | with the sensor and this (or other) library. 165 | 166 | 167 | ## Interface 168 | 169 | ```cpp 170 | #include "AGS02MA.h" 171 | ``` 172 | 173 | ### Constructor 174 | 175 | - **AGS02MA(uint8_t deviceAddress = 26, TwoWire \*wire = &Wire)** constructor, 176 | with default address and default I2C interface. 177 | - **bool begin()** initialize the library. 178 | Returns false if deviceAddress cannot be seen on the I2C bus. 179 | - **bool isConnected()** returns true if deviceAddress can be seen on I2C, false otherwise. 180 | - **void reset()** resets the internal variables. 181 | 182 | 183 | ### Heating 184 | 185 | - **bool isHeated()** returns true if 2 minutes have passed after call of **begin()**. 186 | Otherwise the device is not optimal ready. 187 | According to the datasheet, preheating will improve the quality of the measurements. 188 | Note: if begin() is not called, isHeated() might be incorrect. 189 | - **uint32_t lastRead()** returns the last time the device is read, 190 | timestamp is in milliseconds since start. 191 | Returns 0 if **readPPB()** or **readUGM3()** is not called yet. 192 | This function allows to implement sort of asynchronous wait. 193 | One must keep reads / measurements at least 1.5 seconds but preferred 3 seconds 194 | apart according to the datasheet. 195 | 196 | 197 | ### Administration 198 | 199 | - **bool setAddress(const uint8_t deviceAddress)** sets a new address for the sensor. 200 | If function succeeds the address changes immediately and will be persistent over a reboot. 201 | - **uint8_t getAddress()** returns the set address. Default the function will return 26 or 0x1A. 202 | - **uint8_t getSensorVersion()** reads sensor version from the device. 203 | If the version cannot be read the function will return 255. 204 | My test sensors all return version 117, version 118 is reported to exist too. 205 | - **uint32_t getSensorDate()** (experimental) reads bytes from the sensor that seem 206 | to indicate the production date(?). 207 | This date is encoded in an uint32_t to minimize footprint as it is a debug function. 208 | 209 | ```cpp 210 | uint32_t dd = sensor.getSensorDate(); 211 | Serial.println(dd, HEX); // prints YYYYMMDD e.g. 20210203 212 | ``` 213 | 214 | 215 | ### I2C clock speed 216 | 217 | The library sets the clock speed to 25 KHz during operation 218 | and resets it to 100 KHz after operation. 219 | This is done to minimize interference with the communication of other devices. 220 | The following function can change the I2C reset speed to e.g. 200 or 400 KHz. 221 | 222 | - **void setI2CResetSpeed(uint32_t speed)** sets the I2C speed the library need to reset the I2C speed to. 223 | - **uint32_t getI2CResetSpeed()** returns the value set. Default is 100 KHz. 224 | 225 | 226 | ### setMode 227 | 228 | The default mode at startup of the sensor is PPB = parts per billion. 229 | 230 | - **bool setPPBMode()** sets device in PartPerBillion mode. Returns true on success. 231 | - **bool setUGM3Mode()** sets device in micro gram per cubic meter mode. Returns true on success. 232 | - **uint8_t getMode()** returns mode set. 0 = PPB, 1 = UGm3, 255 = not set. 233 | 234 | 235 | ### Air quality classification 236 | 237 | Indicative description and colour representation. 238 | 239 | | TVOC(ppb) | Scale | Description | Colour | Notes | 240 | |:---------:|:-------:|:---------------------:|:-------------|:--------| 241 | | <= 220 | 1 | Good | Green | 242 | | <= 660 | 3 | Moderate | Yellow | 243 | | <= 1430 | 7 | Bad | Orange | 244 | | <= 2200 | 10 | Unhealthy | Red | 245 | | <= 3300 | 15 | Very unhealthy | Purple | add pulsating effect 246 | | <= 5500 | 25 | Hazardous | Deep Purple | add pulsating effect 247 | | > 5500 | 50 | Extremely Hazardous | Deep Purple | add pulsating effect 248 | 249 | [Source](https://learn.kaiterra.com/en/resources/understanding-tvoc-volatile-organic-compounds) 250 | 251 | - Scale is a relative (linear) scale where 220 ~~ 1 252 | - Colour is an indicative colour mapping. 253 | - https://github.com/RobTillaart/map2colour for continuous colour scale mapping. 254 | 255 | 256 | ### PPB versus UGM3 257 | 258 | There is no 1 to 1 relation between the PPB and the uG/m3 readings as this relation depends 259 | on the weight of the individual molecules. 260 | PPB is therefore an more an absolute indicator where uG/m3 is sort of relative indicator. 261 | If the gas is unknown, PPB is in my opinion the preferred measurement. 262 | 263 | 264 | From an unverified source the following formula: 265 | M = molecular weight of the gas. 266 | 267 | **μg/m3 = ppb \* M \* 12.187 / (273.15 + °C)** 268 | 269 | Simplified formula for 1 atm @ 25°C: 270 | 271 | **μg/m3 = ppb \* M \* 0.04087539829** 272 | 273 | Some known gasses 274 | 275 | | gas | Common name | ratio ppb-μg/m3 | molecular weight M | 276 | |:-------|:--------------------|:----------------------|:--------------------:| 277 | | SO2 | Sulphur dioxide | 1 ppb = 2.62 μg/m3 | 64 gr/mol | 278 | | NO2 | Nitrogen dioxide | 1 ppb = 1.88 μg/m3 | 46 gr/mol | 279 | | NO | Nitrogen monoxide | 1 ppb = 1.25 μg/m3 | 30 gr/mol | 280 | | O3 | Ozone | 1 ppb = 2.00 μg/m3 | 48 gr/mol | 281 | | CO | Carbon Monoxide | 1 ppb = 1.145 μg/m3 | 28 gr/mol | 282 | | C6H6 | Benzene | 1 ppb = 3.19 μg/m3 | 78 gr/mol | 283 | 284 | 285 | - https://github.com/RobTillaart/AtomicWeight (determine Mass from chemical formula) 286 | 287 | 288 | ### Read the sensor 289 | 290 | WARNING: The datasheet advises to take 3 seconds between reads. 291 | Tests gave stable results at 1.5 second intervals. 292 | Use this faster rate at your own risk. 293 | 294 | - **uint32_t readPPB()** reads the PPB (parts per billion) from the device. 295 | Typical value should be between 1 .. 999999. 296 | Returns **lastPPB()** value if failed so one does not get sudden jumps in graphs. 297 | Check **lastStatus()** and **lastError()** to get more info about the success of the read(). 298 | Time needed is ~35 milliseconds (which might cause problems). 299 | - **uint32_t readUGM3()** reads UGM3 (microgram per cubic meter) current value from device. 300 | Typical values depend on the molecular weight of the TVOC. 301 | Returns **lastUGM3()** if failed so one does not get sudden jumps in graphs. 302 | 303 | Wrappers 304 | 305 | - **float readPPM()** returns parts per million (PPM). 306 | This function is a wrapper around readPPB(). 307 | Typical value should be between 0.01 .. 999.99 308 | - **float readMGM3()** returns milligram per cubic meter. 309 | - **float readUGF3()** returns microgram per cubic feet. 310 | 311 | 312 | ### Error Codes 313 | 314 | | ERROR_CODES | value | 315 | |:----------------------------|:-------:| 316 | | AGS02MA_OK | 0 | 317 | | AGS02MA_ERROR | -10 | 318 | | AGS02MA_ERROR_CRC | -11 | 319 | | AGS02MA_ERROR_READ | -12 | 320 | | AGS02MA_ERROR_NOT_READY | -13 | 321 | 322 | 323 | ### Cached values 324 | 325 | - **float lastPPM()** returns last readPPM (parts per million) value (cached). 326 | - **uint32_t lastPPB()** returns last read PPB (parts per billion) value (cached). Should be between 1..999999. 327 | - **uint32_t lastUGM3()** returns last read UGM3 (microgram per cubic meter) value (cached). 328 | 329 | 330 | ### Calibration 331 | 332 | - **bool zeroCalibration()** to be called after at least 5 minutes in fresh air. 333 | See example sketch. 334 | - **bool manualZeroCalibration(uint16_t value = 0)** Set the zero calibration value manually. 335 | To be called after at least 5 minutes in fresh air. 336 | - For v117: 0-65535 = automatic calibration. 337 | - For v118: 0 = automatic calibration, 1-65535 manual calibration. 338 | - **bool getZeroCalibrationData(ZeroCalibrationData &data)** fills a data struct with the 339 | current zero calibration status and value. 340 | Returns true on success. 341 | 342 | 343 | ### Other 344 | 345 | - **bool readRegister(uint8_t address, RegisterData ®)** fills a data struct with the chip's register data at that address. 346 | Primarily intended for troubleshooting and analysis of the sensor. Not recommended to build applications on top of this method's raw data. 347 | Returns true when the **RegisterData** is filled, false when the data could not be read. 348 | Note: unlike other public methods, CRC errors don't return false or show up in `lastError()`, 349 | instead the CRC result is stored in `RegisterData.crcValid`. 350 | - **int lastError()** returns last error. 351 | - **uint8_t lastStatus()** returns status byte from last read. 352 | Read datasheet or table below for details. A new read is needed to update this. 353 | - **uint8_t dataReady()** returns RDY bit from last read. 354 | 355 | 356 | ### Status bits. 357 | 358 | | bit | description | notes | 359 | |:-----:|:------------------------------------|:--------| 360 | | 7-4 | internal use | 361 | | 3-1 | 000 = PPB 001 = uG/M3 | 362 | | 0 | RDY bit 0 = ready 1 = not ready | 1 == busy 363 | 364 | 365 | ## Future 366 | 367 | #### Must 368 | 369 | - improve documentation 370 | - references? 371 | 372 | #### Should 373 | 374 | - check the mode bits of the status byte with internal \_mode. 375 | - maximize robustness of state 376 | - test with hardware 377 | - different gasses ? indoor / outdoor? 378 | - test with different processors 379 | - isHeated() bugs if begin() is not called before... 380 | 381 | #### Could 382 | 383 | - elaborate error handling. 384 | - create an async interface for **readPPB()** if possible 385 | - delay(30) blocks performance ==> async version of **readRegister()** 386 | - could introduce complex I2C speed handling... 387 | - separate state - request pending or so? 388 | - move code to .cpp? 389 | 390 | #### Wont 391 | 392 | 393 | ## Support 394 | 395 | If you appreciate my libraries, you can support the development and maintenance. 396 | Improve the quality of the libraries by providing issues and Pull Requests, or 397 | donate through PayPal or GitHub sponsors. 398 | 399 | Thank you, 400 | 401 | -------------------------------------------------------------------------------- /examples/AGS02MA_PPB/AGS02MA_PPB.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_PPB.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | 7 | 8 | #include "AGS02MA.h" 9 | 10 | 11 | AGS02MA AGS(26); 12 | 13 | 14 | void setup() 15 | { 16 | // ESP devices typically miss the first serial log lines after flashing. 17 | // Delay somewhat to include all output. 18 | delay(1000); 19 | 20 | Serial.begin(115200); 21 | Serial.println(); 22 | Serial.println(__FILE__); 23 | Serial.print("AGS02MA_LIB_VERSION: "); 24 | Serial.println(AGS02MA_LIB_VERSION); 25 | Serial.println(); 26 | 27 | Wire.begin(); 28 | 29 | bool b = AGS.begin(); 30 | Serial.print("BEGIN:\t"); 31 | Serial.println(b); 32 | 33 | Serial.print("VERSION:\t"); 34 | Serial.println(AGS.getSensorVersion()); 35 | 36 | // pre-heating improves measurement quality 37 | // can be skipped 38 | Serial.println("\nWarming up (120 seconds = 24 dots)"); 39 | while (AGS.isHeated() == false) 40 | { 41 | delay(5000); 42 | Serial.print("."); 43 | } 44 | Serial.println(); 45 | 46 | b = AGS.setPPBMode(); 47 | uint8_t m = AGS.getMode(); 48 | Serial.print("MODE:\t"); 49 | Serial.print(b); 50 | Serial.print("\t"); 51 | Serial.println(m); 52 | 53 | uint8_t version = AGS.getSensorVersion(); 54 | Serial.print("VERS:\t"); 55 | Serial.println(version); 56 | } 57 | 58 | 59 | void loop() 60 | { 61 | delay(3000); 62 | uint32_t value = AGS.readPPB(); 63 | Serial.print("PPB:\t"); 64 | Serial.print(value); 65 | Serial.print("\t"); 66 | Serial.print(AGS.lastStatus(), HEX); 67 | Serial.print("\t"); 68 | Serial.print(AGS.lastError(), HEX); 69 | Serial.println(); 70 | 71 | } 72 | 73 | 74 | // -- END OF FILE -- 75 | -------------------------------------------------------------------------------- /examples/AGS02MA_PPB_TIMING/AGS02MA_PPB_TIMING.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_PPB_TIMING.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | // 7 | 8 | #include "AGS02MA.h" 9 | 10 | 11 | AGS02MA AGS(26); 12 | 13 | 14 | void setup() 15 | { 16 | // ESP devices typically miss the first serial log lines after flashing. 17 | // Delay somewhat to include all output. 18 | delay(1000); 19 | 20 | Serial.begin(115200); 21 | Serial.println(); 22 | Serial.println(__FILE__); 23 | Serial.print("AGS02MA_LIB_VERSION: "); 24 | Serial.println(AGS02MA_LIB_VERSION); 25 | Serial.println(); 26 | 27 | Wire.begin(); 28 | 29 | bool b = AGS.begin(); 30 | Serial.print("BEGIN:\t"); 31 | Serial.println(b); 32 | 33 | Serial.print("VERS:\t"); 34 | Serial.println(AGS.getSensorVersion()); 35 | 36 | // pre-heating improves measurement quality 37 | // can be skipped 38 | Serial.println("\nWarming up (120 seconds = 24 dots)"); 39 | while (AGS.isHeated() == false) 40 | { 41 | delay(5000); 42 | Serial.print("."); 43 | } 44 | Serial.println(); 45 | 46 | b = AGS.setPPBMode(); 47 | uint8_t m = AGS.getMode(); 48 | Serial.print("MODE:\t"); 49 | Serial.print(b); 50 | Serial.print("\t"); 51 | Serial.println(m); 52 | } 53 | 54 | 55 | void loop() 56 | { 57 | delay(1500); 58 | uint32_t start = millis(); 59 | uint32_t value = AGS.readPPB(); 60 | uint32_t stop = millis(); 61 | 62 | uint32_t duration = stop - start; 63 | Serial.print(duration); 64 | Serial.print("\t"); 65 | Serial.print(value); 66 | Serial.print("\t"); 67 | Serial.print(AGS.lastStatus(), HEX); 68 | Serial.print("\t"); 69 | Serial.print(AGS.lastError(), HEX); 70 | Serial.println(); 71 | } 72 | 73 | 74 | // -- END OF FILE -- 75 | 76 | -------------------------------------------------------------------------------- /examples/AGS02MA_PPB_TIMING/performance_0.3.0.txt: -------------------------------------------------------------------------------- 1 | 2 | 30 KHz 3 | 4 | 16:14:33.046 -> ...\AGS02MA_PPB_TIMING.ino 5 | 16:14:33.046 -> AGS02MA_LIB_VERSION: 0.3.0 6 | 16:14:33.046 -> 7 | 16:14:33.046 -> BEGIN: 1 8 | 16:14:33.046 -> VERS: 117 9 | 16:14:33.093 -> 10 | 16:14:33.093 -> Warming up (120 seconds = 24 dots) 11 | 16:14:38.107 -> ........................ 12 | 16:16:33.243 -> MODE: 1 0 13 | 16:16:34.790 -> 32 549 10 0 14 | 16:16:36.325 -> 32 547 10 0 15 | 16:16:37.872 -> 31 545 10 0 16 | 16:16:39.418 -> 32 545 10 0 17 | 16:16:40.922 -> 33 544 10 0 18 | 16:16:42.469 -> 33 543 10 0 19 | 16:16:44.015 -> 33 542 10 0 20 | 16:16:45.562 -> 33 543 10 0 21 | 16:16:47.062 -> 33 536 10 0 22 | 16:16:48.608 -> 33 541 10 0 23 | 16:16:50.155 -> 33 537 10 0 24 | 16:16:51.701 -> 32 537 10 0 25 | 16:16:53.201 -> 33 536 10 0 26 | 16:16:54.747 -> 33 535 10 0 27 | 16:16:56.294 -> 33 534 10 0 28 | 16:16:57.810 -> 32 533 10 0 29 | 16:16:59.357 -> 32 531 10 0 30 | -------------------------------------------------------------------------------- /examples/AGS02MA_PPB_TIMING/performance_0.3.1_10khz.txt: -------------------------------------------------------------------------------- 1 | 2 | 10 KHz (TWBR 198, TWSR 1) just a test 3 | 4 | 16:08:08.836 -> ...\AGS02MA_PPB_TIMING.ino 5 | 16:20:01.824 -> AGS02MA_LIB_VERSION: 0.3.1 6 | 16:20:01.824 -> 7 | 16:20:01.824 -> BEGIN: 1 8 | 16:20:01.824 -> VERS: 117 9 | 16:20:01.871 -> 10 | 16:20:01.871 -> Warming up (120 seconds = 24 dots) 11 | 16:20:06.885 -> ........................ 12 | 16:22:02.077 -> MODE: 1 0 13 | 16:22:03.624 -> 38 476 10 0 14 | 16:22:05.165 -> 38 474 10 0 15 | 16:22:06.712 -> 38 471 10 0 16 | 16:22:08.247 -> 38 474 10 0 17 | 16:22:09.794 -> 38 471 10 0 18 | 16:22:11.325 -> 38 472 10 0 19 | 16:22:12.871 -> 38 468 10 0 20 | 16:22:14.418 -> 37 465 10 0 21 | 16:22:15.917 -> 38 463 10 0 22 | 16:22:17.464 -> 38 463 10 0 23 | 16:22:19.016 -> 38 459 10 0 24 | 16:22:20.547 -> 37 460 10 0 25 | 16:22:22.094 -> 36 459 10 0 26 | 16:22:23.640 -> 38 461 10 0 27 | 28 | -------------------------------------------------------------------------------- /examples/AGS02MA_PPB_TIMING/performance_0.3.1_25khz.txt: -------------------------------------------------------------------------------- 1 | 2 | 25 KHz 3 | 4 | 16:08:08.836 -> ...\AGS02MA_PPB_TIMING.ino 5 | 16:08:08.836 -> AGS02MA_LIB_VERSION: 0.3.1 6 | 16:08:08.836 -> 7 | 16:08:08.836 -> BEGIN: 1 8 | 16:08:08.836 -> VERS: 117 9 | 16:08:08.929 -> 10 | 16:08:08.929 -> Warming up (120 seconds = 24 dots) 11 | 16:08:13.944 -> ........................ 12 | 16:10:09.086 -> MODE: 1 0 13 | 16:10:10.633 -> 33 845 10 0 14 | 16:10:12.179 -> 32 846 10 0 15 | 16:10:13.726 -> 32 847 10 0 16 | 16:10:15.225 -> 33 847 10 0 17 | 16:10:16.772 -> 32 847 10 0 18 | 16:10:18.318 -> 33 841 10 0 19 | 16:10:19.849 -> 33 835 10 0 20 | 16:10:21.396 -> 34 829 10 0 21 | 16:10:22.895 -> 33 825 10 0 22 | 16:10:24.442 -> 33 828 10 0 23 | 16:10:25.988 -> 33 823 10 0 24 | 16:10:27.535 -> 34 824 10 0 25 | 16:10:29.081 -> 33 821 10 0 26 | 16:10:30.581 -> 33 821 10 0 27 | 16:10:32.127 -> 33 816 10 0 28 | 16:10:33.674 -> 34 826 10 0 29 | 16:10:35.221 -> 33 839 10 0 30 | 16:10:36.720 -> 33 855 10 0 31 | 16:10:38.267 -> 33 857 10 0 32 | 16:10:39.813 -> 33 857 10 0 33 | -------------------------------------------------------------------------------- /examples/AGS02MA_PPM/AGS02MA_PPM.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_PPM.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | // 7 | 8 | #include "AGS02MA.h" 9 | 10 | 11 | AGS02MA AGS(26); 12 | 13 | 14 | void setup() 15 | { 16 | // ESP devices typically miss the first serial log lines after flashing. 17 | // Delay somewhat to include all output. 18 | delay(1000); 19 | 20 | Serial.begin(115200); 21 | Serial.println(); 22 | Serial.println(__FILE__); 23 | Serial.print("AGS02MA_LIB_VERSION: "); 24 | Serial.println(AGS02MA_LIB_VERSION); 25 | Serial.println(); 26 | 27 | Wire.begin(); 28 | 29 | bool b = AGS.begin(); 30 | Serial.print("BEGIN:\t"); 31 | Serial.println(b); 32 | 33 | Serial.print("VERSION:\t"); 34 | Serial.println(AGS.getSensorVersion()); 35 | 36 | // pre-heating improves measurement quality 37 | // can be skipped 38 | Serial.println("\nWarming up (120 seconds = 24 dots)"); 39 | while (AGS.isHeated() == false) 40 | { 41 | delay(5000); 42 | Serial.print("."); 43 | } 44 | Serial.println(); 45 | 46 | b = AGS.setPPBMode(); 47 | uint8_t m = AGS.getMode(); 48 | Serial.print("MODE:\t"); 49 | Serial.print(b); 50 | Serial.print("\t"); 51 | Serial.println(m); 52 | } 53 | 54 | 55 | void loop() 56 | { 57 | delay(3000); 58 | Serial.print("PPM:\t"); 59 | Serial.print(AGS.readPPM(), 3); 60 | Serial.print("\t"); 61 | Serial.print(AGS.dataReady(), HEX); 62 | Serial.print("\t"); 63 | Serial.print(AGS.lastStatus(), HEX); 64 | Serial.print("\t"); 65 | Serial.print(AGS.lastError(), HEX); 66 | Serial.println(); 67 | } 68 | 69 | 70 | // -- END OF FILE -- 71 | 72 | -------------------------------------------------------------------------------- /examples/AGS02MA_UGM3/AGS02MA_UGM3.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_UGM3.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | 7 | 8 | #include "AGS02MA.h" 9 | 10 | 11 | AGS02MA AGS(26); 12 | 13 | 14 | void setup() 15 | { 16 | // ESP devices typically miss the first serial log lines after flashing. 17 | // Delay somewhat to include all output. 18 | delay(1000); 19 | 20 | Serial.begin(115200); 21 | Serial.println(); 22 | Serial.println(__FILE__); 23 | Serial.print("AGS02MA_LIB_VERSION: "); 24 | Serial.println(AGS02MA_LIB_VERSION); 25 | Serial.println(); 26 | 27 | Wire.begin(); 28 | 29 | bool b = AGS.begin(); 30 | Serial.print("BEGIN:\t"); 31 | Serial.println(b); 32 | 33 | // pre-heating improves measurement quality 34 | // can be skipped 35 | Serial.println("\nWarming up (120 seconds = 24 dots)"); 36 | while (AGS.isHeated() == false) 37 | { 38 | delay(5000); 39 | Serial.print("."); 40 | } 41 | Serial.println(); 42 | 43 | b = AGS.setUGM3Mode(); 44 | uint8_t m = AGS.getMode(); 45 | Serial.print("MODE:\t"); 46 | Serial.print(b); 47 | Serial.print("\t"); 48 | Serial.println(m); 49 | 50 | uint8_t version = AGS.getSensorVersion(); 51 | Serial.print("VERS:\t"); 52 | Serial.println(version); 53 | } 54 | 55 | 56 | void loop() 57 | { 58 | delay(3000); 59 | uint32_t value = AGS.readUGM3(); 60 | Serial.print("UGM3:\t"); 61 | Serial.print(value); 62 | Serial.print("\t"); 63 | Serial.print(AGS.lastStatus(), HEX); 64 | Serial.print("\t"); 65 | Serial.print(AGS.lastError(), HEX); 66 | Serial.println(); 67 | 68 | } 69 | 70 | 71 | // -- END OF FILE -- 72 | -------------------------------------------------------------------------------- /examples/AGS02MA_calibrate/AGS02MA_calibrate.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_calibrate.ino 3 | // AUTHOR: Rob Tillaart, Beanow 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | 7 | 8 | #include "AGS02MA.h" 9 | 10 | // You can decrease/disable warmup when you're certain the chip already warmed up. 11 | #define WARMUP_MINUTES 6 12 | #define READ_INTERVAL 3000 13 | 14 | 15 | uint32_t start, stop; 16 | uint8_t version; 17 | 18 | AGS02MA AGS(26); 19 | 20 | 21 | void setup() 22 | { 23 | // ESP devices typically miss the first serial log lines after flashing. 24 | // Delay somewhat to include all output. 25 | delay(1000); 26 | 27 | Serial.begin(115200); 28 | Serial.println(); 29 | Serial.println(__FILE__); 30 | Serial.print("AGS02MA_LIB_VERSION: "); 31 | Serial.println(AGS02MA_LIB_VERSION); 32 | Serial.println(); 33 | 34 | Serial.print("WARMUP:\t\t"); 35 | Serial.println(WARMUP_MINUTES); 36 | Serial.print("INTERVAL:\t"); 37 | Serial.println(READ_INTERVAL); 38 | 39 | Wire.begin(); 40 | 41 | bool b = AGS.begin(); 42 | Serial.print("BEGIN:\t\t"); 43 | Serial.println(b); 44 | 45 | Serial.print("VERSION:\t"); 46 | version = AGS.getSensorVersion(); 47 | Serial.println(version); 48 | int err = AGS.lastError(); 49 | 50 | // Reading version correctly matters, as we display additional comments based on it. 51 | if(err != AGS02MA_OK) 52 | { 53 | Serial.print("Error reading version:\t"); 54 | Serial.println(err); 55 | Serial.println("Won't attempt to calibrate. Reset when connection with the sensor is stable."); 56 | Serial.println(); 57 | return; 58 | } 59 | 60 | b = AGS.setPPBMode(); 61 | uint8_t m = AGS.getMode(); 62 | Serial.print("MODE:\t\t"); 63 | Serial.print(b); 64 | Serial.print("\t"); 65 | Serial.println(m); 66 | 67 | Serial.println(); 68 | Serial.print("Place the device outside in open air for "); 69 | Serial.print(WARMUP_MINUTES); 70 | Serial.println(" minute(s)."); 71 | Serial.println("Make sure your device has warmed up sufficiently for the best results."); 72 | Serial.println("The PPB values should be stable (may include noise) not constantly decreasing."); 73 | Serial.println(); 74 | 75 | start = millis(); 76 | stop = WARMUP_MINUTES * 60000UL; 77 | while(millis() - start < stop) 78 | { 79 | Serial.print("[PRE ]\t"); 80 | printPPB(); 81 | delay(READ_INTERVAL); 82 | } 83 | 84 | Serial.println(); 85 | Serial.println("About to perform calibration now."); 86 | 87 | AGS02MA::ZeroCalibrationData initialValue; 88 | if (!AGS.getZeroCalibrationData(initialValue)) 89 | { 90 | Serial.print("Error reading zero calibration data:\t"); 91 | Serial.println(AGS.lastError()); 92 | Serial.println("Won't attempt to calibrate. Reset when connection with the sensor is stable."); 93 | Serial.println(); 94 | return; 95 | } 96 | 97 | Serial.println("Your previous calibration data was:"); 98 | printZeroCalibrationData(initialValue); 99 | 100 | delay(1000); 101 | 102 | // returns 1 if successful written 103 | b = AGS.zeroCalibration(); 104 | Serial.println(); 105 | Serial.print("CALIB:\t"); 106 | Serial.println(b); 107 | Serial.println(); 108 | Serial.println("Calibration done."); 109 | 110 | AGS02MA::ZeroCalibrationData zc; 111 | while (!AGS.getZeroCalibrationData(zc)) 112 | { 113 | Serial.print("Error:\t"); 114 | Serial.print(AGS.lastError()); 115 | Serial.println("\tretrying..."); 116 | delay(READ_INTERVAL); 117 | } 118 | 119 | Serial.println("Your new calibration data is:"); 120 | printZeroCalibrationData(zc); 121 | 122 | Serial.println(); 123 | Serial.println("Showing what PPB values look like post calibration."); 124 | // A 125 status is typically shown on v118's after they've been powered off. 125 | // Either having this version at all, or seeing this status, we'll display a notice. 126 | if (version == 118 || initialValue.status == 125) 127 | { 128 | Serial.println("NOTICE: v118 sensors are known to give different results after powering off!"); 129 | Serial.println("You may need to manually set your calibration value every time power was lost."); 130 | } 131 | Serial.println(); 132 | } 133 | 134 | 135 | void loop() 136 | { 137 | Serial.print("[POST]\t"); 138 | printPPB(); 139 | delay(READ_INTERVAL); 140 | } 141 | 142 | 143 | void printZeroCalibrationData(AGS02MA::ZeroCalibrationData &zc) { 144 | Serial.print("Status:\t"); 145 | Serial.println(zc.status); 146 | Serial.print("Value:\t"); 147 | Serial.println(zc.value); 148 | } 149 | 150 | 151 | void printPPB() 152 | { 153 | uint32_t value = AGS.readPPB(); 154 | Serial.print("PPB:\t"); 155 | Serial.print(value); 156 | Serial.print("\t"); 157 | Serial.print(AGS.lastStatus(), HEX); 158 | Serial.print("\t"); 159 | Serial.print(AGS.lastError(), HEX); 160 | Serial.println(); 161 | } 162 | 163 | 164 | // -- END OF FILE -- 165 | -------------------------------------------------------------------------------- /examples/AGS02MA_calibrate_manual/AGS02MA_calibrate_manual.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_calibrate_manual.ino 3 | // AUTHOR: Rob Tillaart, Beanow 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | 7 | 8 | #include "AGS02MA.h" 9 | 10 | // The zero calibration value we'll (temporarily) set in the example. 11 | #define ZC_VALUE 700 12 | 13 | #define READS 10 14 | #define INTERVAL 3000 15 | 16 | 17 | AGS02MA AGS(26); 18 | 19 | AGS02MA::ZeroCalibrationData initialValue; 20 | uint8_t version; 21 | 22 | 23 | void setup() 24 | { 25 | // ESP devices typically miss the first serial log lines after flashing. 26 | // Delay somewhat to include all output. 27 | delay(1000); 28 | 29 | Serial.begin(115200); 30 | Serial.println(); 31 | Serial.println(__FILE__); 32 | Serial.print("AGS02MA_LIB_VERSION: "); 33 | Serial.println(AGS02MA_LIB_VERSION); 34 | Serial.println(); 35 | 36 | Serial.print("READS:\t\t"); 37 | Serial.println(READS); 38 | Serial.print("INTERVAL:\t"); 39 | Serial.println(INTERVAL); 40 | 41 | Wire.begin(); 42 | 43 | bool b = AGS.begin(); 44 | Serial.print("BEGIN:\t\t"); 45 | Serial.println(b); 46 | 47 | Serial.print("VERSION:\t"); 48 | version = AGS.getSensorVersion(); 49 | Serial.println(version); 50 | int err = AGS.lastError(); 51 | 52 | // Reading version correctly matters, as we display additional comments based on it. 53 | if(err != AGS02MA_OK) 54 | { 55 | Serial.print("Error reading version:\t"); 56 | Serial.println(err); 57 | Serial.println("Won't attempt to calibrate. Reset when connection with the sensor is stable."); 58 | Serial.println(); 59 | return; 60 | } 61 | 62 | if (version != 118) 63 | { 64 | Serial.println(); 65 | Serial.println("Only v118 sensors support manual zero calibration. For other versions, you can use the 'AGS02MA_calibrate' example instead."); 66 | } 67 | else 68 | { 69 | b = AGS.setPPBMode(); 70 | uint8_t m = AGS.getMode(); 71 | Serial.print("MODE:\t\t"); 72 | Serial.print(b); 73 | Serial.print("\t"); 74 | Serial.println(m); 75 | 76 | while (!AGS.getZeroCalibrationData(initialValue)) 77 | { 78 | onError(AGS.lastError()); 79 | } 80 | 81 | Serial.println(); 82 | Serial.println("Your initial zero calibration is:"); 83 | printZeroCalibrationData(initialValue); 84 | Serial.println(); 85 | 86 | Serial.println("Showing sample data before changing."); 87 | for (size_t i = 0; i < READS; i++) 88 | { 89 | delay(INTERVAL); 90 | printPPB(); 91 | } 92 | 93 | Serial.println(); 94 | Serial.println("Manually setting zero calibration:"); 95 | b = AGS.manualZeroCalibration(ZC_VALUE); 96 | Serial.print("CALIB:\t"); 97 | Serial.println(b); 98 | 99 | AGS02MA::ZeroCalibrationData newValue; 100 | while (!AGS.getZeroCalibrationData(newValue)) 101 | { 102 | onError(AGS.lastError()); 103 | } 104 | 105 | printZeroCalibrationData(newValue); 106 | Serial.println(); 107 | 108 | Serial.println("Showing sample data."); 109 | Serial.println("NOTICE: v118 sensors are known to give different results after powering off!"); 110 | Serial.println("You may need to manually set your calibration value every time power was lost."); 111 | for (size_t i = 0; i < READS; i++) 112 | { 113 | delay(INTERVAL); 114 | printPPB(); 115 | } 116 | 117 | Serial.println(); 118 | Serial.println("Restoring initial zero calibration:"); 119 | b = AGS.manualZeroCalibration(initialValue.value); 120 | Serial.print("CALIB:\t"); 121 | Serial.println(b); 122 | 123 | AGS02MA::ZeroCalibrationData restoredValue; 124 | while (!AGS.getZeroCalibrationData(restoredValue)) 125 | { 126 | onError(AGS.lastError()); 127 | } 128 | 129 | printZeroCalibrationData(restoredValue); 130 | Serial.println(); 131 | } 132 | 133 | } 134 | 135 | 136 | void loop() 137 | { 138 | delay(INTERVAL); 139 | printPPB(); 140 | } 141 | 142 | 143 | void onError(int err) { 144 | Serial.print("Error:\t"); 145 | Serial.print(err); 146 | Serial.println("\tretrying..."); 147 | delay(INTERVAL); 148 | } 149 | 150 | 151 | void printZeroCalibrationData(AGS02MA::ZeroCalibrationData &zc) { 152 | Serial.print("Status:\t"); 153 | Serial.println(zc.status); 154 | Serial.print("Value:\t"); 155 | Serial.println(zc.value); 156 | } 157 | 158 | 159 | void printPPB() 160 | { 161 | uint32_t value = AGS.readPPB(); 162 | Serial.print("PPB:\t"); 163 | Serial.print(value); 164 | Serial.print("\t"); 165 | Serial.print(AGS.lastStatus(), HEX); 166 | Serial.print("\t"); 167 | Serial.print(AGS.lastError(), HEX); 168 | Serial.println(); 169 | } 170 | 171 | 172 | // -- END OF FILE -- 173 | -------------------------------------------------------------------------------- /examples/AGS02MA_get_registers/.arduino-ci.yml: -------------------------------------------------------------------------------- 1 | platforms: 2 | rpipico: 3 | board: rp2040:rp2040:rpipico 4 | package: rp2040:rp2040 5 | gcc: 6 | features: 7 | defines: 8 | - ARDUINO_ARCH_RP2040 9 | warnings: 10 | flags: 11 | 12 | packages: 13 | rp2040:rp2040: 14 | url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 15 | 16 | compile: 17 | # Choosing to run compilation tests on 2 different Arduino platforms 18 | platforms: 19 | # - uno 20 | # - due 21 | # - zero 22 | # - leonardo 23 | # - m4 24 | # - esp32 25 | # - esp8266 26 | # - mega2560 27 | # - rpipico 28 | 29 | -------------------------------------------------------------------------------- /examples/AGS02MA_get_registers/AGS02MA_get_registers.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_get_registers.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: low level develop application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | // 7 | // PURPOSE: this is a debugging tool for developing / investigating. 8 | // Do not use it unless you are willing to crash your sensor. 9 | // 10 | // usage: make _readRegister(), _writeRegister() and _buffer public 11 | // 12 | // USE AT OWN RISK 13 | 14 | 15 | #include "AGS02MA.h" 16 | 17 | 18 | AGS02MA AGS(26); 19 | 20 | 21 | void setup() 22 | { 23 | Serial.begin(115200); 24 | Serial.println(); 25 | Serial.println(__FILE__); 26 | Serial.print("AGS02MA_LIB_VERSION: "); 27 | Serial.println(AGS02MA_LIB_VERSION); 28 | Serial.println(); 29 | 30 | Wire.begin(); 31 | 32 | bool b = AGS.begin(); 33 | Serial.print("BEGIN:\t"); 34 | Serial.println(b); 35 | 36 | uint8_t version = AGS.getSensorVersion(); 37 | Serial.print("VERS:\t"); 38 | Serial.println(version); 39 | 40 | // AGS._buffer[0] = 0; 41 | // AGS._buffer[1] = 13; 42 | // AGS._buffer[2] = 14; 43 | // AGS._buffer[3] = 90; 44 | // AGS._buffer[5] = 248; 45 | // int x = AGS._writeRegister(0x01); // does not work. 46 | // Serial.println(x); 47 | 48 | for (uint8_t reg = 0; reg < 9; reg++) 49 | { 50 | dumpRegister(reg); 51 | } 52 | dumpRegister(0x11); 53 | dumpRegister(0x20); 54 | dumpRegister(0x21); 55 | 56 | AGS.setPPBMode(); 57 | } 58 | 59 | 60 | void loop() 61 | { 62 | // dumpRegister(0); 63 | // uint32_t zero = dumpRegister(1); 64 | // uint32_t x = dumpRegister(0x20); // seems to be the raw value. 65 | // delay(100); 66 | // uint32_t y = AGS.readPPB(); 67 | // Serial.print(zero); 68 | // Serial.print("\t"); 69 | // Serial.print(x); 70 | // Serial.print("\t"); 71 | // Serial.print(y); 72 | // Serial.print("\t"); 73 | // Serial.print((1.0 * x) / y, 2); 74 | // Serial.print("\t"); 75 | // Serial.print((zero - x) / 350); 76 | // Serial.println(); 77 | // Serial.println(); 78 | // delay(2000); 79 | } 80 | 81 | 82 | uint32_t dumpRegister(uint8_t reg) 83 | { 84 | Serial.print("REG["); 85 | Serial.print(reg); 86 | Serial.print("]"); 87 | 88 | bool b = AGS._readRegister(reg); 89 | uint32_t value = 0; 90 | for (int i = 0; i < 4; i++) 91 | { 92 | Serial.print("\t"); 93 | Serial.print(AGS._buffer[i]); 94 | 95 | value *= 256; 96 | value += AGS._buffer[i]; 97 | } 98 | Serial.print("\t"); 99 | Serial.print(value); 100 | 101 | Serial.println(); 102 | delay(100); 103 | 104 | return value; 105 | } 106 | 107 | 108 | // -- END OF FILE -- 109 | -------------------------------------------------------------------------------- /examples/AGS02MA_minimal/AGS02MA_minimal.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_minimal.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | // 7 | // default register is 0x00 at start of the sensor 8 | // datasheet states one can get the value with minimal interaction. 9 | // note this sketch does not use the library! 10 | 11 | 12 | #include "Wire.h" 13 | 14 | 15 | uint8_t buffer[5]; 16 | 17 | 18 | void setup() 19 | { 20 | // ESP devices typically miss the first serial log lines after flashing. 21 | // Delay somewhat to include all output. 22 | delay(1000); 23 | 24 | Serial.begin(115200); 25 | Serial.println(); 26 | Serial.println(__FILE__); 27 | // Serial.print("AGS02MA_LIB_VERSION: "); 28 | // Serial.println(AGS02MA_LIB_VERSION); 29 | Serial.println(); 30 | 31 | Wire.begin(); 32 | Wire.setClock(30400); // lowest speed an UNO supports that works with sensor. 33 | } 34 | 35 | 36 | void loop() 37 | { 38 | delay(3000); 39 | Wire.requestFrom(26, 5); 40 | for ( int i = 0; i < 5; i++) 41 | { 42 | buffer[i] = Wire.read(); 43 | // Serial.print(buffer[i], HEX); // for debugging. 44 | // Serial.print('\t'); 45 | } 46 | Serial.println(); 47 | 48 | // CONVERT RAW DATA 49 | Serial.print("STAT:\t"); 50 | Serial.println(buffer[0]); 51 | Serial.print("PPB:\t"); 52 | Serial.println(buffer[1] * 65536UL + buffer[2] * 256 + buffer[3]); 53 | Serial.print("CRC:\t"); 54 | Serial.println(buffer[4]); 55 | Serial.println(); 56 | } 57 | 58 | 59 | // -- END OF FILE -- 60 | -------------------------------------------------------------------------------- /examples/AGS02MA_minimal_plotter/AGS02MA_minimal_plotter.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_minimal_plotter.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test application not using the library 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | // 7 | // default register is 0x00 at start of the sensor 8 | // datasheet states one can get the value with minimal interaction. 9 | // note this sketch does not use the library! 10 | 11 | 12 | #include "Wire.h" 13 | 14 | 15 | uint8_t buffer[5]; 16 | 17 | uint8_t cnt = 0; 18 | 19 | 20 | void setup() 21 | { 22 | // ESP devices typically miss the first serial log lines after flashing. 23 | // Delay somewhat to include all output. 24 | delay(1000); 25 | 26 | Serial.begin(115200); 27 | // Serial.println(); 28 | // Serial.println(__FILE__); 29 | // Serial.print("AGS02MA_LIB_VERSION: "); 30 | // Serial.println(AGS02MA_LIB_VERSION); 31 | // Serial.println(); 32 | 33 | Wire.begin(); 34 | Wire.setClock(30400); // lowest speed an UNO supports that works with sensor. 35 | } 36 | 37 | 38 | void loop() 39 | { 40 | Wire.requestFrom(26, 5); 41 | for ( int i = 0; i < 5; i++) 42 | { 43 | buffer[i] = Wire.read(); 44 | // Serial.print(buffer[i], HEX); // for debugging. 45 | // Serial.print('\t'); 46 | } 47 | // Serial.println(); 48 | 49 | if (cnt == 0) 50 | { 51 | // CONVERT RAW DATA 52 | Serial.println("\nSTAT\tPPB\tCRC"); 53 | cnt = 20; 54 | } 55 | cnt--; 56 | if (buffer[0] == 0x10) 57 | { 58 | Serial.print(buffer[0]); 59 | Serial.print("\t"); 60 | Serial.print(buffer[1] * 65536UL + buffer[2] * 256 + buffer[3]); 61 | Serial.print("\t"); 62 | Serial.print(buffer[4]); 63 | Serial.println(); 64 | delay(2000); 65 | } 66 | } 67 | 68 | 69 | // -- END OF FILE -- 70 | -------------------------------------------------------------------------------- /examples/AGS02MA_readRegister/AGS02MA_readRegister.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_readRegister.ino 3 | // AUTHOR: Rob Tillaart, Beanow 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | 7 | 8 | #include "AGS02MA.h" 9 | 10 | 11 | const uint8_t addresses[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 0x11, 0x20, 0x21}; 12 | 13 | 14 | AGS02MA::RegisterData reg; 15 | AGS02MA AGS(26); 16 | 17 | 18 | void setup() 19 | { 20 | // ESP devices typically miss the first serial log lines after flashing. 21 | // Delay somewhat to include all output. 22 | delay(1000); 23 | 24 | Serial.begin(115200); 25 | Serial.println(); 26 | Serial.println(__FILE__); 27 | Serial.print("AGS02MA_LIB_VERSION: "); 28 | Serial.println(AGS02MA_LIB_VERSION); 29 | Serial.println(); 30 | 31 | Wire.begin(); 32 | 33 | bool b = AGS.begin(); 34 | Serial.print("BEGIN:\t"); 35 | Serial.println(b); 36 | } 37 | 38 | 39 | void loop() 40 | { 41 | delay(3000); 42 | for (auto address : addresses) 43 | { 44 | bool b = AGS.readRegister(address, reg); 45 | Serial.print("REG[0x"); 46 | Serial.print(address, HEX); 47 | Serial.print("]"); 48 | 49 | if(b) 50 | { 51 | printRegister(address, reg); 52 | } 53 | else 54 | { 55 | Serial.print("\tError:\t"); 56 | Serial.println(AGS.lastError()); 57 | } 58 | delay(50); 59 | } 60 | Serial.println(); 61 | } 62 | 63 | 64 | void printRegister(uint8_t address, AGS02MA::RegisterData ®) { 65 | // Raw bytes first for any register. 66 | for (auto b : reg.data) 67 | { 68 | Serial.print("\t"); 69 | Serial.print(b); 70 | } 71 | 72 | Serial.print("\tCRC: "); 73 | Serial.print(reg.crcValid ? "OK " : "ERR "); 74 | Serial.print(reg.crc); 75 | 76 | // Specific interpretations 77 | switch (address) 78 | { 79 | case 0x00: 80 | Serial.print("\tSensor data:\t"); 81 | Serial.print(reg.data[0]); 82 | Serial.print("\t"); 83 | Serial.print( 84 | (reg.data[1] << 16) + 85 | (reg.data[2] << 8) + 86 | reg.data[3] 87 | ); 88 | break; 89 | 90 | case 0x01: 91 | case 0x02: 92 | case 0x03: 93 | case 0x04: 94 | Serial.print("\tCalibration:\t"); 95 | Serial.print( 96 | (reg.data[0] << 8) + 97 | reg.data[1] 98 | ); 99 | Serial.print("\t"); 100 | Serial.print( 101 | (reg.data[2] << 8) + 102 | reg.data[3] 103 | ); 104 | break; 105 | 106 | case 0x11: 107 | Serial.print("\tVersion:\t"); 108 | Serial.print(reg.data[3]); 109 | break; 110 | 111 | case 0x21: 112 | Serial.print("\tI2C address:\t0x"); 113 | Serial.print(reg.data[0], HEX); 114 | break; 115 | 116 | default: 117 | break; 118 | } 119 | Serial.println(); 120 | } 121 | 122 | 123 | // -- END OF FILE -- 124 | -------------------------------------------------------------------------------- /examples/AGS02MA_setAddress/AGS02MA_setAddress.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_setAddress.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | 7 | 8 | #include "AGS02MA.h" 9 | 10 | 11 | AGS02MA AGS(26); 12 | 13 | 14 | void setup() 15 | { 16 | // ESP devices typically miss the first serial log lines after flashing. 17 | // Delay somewhat to include all output. 18 | delay(1000); 19 | 20 | Serial.begin(115200); 21 | Serial.println(); 22 | Serial.println(__FILE__); 23 | Serial.print("AGS02MA_LIB_VERSION: "); 24 | Serial.println(AGS02MA_LIB_VERSION); 25 | Serial.println(); 26 | 27 | Wire.begin(); 28 | 29 | bool b = AGS.begin(); 30 | Serial.print("BEGIN:\t"); 31 | Serial.println(b); 32 | 33 | b = AGS.setAddress(42); 34 | Serial.print("SET_ADDR:\t"); 35 | Serial.print(b, HEX); 36 | Serial.print("\t"); 37 | Serial.print(AGS.lastStatus(), HEX); 38 | Serial.print("\t"); 39 | Serial.print(AGS.lastError(), HEX); 40 | Serial.println(); 41 | 42 | uint8_t addr = AGS.getAddress(); 43 | Serial.print("GET_ADDR:\t"); 44 | Serial.print(addr, HEX); 45 | Serial.print("\t"); 46 | Serial.print(AGS.lastStatus(), HEX); 47 | Serial.print("\t"); 48 | Serial.print(AGS.lastError(), HEX); 49 | Serial.println(); 50 | 51 | Serial.println("\ndone..."); 52 | } 53 | 54 | 55 | void loop() 56 | { 57 | } 58 | 59 | 60 | // -- END OF FILE -- 61 | -------------------------------------------------------------------------------- /examples/AGS02MA_test/AGS02MA_test.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_test.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | 7 | 8 | #include "AGS02MA.h" 9 | 10 | 11 | AGS02MA AGS(26); 12 | 13 | 14 | void setup() 15 | { 16 | // ESP devices typically miss the first serial log lines after flashing. 17 | // Delay somewhat to include all output. 18 | delay(1000); 19 | 20 | Serial.begin(115200); 21 | Serial.println(); 22 | Serial.println(__FILE__); 23 | Serial.print("AGS02MA_LIB_VERSION: "); 24 | Serial.println(AGS02MA_LIB_VERSION); 25 | Serial.println(); 26 | 27 | Wire.begin(); 28 | 29 | bool b = AGS.begin(); 30 | Serial.print("BEGIN:\t"); 31 | Serial.println(b); 32 | 33 | b = AGS.setPPBMode(); 34 | uint8_t m = AGS.getMode(); 35 | Serial.print("MODE:\t"); 36 | Serial.print(b); 37 | Serial.print("\t"); 38 | Serial.println(m); 39 | 40 | uint8_t version = AGS.getSensorVersion(); 41 | Serial.print("VERS:\t"); 42 | Serial.println(version); 43 | } 44 | 45 | 46 | void loop() 47 | { 48 | delay(2000); 49 | uint32_t value = AGS.readPPB(); 50 | Serial.print("PPB:\t"); 51 | Serial.println(value); 52 | } 53 | 54 | 55 | // -- END OF FILE -- 56 | -------------------------------------------------------------------------------- /examples/AGS02MA_test_CRC8/.arduino-ci.yml: -------------------------------------------------------------------------------- 1 | compile: 2 | # Choosing to run compilation tests on 2 different Arduino platforms 3 | platforms: 4 | # - uno 5 | # - leonardo 6 | # - due 7 | # - zero 8 | -------------------------------------------------------------------------------- /examples/AGS02MA_test_CRC8/AGS02MA_test_CRC8.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_test_CRC8.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | // 7 | // NOTE: this is a low level test for the communication / CRC 8 | // to have this example to work, 9 | // one need to make the _CRC8() and _buffer[] 10 | // public in the AGS02MA.h file. 11 | 12 | 13 | #include "AGS02MA.h" 14 | 15 | 16 | AGS02MA AGS(26); 17 | 18 | 19 | void setup() 20 | { 21 | // ESP devices typically miss the first serial log lines after flashing. 22 | // Delay somewhat to include all output. 23 | delay(1000); 24 | 25 | Serial.begin(115200); 26 | Serial.println(); 27 | Serial.println(__FILE__); 28 | Serial.print("AGS02MA_LIB_VERSION: "); 29 | Serial.println(AGS02MA_LIB_VERSION); 30 | Serial.println(); 31 | 32 | Wire.begin(); 33 | 34 | bool b = AGS.begin(); 35 | Serial.print("BEGIN:\t"); 36 | Serial.println(b); 37 | 38 | uint8_t version = AGS.getSensorVersion(); 39 | Serial.print("VERS:\t"); 40 | Serial.println(version); 41 | dump("getSensorVersion"); 42 | } 43 | 44 | 45 | void loop() 46 | { 47 | delay(3000); 48 | AGS.setPPBMode(); 49 | dump("MODE0"); 50 | AGS.readPPB(); 51 | dump("PPB"); 52 | 53 | delay(3000); 54 | AGS.setUGM3Mode(); 55 | dump("MODE1"); 56 | AGS.readUGM3(); 57 | dump("UGM3"); 58 | } 59 | 60 | 61 | void dump(char * str) 62 | { 63 | Serial.print(str); 64 | for (int i = 0; i < 5; i++) 65 | { 66 | Serial.print("\t"); 67 | Serial.print(AGS._buffer[i], HEX); 68 | } 69 | Serial.print('\t'); 70 | Serial.print(AGS._CRC8(AGS._buffer, 4), HEX); 71 | Serial.println(); 72 | } 73 | 74 | 75 | // -- END OF FILE -- 76 | 77 | -------------------------------------------------------------------------------- /examples/issue/issue.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: AGS02MA_PPB.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | 7 | 8 | #include "AGS02MA.h" 9 | 10 | AGS02MA AGS(26); 11 | 12 | uint32_t rounds = 0; 13 | 14 | 15 | void setup() 16 | { 17 | // ESP devices typically miss the first serial log lines after flashing. 18 | // Delay somewhat to include all output. 19 | delay(1000); 20 | 21 | Serial.begin(115200); 22 | Serial.println(); 23 | Serial.println(__FILE__); 24 | Serial.print("AGS02MA_LIB_VERSION: "); 25 | Serial.println(AGS02MA_LIB_VERSION); 26 | Serial.println(); 27 | 28 | Wire.begin(); 29 | 30 | bool b = AGS.begin(); 31 | Serial.print("BEGIN:\t"); 32 | Serial.println(b); 33 | 34 | Serial.print("VERSION:\t"); 35 | Serial.println(AGS.getSensorVersion()); 36 | 37 | Serial.print("DATE:\t"); 38 | Serial.println(AGS.getSensorDate(), HEX); 39 | 40 | // pre-heating improves measurement quality 41 | // can be skipped 42 | // Serial.println("\nWarming up (120 seconds = 24 dots)"); 43 | // while (AGS.isHeated() == false) 44 | // { 45 | // delay(5000); 46 | // Serial.print("."); 47 | // } 48 | // Serial.println(); 49 | 50 | uint8_t version = AGS.getSensorVersion(); 51 | Serial.print("VERS:\t"); 52 | Serial.println(version); 53 | } 54 | 55 | 56 | void loop() 57 | { 58 | delay(3000); 59 | 60 | uint8_t kind = rounds % 20; 61 | 62 | // Switch mode every 10 and 20 rounds. 63 | bool b; 64 | if (kind == 0) { 65 | b = AGS.setPPBMode(); 66 | uint8_t m = AGS.getMode(); 67 | Serial.print("MODE:\t"); 68 | Serial.print(b); 69 | Serial.print("\t"); 70 | Serial.println(m); 71 | } else if (kind == 10) { 72 | b = AGS.setUGM3Mode(); 73 | uint8_t m = AGS.getMode(); 74 | Serial.print("MODE:\t"); 75 | Serial.print(b); 76 | Serial.print("\t"); 77 | Serial.println(m); 78 | } 79 | 80 | // Read PPB in first half of a 20-round cycle. 81 | if (kind < 10) { 82 | uint32_t value = AGS.readPPB(); 83 | Serial.print("PPB:\t"); 84 | Serial.print(value); 85 | Serial.print("\t"); 86 | Serial.print(AGS.lastStatus(), HEX); 87 | Serial.print("\t"); 88 | Serial.print(AGS.lastError(), HEX); 89 | Serial.println(); 90 | } else { 91 | uint32_t value = AGS.readUGM3(); 92 | Serial.print("UGM3:\t"); 93 | Serial.print(value); 94 | Serial.print("\t"); 95 | Serial.print(AGS.lastStatus(), HEX); 96 | Serial.print("\t"); 97 | Serial.print(AGS.lastError(), HEX); 98 | Serial.println(); 99 | } 100 | 101 | rounds++; 102 | } 103 | 104 | 105 | // -- END OF FILE -- 106 | -------------------------------------------------------------------------------- /examples/test_CRC8/.arduino-ci.yml: -------------------------------------------------------------------------------- 1 | compile: 2 | # Choosing to run compilation tests on 2 different Arduino platforms 3 | platforms: 4 | # - uno 5 | # - leonardo 6 | # - due 7 | # - zero 8 | -------------------------------------------------------------------------------- /examples/test_CRC8/test_CRC8.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: test_CRC8.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test application 5 | // URL: https://github.com/RobTillaart/AGS02MA 6 | // 7 | // just for develop scratch pad 8 | // need to make the CRC function public in the library 9 | 10 | 11 | #include "AGS02MA.h" 12 | 13 | 14 | AGS02MA AGS(26); 15 | 16 | 17 | void setup() 18 | { 19 | // ESP devices typically miss the first serial log lines after flashing. 20 | // Delay somewhat to include all output. 21 | delay(1000); 22 | 23 | Serial.begin(115200); 24 | Serial.println(); 25 | Serial.println(__FILE__); 26 | Serial.print("AGS02MA_LIB_VERSION: "); 27 | Serial.println(AGS02MA_LIB_VERSION); 28 | Serial.println(); 29 | 30 | Wire.begin(); 31 | 32 | bool b = AGS.begin(); 33 | Serial.print("BEGIN:\t"); 34 | Serial.println(b); 35 | 36 | uint8_t _buffer[8]; 37 | 38 | _buffer[0] = 0x00; 39 | _buffer[1] = 0x0C; 40 | _buffer[2] = 0xFF; 41 | _buffer[3] = 0xF3; 42 | _buffer[4] = 0xFC; 43 | Serial.println(AGS._CRC8(_buffer, 5), HEX); 44 | 45 | 46 | _buffer[0] = 0x02; 47 | _buffer[1] = 0xFD; 48 | _buffer[2] = 0x02; 49 | _buffer[3] = 0xFD; 50 | _buffer[4] = 0x00; 51 | Serial.println(AGS._CRC8(_buffer, 5), HEX); 52 | 53 | _buffer[0] = 0x00; 54 | _buffer[1] = 0xFF; 55 | _buffer[2] = 0x00; 56 | _buffer[3] = 0xFF; 57 | _buffer[4] = 0x30; 58 | Serial.println(AGS._CRC8(_buffer, 5), HEX); 59 | 60 | _buffer[0] = 0x14; 61 | _buffer[1] = 0x14; 62 | _buffer[2] = 0x14; 63 | _buffer[3] = 0x1C; 64 | _buffer[4] = 0x75; 65 | Serial.println(AGS._CRC8(_buffer, 5), HEX); 66 | 67 | _buffer[0] = 0x0; 68 | _buffer[1] = 0x0; 69 | _buffer[2] = 0xBB; 70 | Serial.println( _buffer[0] * 65536UL + _buffer[1] * 256 + _buffer[2]); 71 | _buffer[0] = 0x0; 72 | _buffer[1] = 0x01; 73 | _buffer[2] = 0xAE; 74 | Serial.println( _buffer[0] * 65536UL + _buffer[1] * 256 + _buffer[2]); 75 | } 76 | 77 | 78 | void loop() 79 | { 80 | } 81 | 82 | 83 | // -- END OF FILE -- 84 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | # Syntax Colouring Map for AGS02MA 2 | 3 | 4 | # Data types (KEYWORD1) 5 | AGS02MA KEYWORD1 6 | 7 | 8 | # Methods and Functions (KEYWORD2) 9 | begin KEYWORD2 10 | isConnected KEYWORD2 11 | reset KEYWORD2 12 | 13 | isHeated KEYWORD2 14 | 15 | setAddress KEYWORD2 16 | getAddress KEYWORD2 17 | 18 | getSensorVersion KEYWORD2 19 | getSensorDate KEYWORD2 20 | 21 | setI2CResetSpeed KEYWORD2 22 | getI2CResetSpeed KEYWORD2 23 | 24 | zeroCalibration KEYWORD2 25 | manualZeroCalibration KEYWORD2 26 | getZeroCalibrationData KEYWORD2 27 | 28 | setPPBMode KEYWORD2 29 | setUGM3Mode KEYWORD2 30 | getMode KEYWORD2 31 | 32 | readPPB KEYWORD2 33 | readUGM3 KEYWORD2 34 | readPPM KEYWORD2 35 | readMGM3 KEYWORD2 36 | readUGF3 KEYWORD2 37 | 38 | lastPPM KEYWORD2 39 | lastPPB KEYWORD2 40 | lastUGM3 KEYWORD2 41 | 42 | lastRead KEYWORD2 43 | lastError KEYWORD2 44 | lastStatus KEYWORD2 45 | dataReady KEYWORD2 46 | 47 | readRegister KEYWORD2 48 | 49 | 50 | # Constants ( LITERAL1) 51 | AGS02MA_LIB_VERSION LITERAL1 52 | 53 | AGS02MA_OK LITERAL1 54 | AGS02MA_ERROR LITERAL1 55 | AGS02MA_ERROR_CRC LITERAL1 56 | AGS02MA_ERROR_READ LITERAL1 57 | AGS02MA_I2C_CLOCK LITERAL1 58 | AGS02MA_ERROR_REQUEST LITERAL1 59 | 60 | 61 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AGS02MA", 3 | "keywords": "I2C, AGS02MA, tvoc", 4 | "description": "Arduino library for AGS02MA - TVOC sensor.", 5 | "authors": 6 | [ 7 | { 8 | "name": "Viktor Balint" 9 | }, 10 | { 11 | "name": "Rob Tillaart", 12 | "email": "Rob.Tillaart@gmail.com", 13 | "maintainer": true 14 | }, 15 | { 16 | "name": "Beanow" 17 | } 18 | ], 19 | "repository": 20 | { 21 | "type": "git", 22 | "url": "https://github.com/RobTillaart/AGS02MA.git" 23 | }, 24 | "version": "0.4.3", 25 | "license": "MIT", 26 | "frameworks": "*", 27 | "platforms": "*", 28 | "headers": "AGS02MA.h" 29 | } 30 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=AGS02MA 2 | version=0.4.3 3 | author=Rob Tillaart 4 | maintainer=Rob Tillaart 5 | sentence=Arduino library for AGS02MA - TVOC sensor 6 | paragraph=Note it uses slow I2C < 30KHz. See readme.md 7 | category=Sensors 8 | url=https://github.com/RobTillaart/AGS02MA.git 9 | architectures=* 10 | includes=AGS02MA.h 11 | depends= 12 | -------------------------------------------------------------------------------- /test/unit_test_001.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: unit_test_001.cpp 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2021-08-12 5 | // PURPOSE: unit tests for the AGS02MA TVOC sensor 6 | // https://github.com/RobTillaart/AGS02MA 7 | // https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md 8 | // 9 | 10 | // supported assertions 11 | // ---------------------------- 12 | // assertEqual(expected, actual) 13 | // assertNotEqual(expected, actual) 14 | // assertLess(expected, actual) 15 | // assertMore(expected, actual) 16 | // assertLessOrEqual(expected, actual) 17 | // assertMoreOrEqual(expected, actual) 18 | // assertTrue(actual) 19 | // assertFalse(actual) 20 | // assertNull(actual) 21 | 22 | #include 23 | 24 | 25 | #include "Arduino.h" 26 | #include "AGS02MA.h" 27 | 28 | // writing to a virtual device does not work 29 | // as millis() function is not implemented in 30 | // the Arduino-CI environment 31 | 32 | 33 | unittest_setup() 34 | { 35 | fprintf(stderr, "AGS02MA_LIB_VERSION: %s\n", (char *) AGS02MA_LIB_VERSION); 36 | } 37 | 38 | 39 | unittest_teardown() 40 | { 41 | } 42 | 43 | 44 | unittest(test_constants) 45 | { 46 | assertEqual( 0, AGS02MA_OK); 47 | assertEqual(-10, AGS02MA_ERROR); 48 | assertEqual(-11, AGS02MA_ERROR_CRC); 49 | assertEqual(-12, AGS02MA_ERROR_READ); 50 | assertEqual(-13, AGS02MA_ERROR_NOT_READY); 51 | assertEqual(-14, AGS02MA_ERROR_REQUEST); 52 | 53 | assertEqual(25000, AGS02MA_I2C_CLOCK); 54 | } 55 | 56 | 57 | unittest(test_base) 58 | { 59 | AGS02MA AGS(26); 60 | Wire.begin(); 61 | 62 | assertTrue(AGS.begin()); 63 | assertTrue(AGS.isConnected()); // TODO - GODMODE 64 | 65 | assertFalse(AGS.isHeated()); 66 | assertEqual(0, AGS.lastRead()); 67 | 68 | assertEqual(26, AGS.getAddress()); 69 | //assertTrue(AGS.setAddress(42)); 70 | //assertEqual(42, AGS.getAddress()); 71 | 72 | assertEqual(100000, AGS.getI2CResetSpeed()); 73 | AGS.setI2CResetSpeed(400000); 74 | assertEqual(400000, AGS.getI2CResetSpeed()); 75 | 76 | assertEqual(0, AGS.lastError()); 77 | assertEqual(0, AGS.lastStatus()); 78 | } 79 | 80 | 81 | unittest(test_mode) 82 | { 83 | AGS02MA AGS(26); 84 | Wire.begin(); 85 | 86 | assertTrue(AGS.begin()); 87 | assertTrue(AGS.isConnected()); // TODO - GODMODE 88 | 89 | assertEqual(255, AGS.getMode()); 90 | 91 | // assertTrue(AGS.setPPBMode()); 92 | // assertEqual(0, AGS.getMode()); 93 | 94 | // assertTrue(AGS.setUGM3Mode()); 95 | // assertEqual(1, AGS.getMode()); 96 | } 97 | 98 | 99 | 100 | unittest_main() 101 | 102 | // -- END OF FILE -- 103 | --------------------------------------------------------------------------------