0.3.3 AGS02MA

This commit is contained in:
rob tillaart 2023-01-21 14:28:43 +01:00
parent 2b5f49b2e3
commit 45f1941984
12 changed files with 166 additions and 95 deletions

View File

@ -6,7 +6,7 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: arduino/arduino-lint-action@v1
with:
library-manager: update

View File

@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6

View File

@ -10,7 +10,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: json-syntax-check
uses: limitusus/json-syntax-check@v1
with:

View File

@ -2,15 +2,15 @@
// FILE: AGS02MA.cpp
// AUTHOR: Rob Tillaart, Viktor Balint, Beanow
// DATE: 2021-08-12
// VERSION: 0.3.2
// PURPOSE: Arduino library for AGS02MA TVOC
// VERSION: 0.3.3
// PURPOSE: Arduino library for AGS02MA TVOC sensor
// URL: https://github.com/RobTillaart/AGS02MA
#include "AGS02MA.h"
// REGISTERS
// REGISTERS
#define AGS02MA_DATA 0x00
#define AGS02MA_CALIBRATION 0x01
#define AGS02MA_VERSION 0x11
@ -44,7 +44,7 @@ bool AGS02MA::begin(uint8_t dataPin, uint8_t clockPin)
bool AGS02MA::begin()
{
_startTime = millis(); // PREHEAT TIMING
_startTime = millis(); // PREHEAT TIMING
_wire->begin();
return isConnected();
}
@ -53,14 +53,16 @@ bool AGS02MA::begin()
bool AGS02MA::isConnected()
{
#if defined (__AVR__)
// TWBR = 255; // == 30.4 KHz with TWSR = 0x00
TWBR = 78; // == 25.0 KHZ
TWSR = 0x01; // prescaler = 4
// TWBR = 255; // == 30.4 KHz with TWSR = 0x00
TWBR = 78; // == 25.0 KHZ
TWSR = 0x01; // prescaler = 4
#else
_wire->setClock(AGS02MA_I2C_CLOCK);
#endif
_wire->beginTransmission(_address);
bool rv = ( _wire->endTransmission(true) == 0);
#if defined (__AVR__)
TWSR = 0x00;
#endif
@ -136,7 +138,7 @@ uint32_t AGS02MA::getSensorDate()
date += _bin2bcd(_buffer[1]);
date <<= 8;
date += _bin2bcd(_buffer[2]);
// version = _buffer[3];
// version = _buffer[3];
if (_CRC8(_buffer, 5) != 0)
{
_error = AGS02MA_ERROR_CRC;
@ -266,6 +268,7 @@ bool AGS02MA::readRegister(uint8_t address, AGS02MA::RegisterData &reg) {
return true;
}
/////////////////////////////////////////////////////////
//
// PRIVATE
@ -299,12 +302,13 @@ bool AGS02MA::_readRegister(uint8_t reg)
while (millis() - _lastRegTime < 30) yield();
#if defined (__AVR__)
// TWBR = 255; // == 30.4 KHz with TWSR = 0x00
TWBR = 78; // == 25.0 KHZ
TWSR = 0x01; // prescaler = 4
// TWBR = 255; // == 30.4 KHz with TWSR = 0x00
TWBR = 78; // == 25.0 KHZ
TWSR = 0x01; // prescaler = 4
#else
_wire->setClock(AGS02MA_I2C_CLOCK);
#endif
_wire->beginTransmission(_address);
_wire->write(reg);
_error = _wire->endTransmission(true);
@ -339,9 +343,9 @@ bool AGS02MA::_writeRegister(uint8_t reg)
_lastRegTime = millis();
#if defined (__AVR__)
// TWBR = 255; // == 30.4 KHz with TWSR = 0x00
TWBR = 78; // == 25.0 KHZ
TWSR = 0x01; // prescaler = 4
// TWBR = 255; // == 30.4 KHz with TWSR = 0x00
TWBR = 78; // == 25.0 KHZ
TWSR = 0x01; // prescaler = 4
#else
_wire->setClock(AGS02MA_I2C_CLOCK);
#endif
@ -359,16 +363,19 @@ bool AGS02MA::_writeRegister(uint8_t reg)
return (_error == 0);
}
uint16_t AGS02MA::_getDataMSB()
{
return (_buffer[0] << 8) + _buffer[1];
}
uint16_t AGS02MA::_getDataLSB()
{
return (_buffer[2] << 8) + _buffer[3];
}
uint8_t AGS02MA::_CRC8(uint8_t * buf, uint8_t size)
{
uint8_t crc = 0xFF; // start value
@ -391,4 +398,5 @@ uint8_t AGS02MA::_bin2bcd (uint8_t value)
}
// -- END OF FILE --
// -- END OF FILE --

View File

@ -3,8 +3,8 @@
// FILE: AGS02MA.h
// AUTHOR: Rob Tillaart, Viktor Balint, Beanow
// DATE: 2021-08-12
// VERSION: 0.3.2
// PURPOSE: Arduino library for AGS02MA TVOC
// VERSION: 0.3.3
// PURPOSE: Arduino library for AGS02MA TVOC sensor
// URL: https://github.com/RobTillaart/AGS02MA
//
@ -13,7 +13,7 @@
#include "Wire.h"
#define AGS02MA_LIB_VERSION (F("0.3.2"))
#define AGS02MA_LIB_VERSION (F("0.3.3"))
#define AGS02MA_OK 0
#define AGS02MA_ERROR -10
@ -142,4 +142,5 @@ private:
};
// -- END OF FILE --
// -- END OF FILE --

