0.1.0 AGS02MA

This commit is contained in:
rob tillaart 2021-08-13 13:08:52 +02:00
parent 8909f428ff
commit d59f3f3c76
15 changed files with 820 additions and 0 deletions

View File

@ -0,0 +1,7 @@
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
- leonardo
- due
- zero

View File

@ -0,0 +1,13 @@
name: Arduino-lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: arduino/arduino-lint-action@v1
with:
library-manager: update
compliance: strict

View File

@ -0,0 +1,13 @@
---
name: Arduino CI
on: [push, pull_request]
jobs:
arduino_ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1

View File

@ -0,0 +1,18 @@
name: JSON check
on:
push:
paths:
- '**.json'
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: json-syntax-check
uses: limitusus/json-syntax-check@v1
with:
pattern: "\\.json$"

View File

@ -0,0 +1,252 @@
//
// FILE: AGS02MA.cpp
// AUTHOR: Rob Tillaart
// DATE: 2021-08-12
// VERSION: 0.1.0
// PURPOSE: Arduino library for AGS02MA TVOC
// URL: https://github.com/RobTillaart/AGS02MA
#include "AGS02MA.h"
// REGISTERS
#define AGS02MA_DATA 0x00
#define AGS02MA_CALIBRATION 0x01
#define AGS02MA_VERSION 0x11
#define AGS02MA_SLAVE_ADDRESS 0x21
AGS02MA::AGS02MA(const uint8_t deviceAddress, TwoWire *wire)
{
_address = deviceAddress;
_wire = wire;
_error = AGS02MA_OK;
}
#if defined (ESP8266) || defined(ESP32)
bool AGS02MA::begin(uint8_t dataPin, uint8_t clockPin)
{
_startTime = millis(); // PREHEAT
_wire = &Wire;
if ((dataPin < 255) && (clockPin < 255))
{
_wire->begin(dataPin, clockPin);
} else {
_wire->begin();
}
return isConnected();
}
#endif
bool AGS02MA::begin()
{
_startTime = millis(); // PREHEAT TIMING
_wire->begin();
return isConnected();
}
bool AGS02MA::isConnected()
{
#if defined (__AVR__)
TWBR = 255;
#else
_wire->setClock(AGS02MA_I2C_CLOCK);
#endif
_wire->beginTransmission(_address);
bool rv = ( _wire->endTransmission() == 0);
_wire->setClock(_I2CReseSpeed);
return rv;
}
bool AGS02MA::setAddress(const uint8_t deviceAddress)
{
if (deviceAddress < 10 or deviceAddress > 119) return false;
_buffer[2] = _buffer[0] = deviceAddress;
_buffer[3] = _buffer[1] = 0xFF ^ deviceAddress;
_buffer[4] = _CRC8(_buffer, 4);
if (_writeRegister(AGS02MA_SLAVE_ADDRESS))
{
_address = deviceAddress;
}
return isConnected();
}
uint8_t AGS02MA::getSensorVersion()
{
uint8_t version = 0xFF;
if (_readRegister(AGS02MA_VERSION))
{
version = _buffer[3];
if (_CRC8(_buffer, 5) != 0)
{
_error = AGS02MA_CRC_ERROR;
}
}
return version;
}
bool AGS02MA::setPPBMode()
{
_buffer[0] = 0x00;
_buffer[1] = 0xFF;
_buffer[2] = 0x00;
_buffer[3] = 0xFF;
_buffer[4] = 0x30;
if (_writeRegister(AGS02MA_DATA))
{
_mode = 0;
return true;
}
return false;
}
bool AGS02MA::setUGM3Mode()
{
_buffer[0] = 0x02;
_buffer[1] = 0xFD;
_buffer[2] = 0x02;
_buffer[3] = 0xFD;
_buffer[4] = 0x00;
if (_writeRegister(AGS02MA_DATA))
{
_mode = 1;
return true;
}
return false;
}
uint32_t AGS02MA::readPPB()
{
uint32_t val = 0xFFFFFFFF;
_lastRead = millis();
if (_readRegister(AGS02MA_DATA))
{
_status = _buffer[0];
val = _buffer[1] * 65536UL;
val += _buffer[2] * 256;
val += _buffer[3];
if (_CRC8(_buffer, 5) != 0)
{
_error = AGS02MA_CRC_ERROR;
}
}
return val;
}
uint32_t AGS02MA::readUGM3()
{
uint32_t val = 0xFFFFFFFF;
_lastRead = millis();
if (_readRegister(AGS02MA_DATA))
{
_status = _buffer[0];
val = _buffer[1] * 65536UL;
val += _buffer[2] * 256;
val += _buffer[3];
if (_CRC8(_buffer, 5) != 0)
{
_error = AGS02MA_CRC_ERROR;
}
}
return val;
}
bool AGS02MA::zeroCalibration()
{
_buffer[0] = 0x00;
_buffer[1] = 0x0C;
_buffer[2] = 0xFF;
_buffer[3] = 0xF3;
_buffer[4] = 0xFC;
return _writeRegister(AGS02MA_CALIBRATION);
}
int AGS02MA::lastError()
{
int e = _error;
_error = AGS02MA_OK; // reset error after read
return e;
}
/////////////////////////////////////////////////////////
//
// PRIVATE
//
bool AGS02MA::_readRegister(uint8_t reg)
{
#if defined (__AVR__)
TWBR = 255;
#else
_wire->setClock(AGS02MA_I2C_CLOCK);
#endif
_wire->beginTransmission(_address);
_wire->write(reg);
_error = _wire->endTransmission();
if (_wire->requestFrom(_address, (uint8_t)5) != 5)
{
_error = AGS02MA_ERROR;
_wire->setClock(_I2CReseSpeed);
return false;
}
for (int i = 0; i < 5; i++)
{
_buffer[i] = _wire->read();
}
_wire->setClock(_I2CReseSpeed);
return true;
}
bool AGS02MA::_writeRegister(uint8_t reg)
{
#if defined (__AVR__)
TWBR = 255;
#else
_wire->setClock(AGS02MA_I2C_CLOCK);
#endif
_wire->beginTransmission(_address);
_wire->write(reg);
for (int i = 0; i < 5; i++)
{
_wire->write(_buffer[i]);
}
_error = _wire->endTransmission();
_wire->setClock(_I2CReseSpeed);
return (_error == 0);
}
uint8_t AGS02MA::_CRC8(uint8_t * buf, uint8_t size)
{
uint8_t crc = 0xFF; // start value
for (uint8_t b = 0; b < size; b++)
{
crc ^= buf[b];
for (uint8_t i = 0; i < 8; i++)
{
if (crc & 0x80) crc = (crc << 1) ^ 0x31;
else crc = (crc << 1);
}
}
return crc;
}
// -- END OF FILE --