View File

@ -0,0 +1,34 @@
# Change Log AGS02MA
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.3.3] - 2023-01-21
- update GitHub actions
- update license 2023
- update keywords
- minor edit readme.md
- minor edit code
- add CHANGELOG.md (for real)
## [0.3.2] - 2022-10-26
- add CHANGELOG.md
- add RP2040 in build
----
## no info
- 0.3.1
- 0.3.0
- 0.2.0
- 0.1.4
- 0.1.3
- 0.1.2
- 0.1.1
- 0.1.0

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021-2022 Rob Tillaart
Copyright (c) 2021-2023 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

View File

@ -19,33 +19,33 @@ Since 0.3.1 this library uses 25 KHz.
### PIN layout from left to right
| Front L->R | Description |
|:----------:|:------------|
| pin 1 | VDD + |
| pin 2 | SDA data |
| pin 3 | GND |
| pin 4 | SCL clock |
| Front L->R | Description |
|:------------:|:--------------|
| pin 1 | VDD + |
| pin 2 | SDA data |
| pin 3 | GND |
| pin 4 | SCL clock |
### WARNING - LOW SPEED
#### WARNING - LOW SPEED
The sensor uses I2C at very low speed <= 30 KHz.
For an Arduino UNO the lowest speed supported is about 30.4KHz (TWBR = 255) which works.
First runs with Arduino UNO indicate 2 failed reads in > 500 Reads, so less than 1%
Tests with ESP32 / ESP8266 at 30 KHz look good,
Tests with ESP32 / ESP8266 at 30 KHz look good,
tests with ESP32 at lower clock speeds are to be done but expected to work.
The library sets the clock speed to 30 KHz (for non AVR) during operation
The library sets the clock speed to 30 KHz (for non AVR) during operation
and resets it default to 100 KHz after operation.
This is done to minimize interference with the communication of other devices.
This is done to minimize interference with the communication of other devices.
The reset clock speed can be changed with **setI2CResetSpeed(speed)** e.g. to 200 or 400 KHz.
#### 0.3.1 fix.
Version 0.3.1 sets the **I2C prescaler TWSR** register of the Arduino UNO to 4 so the lowest
speed possible is reduced to about 8 KHz.
Version 0.3.1 sets the **I2C prescaler TWSR** register of the Arduino UNO to 4 so the lowest
speed possible is reduced to about 8 KHz.
A test run 4 hours with 6000++ reads on an UNO at 25 KHz gave 0 errors.
So the communication speed will be set to 25 KHz, also for other boards, for stability.
After communication the clock (+ prescaler) is reset again as before.
@ -58,7 +58,7 @@ My devices all report version 117 and this version is used to develop / test thi
There are devices reported with version 118 which behave differently.
### ugM3 not supported
#### ugM3 not supported
See - https://github.com/RobTillaart/AGS02MA/issues/11
@ -69,11 +69,11 @@ If you encounter similar problems with setting the mode (any version), please le
That will help indicating if this is a "structural change" or incident.
### Calibrate problem!
#### Calibrate problem!
See - https://github.com/RobTillaart/AGS02MA/issues/13
In this issue a problem is reported with a version 118 sensor.
In this issue a problem is reported with a version 118 sensor.
The problem exposed itself after running the calibration sketch (command).
The problem has been confirmed by a 2nd version 118 sensor.
Additional calibration runs did not fix the problem.
@ -82,20 +82,20 @@ Version 117 seem to have no problems with calibration.
**Advice**: do **NOT** calibrate a version 118.
Note: the version 0.2.0 determines the version in the calibration function so
it won't calibrate any non 117 version.
it won't calibrate any non 117 version.
### Please report your experiences.
#### Please report your experiences.
If you have a AGS20MA device, version 117 or 118 or other,
please let me know your experiences
If you have a AGS20MA device, version 117 or 118 or other,
please let me know your experiences
with the sensor and this (or other) library.
## Interface
### Constructor
#### Constructor
- **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.
@ -104,36 +104,36 @@ with the sensor and this (or other) library.
- **void reset()** reset internal variables.
### Timing
#### Timing
- **bool isHeated()** returns true if 2 minutes have passed after startup (call of **begin()** ).
Otherwise the device is not optimal ready.
According to the datasheet the preheating will improve the quality of the measurements.
- **uint32_t lastRead()** last time the device is read, timestamp is in milliseconds since start.
Returns 0 if **readPPB()** or **readUGM3()** is not called yet.
This function allows to implement sort of asynchronous wait.
Returns 0 if **readPPB()** or **readUGM3()** is not called yet.
This function allows to implement sort of asynchronous wait.
One must keep reads at least 1.5 seconds but preferred 3 seconds apart according to the datasheet.
### Administration
#### Administration
- **bool setAddress(const uint8_t deviceAddress)** sets a new address for the sensor.
- **bool setAddress(const uint8_t deviceAddress)** sets a new address for the sensor.
If function succeeds the address changes immediately and will be persistent over a reboot.
- **uint8_t getAddress()** returns the set address. Default the function will return 26 or 0x1A.
- **uint8_t getSensorVersion()** reads sensor version from device.
If the version cannot be read the function will return 255.
- **uint8_t getSensorVersion()** reads sensor version from device.
If the version cannot be read the function will return 255.
(My test sensors all return version 117, version 118 is reported)
- **uint32_t getSensorDate()** (experimental) reads bytes from the sensor that seem to indicate the production date(?). This date is encoded in an uint32_t to minimize footprint as it is a debug function.
- **uint32_t getSensorDate()** (experimental) reads bytes from the sensor that seem to indicate the production date(?). This date is encoded in an uint32_t to minimize footprint as it is a debug function.
```cpp
uint32_t dd = sensor.getSensorDate();
Serial.println(dd, HEX); // prints YYYYMMDD e.g. 20210203
Serial.println(dd, HEX); // prints YYYYMMDD e.g. 20210203
```
### I2C clock speed
#### I2C clock speed
The library sets the clock speed to 25 KHz during operation
The library sets the clock speed to 25 KHz during operation
and resets it to 100 KHz after operation.
This is done to minimize interference with the communication of other devices.
The following function can change the I2C reset speed to e.g. 200 or 400 KHz.
@ -142,7 +142,7 @@ The following function can change the I2C reset speed to e.g. 200 or 400 KHz.
- **uint32_t getI2CResetSpeed()** returns the value set. Default is 100 KHz.
### setMode
#### setMode
The default mode at startup of the sensor is PPB = parts per billion.
@ -153,7 +153,7 @@ The default mode at startup of the sensor is PPB = parts per billion.
#### PPB versus UGM3
There is no 1 to 1 relation between the PPB and the uG/m3 readings as this relation depends
There is no 1 to 1 relation between the PPB and the uG/m3 readings as this relation depends
on the weight of the individual molecules.
PPB is therefore an more an absolute indicator where uG/m3 is sort of relative indicator.
If the gas is unknown, PPB is in my opinion the preferred measurement.
@ -162,25 +162,25 @@ If the gas is unknown, PPB is in my opinion the preferred measurement.
From an unverified source the following formula:
M = molecular weight of the gas.
**μg/m3 = (ppb)\*(12.187)\*(M) / (273.15 + °C)**
**μg/m3 = ppb \* M \* 12.187 / (273.15 + °C)**
Simplified formula for 1 atm @ 25°C:
Simplified formula for 1 atm @ 25°C:
**μg/m3 = ppb \* M \* 0.04087539829 μg/m3**
Some known gasses
| gas | Common name | ratio ppb-μg/m3 | molecular weight M |
|:-----|:------------------|:--------------------|:------------------:|
| SO2 | Sulphur dioxide | 1 ppb = 2.62 μg/m3 | 64 gr/mol |
| NO2 | Nitrogen dioxide | 1 ppb = 1.88 μg/m3 | 46 gr/mol |
| NO | Nitrogen monoxide | 1 ppb = 1.25 μg/m3 | 30 gr/mol |
| O3 | Ozone | 1 ppb = 2.00 μg/m3 | 48 gr/mol |
| CO | Carbon Monoxide | 1 ppb = 1.145 μg/m3 | 28 gr/mol |
| C6H6 | Benzene | 1 ppb = 3.19 μg/m3 | 78 gr/mol |
| gas | Common name | ratio ppb-μg/m3 | molecular weight M |
|:-------|:--------------------|:----------------------|:--------------------:|
| SO2 | Sulphur dioxide | 1 ppb = 2.62 μg/m3 | 64 gr/mol |
| NO2 | Nitrogen dioxide | 1 ppb = 1.88 μg/m3 | 46 gr/mol |
| NO | Nitrogen monoxide | 1 ppb = 1.25 μg/m3 | 30 gr/mol |
| O3 | Ozone | 1 ppb = 2.00 μg/m3 | 48 gr/mol |
| CO | Carbon Monoxide | 1 ppb = 1.145 μg/m3 | 28 gr/mol |
| C6H6 | Benzene | 1 ppb = 3.19 μg/m3 | 78 gr/mol |
### Read the sensor
#### Read the sensor
WARNING: The datasheet advises to take 3 seconds between reads.
Tests gave stable results at 1.5 second intervals.
@ -189,37 +189,37 @@ Use this faster rate at your own risk.
- **uint32_t readPPB()** reads PPB (parts per billion) from device.
Typical value should be between 1 .. 999999.
Returns **lastPPB()** value if failed so one does not get sudden jumps in graphs.
Check **lastStatus()** and **lastError()** to get more info about success.
Check **lastStatus()** and **lastError()** to get more info about success.
Time needed is ~35 milliseconds.
- **uint32_t readUGM3()** reads UGM3 (microgram per cubic meter) current value from device.
- **uint32_t readUGM3()** reads UGM3 (microgram per cubic meter) current value from device.
Typical values depend on the molecular weight of the TVOC.
Returns **lastUGM3()** if failed so one does not get sudden jumps in graphs.
- **float readPPM()** returns parts per million (PPM).
- **float readPPM()** returns parts per million (PPM).
This function is a wrapper around readPPB().
Typical value should be between 0.01 .. 999.99
Typical value should be between 0.01 .. 999.99
- **float readMGM3()** returns milligram per cubic meter.
- **float readUGF3()** returns microgram per cubic feet.
### Error Codes
#### Error Codes
| ERROR_CODES | value |
|:---------------------------|:-----:|
| AGS02MA_OK | 0 |
| AGS02MA_ERROR | -10 |
| AGS02MA_ERROR_CRC | -11 |
| AGS02MA_ERROR_READ | -12 |
| AGS02MA_ERROR_NOT_READY | -13 |
| ERROR_CODES | value |
|:----------------------------|:-------:|
| AGS02MA_OK | 0 |
| AGS02MA_ERROR | -10 |
| AGS02MA_ERROR_CRC | -11 |
| AGS02MA_ERROR_READ | -12 |
| AGS02MA_ERROR_NOT_READY | -13 |
#### Cached values
- **float lastPPM()** returns last readPPM (parts per million) value (cached).
- **float lastPPM()** returns last readPPM (parts per million) value (cached).
- **uint32_t lastPPB()** returns last read PPB (parts per billion) value (cached). Should be between 1..999999.
- **uint32_t lastUGM3()** returns last read UGM3 (microgram per cubic meter) value (cached).
### Calibration
#### Calibration
- **bool zeroCalibration()** to be called after at least 5 minutes in fresh air.
See example sketch.
@ -231,7 +231,7 @@ To be called after at least 5 minutes in fresh air.
Returns true on success.
### Other
#### Other
- **bool readRegister(uint8_t address, RegisterData &reg)** fills a data struct with the chip's register data at that address.
Primarily intended for troubleshooting and analysis of the sensor. Not recommended to build applications on top of this method's raw data.
@ -249,18 +249,33 @@ Read datasheet or table below for details. A new read is needed to update this.
|:-----:|:------------------------------------|:--------|
| 7-4 | internal use |
| 3-1 | 000 = PPB 001 = uG/M3 |
| 0 | RDY bit 0 = ready 1 = not ready | 1 == busy
| 0 | RDY bit 0 = ready 1 = not ready | 1 == busy
## Future
- test test test ...
#### Must
#### Should
- improve documentation
- add indicative table for PPB health zone
- check the mode bits of the status byte with internal \_mode.
- elaborate error handling.
- create an async interface for **readPPB()** if possible
- add indicative table for PPB health zone (source)
- put the I2C speed code in 2 inline functions
- less repeating conditional code places
- setLowSpeed() + setNormalSpeed()
- check the mode bits of the status byte with internal \_mode.
- maximize robustness of state
- test with hardware
- different gasses ?
#### Could
- elaborate error handling.
- create an async interface for **readPPB()** if possible
- delay(30) blocks performance ==> async version of **readRegister()**
- move code to .cpp?
#### Wont