View File

@ -0,0 +1,82 @@
#pragma once
//
// FILE: AGS02MA.h
// AUTHOR: Rob Tillaart
// DATE: 2021-08-12
// VERSION: 0.1.0
// PURPOSE: Arduino library for AGS02MA TVOC
// URL: https://github.com/RobTillaart/AGS02MA
//
#include "Arduino.h"
#include "Wire.h"
#define AGS02MA_LIB_VERSION (F("0.1.0"))
#define AGS02MA_OK 0
#define AGS02MA_ERROR -10
#define AGS02MA_CRC_ERROR -11
#define AGS02MA_I2C_CLOCK 25000
#define AGS02MA_I2C_RESET 400000
class AGS02MA
{
public:
explicit AGS02MA(const uint8_t deviceAddress = 26, TwoWire *wire = &Wire);
#if defined (ESP8266) || defined(ESP32)
bool begin(uint8_t sda, uint8_t scl);
#endif
bool begin();
bool isConnected();
bool isHeated() { return (millis() - _startTime) > 120000UL; };
uint32_t lastRead() { return _lastRead; };
bool setAddress(const uint8_t deviceAddress);
uint8_t getAddress() { return _address; };
uint8_t getSensorVersion();
void setI2CResetSpeed(uint32_t s) { _I2CReseSpeed = s; };
uint32_t getI2CResetSpeed() { return _I2CReseSpeed; };
bool setPPBMode();
bool setUGM3Mode();
uint8_t getMode() { return _mode; };
uint32_t readPPB();
uint32_t readUGM3();
// to be called after at least 5 minutes in fresh air.
bool zeroCalibration();
int lastError();
uint8_t lastStatus() { return _status; };
private:
bool _readRegister(uint8_t reg);
bool _writeRegister(uint8_t reg);
uint8_t _CRC8(uint8_t * buf, uint8_t size);
uint32_t _I2CReseSpeed = 100000;
uint32_t _startTime = 0;
uint32_t _lastRead = 0;
uint8_t _address = 0;
uint8_t _mode = 255;
uint8_t _status = 0;
uint8_t _buffer[5];
int _error = AGS02MA_OK;
TwoWire* _wire;
};
// -- END OF FILE --

21
libraries/AGS02MA/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021-2021 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,85 @@
[![Arduino CI](https://github.com/RobTillaart/AGS02MA/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/AGS02MA/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/AGS02MA.svg?maxAge=3600)](https://github.com/RobTillaart/AGS02MA/releases)
# AGS02MA
Arduino library for AGS02MA TVOC sensor.
This library is experimental, so please use with care.
Please note the warning about the I2C speed.
## I2C - warning low speed
The sensor uses I2C at very low speed < 30KHz. For an Arduino UNO the lowest speed is about 30.4KHz (TWBR = 255) which works sometimes. During tests roughly 1 in 20 reads of the sensor was successful.
Tests with ESP32, which can go as low as ~5 KHZ are underway and expected to work.
The library sets the clock speed to 25KHz (for non AVR) during operation and resets it to 100 KHz
after operation. This is done to minimize interference with the communication with other devices.
## Interface
- **AGS02MA(uint8_t deviceAddress = 26, TwoWire \*wire = &Wire)** constructor, with default address and default I2C interface.
- **bool begin(uint8_t sda, uint8_t scl)** begin for ESP32 and ESP8266.
- **bool begin()** initializer for Arduino UNO a.o.
- **bool isConnected()** returns true if device address can be seen on I2C.
### Timing
- **bool isHeated()** returns true if 2 minutes have passed after startup.
Otherwise the device is not ready.
- **uint32_t lastRead()** last time the device is read, timestamp in milliseconds since start.
### Administration
- **bool setAddress(const uint8_t deviceAddress)** Not implemented yet.
- **uint8_t getAddress()** returns set address.
- **uint8_t getSensorVersion()** reads sensor version from device.
The library sets the clock speed to 25KHz (for non AVR) during operation and resets it to 100 KHz
after operation. This is done to minimize interference with the communication with other devices.
The following function can change this reset speed.
- **setI2CResetSpeed(uint32_t s)** sets the I2C speed the library need to reset the I2C speed to.
- **getI2CResetSpeed()** returns the set value above. Default is 100KHz.
### setMode
- **bool setPPBMode()** sets device in PartPerBillion mode. Returns true on success.
- **bool setUGM3Mode()** sets device in micro gram per cubic meter mode. Returns true on success.
- **uint8_t getMode()** returns mode set. 0 = PPB, 1 = UGm3, 255 = not set.
### Reading
WARNING: Take at least 2 seconds between reads.
- **uint32_t readPPB()** reads PPB from device. returns 0xFFFFFFFF if failed.
Check lastStatus() to get more info
- **uint32_t readUGM3()** reads current value from device.
### Other
- **bool zeroCalibration()** to be called after at least 5 minutes in fresh air.
See example sketch.
- **int lastError()** returns last error.
- **uint8_t lastStatus()** returns status byte from last read. Read datasheet.
## Future
- improve documentation
- test test test ...
- add examples
- optimize code
- buy a sensor
- elaborate error handling.
- elaborate unit test.

View File

@ -0,0 +1,72 @@
//
// FILE: AGS02MA_PPB.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: test application
// DATE: 2021-08-12
// URL: https://github.com/RobTillaart/AGS02MA
//
#include "AGS02MA.h"
AGS02MA AGS(26);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Wire.begin();
Wire.setClock(30400);
#if defined(__AVR__)
Serial.print("TWBR:\t");
Serial.println(TWBR);
#endif
Serial.print("AGS02MA_LIB_VERSION: ");
Serial.println(AGS02MA_LIB_VERSION);
Serial.println();
bool b = AGS.begin();
Serial.print("BEGIN:\t");
Serial.println(b);
Serial.println("\nWarming up (120 seconds = 24 dots)");
while (AGS.isHeated() == false)
{
delay(5000);
Serial.print(".");
}
Serial.println();
b = AGS.setPPBMode();
uint8_t m = AGS.getMode();
Serial.print("MODE:\t");
Serial.print(b);
Serial.print("\t");
Serial.println(m);
uint8_t version = AGS.getSensorVersion();
Serial.print("VERS:\t");
Serial.println(version);
}
void loop()
{
delay(2000);
uint32_t value = AGS.readPPB();
Serial.print("PPB:\t");
Serial.print(value);
Serial.print("\t");
Serial.print(AGS.lastStatus(), HEX);
Serial.print("\t");
Serial.print(AGS.lastError(), HEX);
Serial.println();
}
// -- END OF FILE --

View File

@ -0,0 +1,65 @@
//
// FILE: AGS02MA_UGM3.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: test application
// DATE: 2021-08-12
// URL: https://github.com/RobTillaart/AGS02MA
//
#include "AGS02MA.h"
AGS02MA AGS(26);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Wire.begin();
Wire.setClock(30400);
#if defined(__AVR__)
Serial.print("TWBR:\t");
Serial.println(TWBR);
#endif
Serial.print("AGS02MA_LIB_VERSION: ");
Serial.println(AGS02MA_LIB_VERSION);
Serial.println();
bool b = AGS.begin();
Serial.print("BEGIN:\t");
Serial.println(b);
b = AGS.setUGM3Mode();
uint8_t m = AGS.getMode();
Serial.print("MODE:\t");
Serial.print(b);
Serial.print("\t");
Serial.println(m);
uint8_t version = AGS.getSensorVersion();
Serial.print("VERS:\t");
Serial.println(version);
}
void loop()
{
delay(2000);
uint32_t value = AGS.readUGM3();
Serial.print("UGM3:\t");
Serial.print(value);
Serial.print("\t");
Serial.print(AGS.lastStatus(), HEX);
Serial.print("\t");
Serial.print(AGS.lastError(), HEX);
Serial.println();
}
// -- END OF FILE --