View File

@ -11,29 +11,41 @@ isConnected KEYWORD2
reset KEYWORD2
isHeated KEYWORD2
lastRead KEYWORD2
setAddress KEYWORD2
getAddress KEYWORD2
getSensorVersion KEYWORD2
getSensorDate KEYWORD2
setI2CResetSpeed KEYWORD2
getI2CResetSpeed KEYWORD2
zeroCalibration KEYWORD2
manualZeroCalibration KEYWORD2
getZeroCalibrationData KEYWORD2
setPPBMode KEYWORD2
setUGM3Mode KEYWORD2
getMode KEYWORD2
readPPM KEYWORD2
readPPB KEYWORD2
readUGM3 KEYWORD2
readPPM KEYWORD2
readMGM3 KEYWORD2
readUGF3 KEYWORD2
lastPPM KEYWORD2
lastPPB KEYWORD2
lastUGM3 KEYWORD2
zeroCalibration KEYWORD2
lastRead KEYWORD2
lastError KEYWORD2
lastStatus KEYWORD2
dataReady KEYWORD2
readRegister KEYWORD2
# Constants ( LITERAL1)
AGS02MA_LIB_VERSION LITERAL1

View File

@ -21,7 +21,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/AGS02MA.git"
},
"version": "0.3.2",
"version": "0.3.3",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=AGS02MA
version=0.3.2
version=0.3.3
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for AGS02MA - TVOC sensor

View File

@ -2,7 +2,7 @@
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// DATE: 2021-08-12
// PURPOSE: unit tests for the AGS02NA tvoc sensor
// PURPOSE: unit tests for the AGS02NA TVOC sensor
// https://github.com/RobTillaart/AGS02MA
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
//
@ -29,6 +29,7 @@
// as millis() function is not implemented in
// the Arduino-CI environment
unittest_setup()
{
fprintf(stderr, "AGS02MA_LIB_VERSION: %s\n", (char *) AGS02MA_LIB_VERSION);
@ -97,4 +98,4 @@ unittest(test_mode)
unittest_main()
// --------
// -- END OF FILE --