View File

@ -0,0 +1,59 @@
//
// FILE: AGS02MA_calibrate.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: test application
// DATE: 2021-08-12
// URL: https://github.com/RobTillaart/AGS02MA
//
#include "AGS02MA.h"
uint32_t start, stop;
AGS02MA AGS(26);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AGS02MA_LIB_VERSION: ");
Serial.println(AGS02MA_LIB_VERSION);
Serial.println();
Wire.begin();
Wire.setClock(30400);
bool b = AGS.begin();
Serial.print("BEGIN:\t");
Serial.println(b);
Serial.println("Place the device outisde in open air for 6 minutes");
Serial.println("Take a drink and relax ;)");
Serial.println();
start = millis();
while(millis() - start < 360000UL)
{
delay(15000);
Serial.println(millis() - start);
}
b = AGS.zeroCalibration();
Serial.print("CALIB:\t");
Serial.println(b);
Serial.println("Calibration done");
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,64 @@
//
// FILE: AGS02MA_test.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: test application
// DATE: 2021-08-12
// URL: https://github.com/RobTillaart/AGS02MA
//
#include "AGS02MA.h"
uint32_t start, stop;
AGS02MA AGS(26);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Wire.begin();
Wire.setClock(30400);
#if defined(__AVR__)
Serial.print("TWBR:\t");
Serial.println(TWBR);
#endif
Serial.print("AGS02MA_LIB_VERSION: ");
Serial.println(AGS02MA_LIB_VERSION);
Serial.println();
bool b = AGS.begin();
Serial.print("BEGIN:\t");
Serial.println(b);
b = AGS.setPPBMode();
uint8_t m = AGS.getMode();
Serial.print("MODE:\t");
Serial.print(b);
Serial.print("\t");
Serial.println(m);
uint8_t version = AGS.getSensorVersion();
Serial.print("VERS:\t");
Serial.println(version);
}
void loop()
{
delay(2000);
uint32_t value = AGS.readPPB();
Serial.print("PPB:\t");
Serial.println(value);
}
// -- END OF FILE --

View File

@ -0,0 +1,36 @@
# Syntax Colouring Map for AGS02MA
# Data types (KEYWORD1)
AGS02MA KEYWORD1
# Methods and Functions (KEYWORD2)
begin KEYWORD2
isConnected KEYWORD2
isHeated KEYWORD2
lastRead KEYWORD2
setAddress KEYWORD2
getAddress KEYWORD2
getSensorVersion KEYWORD2
setPPBMode KEYWORD2
setUGM3Mode KEYWORD2
getMode KEYWORD2
readPPB KEYWORD2
readUGM3 KEYWORD2
zeroCalibration KEYWORD2
lastError KEYWORD2
lastStatus KEYWORD2
# Constants ( LITERAL1)
AGS02MA_LIB_VERSION LITERAL1
AGS02MA_OK LITERAL1
AGS02MA_ERROR LITERAL1

View File

@ -0,0 +1,22 @@
{
"name": "AGS02MA",
"keywords": "I2C, AGS02MA, tvoc",
"description": "Arduino library for AGS02MA - TVOC sensor.",
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
}
],
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/AGS02MA.git"
},
"version": "0.1.0",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*"
}

View File

@ -0,0 +1,11 @@
name=AGS02MA
version=0.1.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for AGS02MA - TVOC sensor
paragraph=Note it uses slow I2C < 30KHz. See readme.
category=Sensors
url=https://github.com/RobTillaart/AGS02MA.git
architectures=*
includes=AGS02MA.h
depends=