Skip to content

Commit e67bc3a

Browse files
committed
0.1.1 AGS02MA
1 parent d59f3f3 commit e67bc3a

File tree

14 files changed

+421
-91
lines changed

14 files changed

+421
-91
lines changed

libraries/AGS02MA/AGS02MA.cpp

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//
22
// FILE: AGS02MA.cpp
3-
// AUTHOR: Rob Tillaart
3+
// AUTHOR: Rob Tillaart, Viktor Balint
44
// DATE: 2021-08-12
5-
// VERSION: 0.1.0
5+
// VERSION: 0.1.1
66
// PURPOSE: Arduino library for AGS02MA TVOC
77
// URL: https://github.com/RobTillaart/AGS02MA
88

@@ -18,12 +18,17 @@
1818

1919

2020

21-
2221
AGS02MA::AGS02MA(const uint8_t deviceAddress, TwoWire *wire)
2322
{
24-
_address = deviceAddress;
25-
_wire = wire;
26-
_error = AGS02MA_OK;
23+
_address = deviceAddress;
24+
_wire = wire;
25+
26+
_I2CResetSpeed = 100000;
27+
_startTime = 0;
28+
_lastRead = 0;
29+
_mode = 255;
30+
_status = AGS02MA_OK;
31+
_error = AGS02MA_OK;
2732
}
2833

2934

@@ -54,13 +59,13 @@ bool AGS02MA::begin()
5459
bool AGS02MA::isConnected()
5560
{
5661
#if defined (__AVR__)
57-
TWBR = 255;
62+
TWBR = 255;
5863
#else
5964
_wire->setClock(AGS02MA_I2C_CLOCK);
6065
#endif
6166
_wire->beginTransmission(_address);
62-
bool rv = ( _wire->endTransmission() == 0);
63-
_wire->setClock(_I2CReseSpeed);
67+
bool rv = ( _wire->endTransmission(true) == 0);
68+
_wire->setClock(_I2CResetSpeed);
6469
return rv;
6570
}
6671

@@ -84,6 +89,7 @@ uint8_t AGS02MA::getSensorVersion()
8489
uint8_t version = 0xFF;
8590
if (_readRegister(AGS02MA_VERSION))
8691
{
92+
// unclear what the other bytes have for information. (if there is any)
8793
version = _buffer[3];
8894
if (_CRC8(_buffer, 5) != 0)
8995
{
@@ -147,6 +153,7 @@ uint32_t AGS02MA::readPPB()
147153

148154
uint32_t AGS02MA::readUGM3()
149155
{
156+
// TODO identical code wise to PPB, can be merged into one.
150157
uint32_t val = 0xFFFFFFFF;
151158
_lastRead = millis();
152159
if (_readRegister(AGS02MA_DATA))
@@ -183,41 +190,41 @@ int AGS02MA::lastError()
183190
}
184191

185192

186-
187193
/////////////////////////////////////////////////////////
188194
//
189195
// PRIVATE
190196
//
191197
bool AGS02MA::_readRegister(uint8_t reg)
192198
{
193199
#if defined (__AVR__)
194-
TWBR = 255;
200+
TWBR = 255;
195201
#else
196202
_wire->setClock(AGS02MA_I2C_CLOCK);
197203
#endif
198204
_wire->beginTransmission(_address);
199205
_wire->write(reg);
200-
_error = _wire->endTransmission();
201-
206+
_error = _wire->endTransmission(true);
207+
delay(30);
202208
if (_wire->requestFrom(_address, (uint8_t)5) != 5)
203209
{
204210
_error = AGS02MA_ERROR;
205-
_wire->setClock(_I2CReseSpeed);
211+
_wire->setClock(_I2CResetSpeed);
206212
return false;
207213
}
208214
for (int i = 0; i < 5; i++)
209215
{
210216
_buffer[i] = _wire->read();
211217
}
212-
_wire->setClock(_I2CReseSpeed);
218+
delay(30);
219+
_wire->setClock(_I2CResetSpeed);
213220
return true;
214221
}
215222

216223

217224
bool AGS02MA::_writeRegister(uint8_t reg)
218225
{
219226
#if defined (__AVR__)
220-
TWBR = 255;
227+
TWBR = 255;
221228
#else
222229
_wire->setClock(AGS02MA_I2C_CLOCK);
223230
#endif
@@ -227,8 +234,9 @@ bool AGS02MA::_writeRegister(uint8_t reg)
227234
{
228235
_wire->write(_buffer[i]);
229236
}
230-
_error = _wire->endTransmission();
231-
_wire->setClock(_I2CReseSpeed);
237+
_error = _wire->endTransmission(true);
238+
delay(30);
239+
_wire->setClock(_I2CResetSpeed);
232240
return (_error == 0);
233241
}
234242

libraries/AGS02MA/AGS02MA.h

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#pragma once
22
//
33
// FILE: AGS02MA.h
4-
// AUTHOR: Rob Tillaart
4+
// AUTHOR: Rob Tillaart, Viktor Balint
55
// DATE: 2021-08-12
6-
// VERSION: 0.1.0
6+
// VERSION: 0.1.1
77
// PURPOSE: Arduino library for AGS02MA TVOC
88
// URL: https://github.com/RobTillaart/AGS02MA
99
//
@@ -13,66 +13,72 @@
1313
#include "Wire.h"
1414

1515

16-
#define AGS02MA_LIB_VERSION (F("0.1.0"))
16+
#define AGS02MA_LIB_VERSION (F("0.1.1"))
1717

1818
#define AGS02MA_OK 0
1919
#define AGS02MA_ERROR -10
2020
#define AGS02MA_CRC_ERROR -11
2121

2222

23-
#define AGS02MA_I2C_CLOCK 25000
24-
#define AGS02MA_I2C_RESET 400000
23+
#define AGS02MA_I2C_CLOCK 30000
2524

2625

2726
class AGS02MA
2827
{
2928
public:
29+
// address 26 = 0x1A
3030
explicit AGS02MA(const uint8_t deviceAddress = 26, TwoWire *wire = &Wire);
3131

32-
#if defined (ESP8266) || defined(ESP32)
32+
#if defined (ESP8266) || defined(ESP32)
3333
bool begin(uint8_t sda, uint8_t scl);
3434
#endif
3535
bool begin();
3636
bool isConnected();
3737

3838
bool isHeated() { return (millis() - _startTime) > 120000UL; };
39-
uint32_t lastRead() { return _lastRead; };
4039

40+
41+
// CONFIGURATION
4142
bool setAddress(const uint8_t deviceAddress);
4243
uint8_t getAddress() { return _address; };
43-
4444
uint8_t getSensorVersion();
45-
void setI2CResetSpeed(uint32_t s) { _I2CReseSpeed = s; };
46-
uint32_t getI2CResetSpeed() { return _I2CReseSpeed; };
4745

46+
// to set the speed the I2C bus should return to
47+
// as the device operates at very low bus speed.
48+
void setI2CResetSpeed(uint32_t s) { _I2CResetSpeed = s; };
49+
uint32_t getI2CResetSpeed() { return _I2CResetSpeed; };
50+
51+
// to be called after at least 5 minutes in fresh air.
52+
bool zeroCalibration();
4853

54+
55+
// CORE
4956
bool setPPBMode();
5057
bool setUGM3Mode();
5158
uint8_t getMode() { return _mode; };
5259

5360
uint32_t readPPB();
5461
uint32_t readUGM3();
5562

56-
// to be called after at least 5 minutes in fresh air.
57-
bool zeroCalibration();
58-
63+
uint32_t lastRead() { return _lastRead; };
5964
int lastError();
6065
uint8_t lastStatus() { return _status; };
6166

67+
6268
private:
6369
bool _readRegister(uint8_t reg);
6470
bool _writeRegister(uint8_t reg);
65-
uint8_t _CRC8(uint8_t * buf, uint8_t size);
6671

67-
uint32_t _I2CReseSpeed = 100000;
72+
uint32_t _I2CResetSpeed = 100000;
6873
uint32_t _startTime = 0;
6974
uint32_t _lastRead = 0;
7075
uint8_t _address = 0;
7176
uint8_t _mode = 255;
7277
uint8_t _status = 0;
73-
7478
uint8_t _buffer[5];
7579

80+
uint8_t _CRC8(uint8_t * buf, uint8_t size);
81+
7682
int _error = AGS02MA_OK;
7783

7884
TwoWire* _wire;

libraries/AGS02MA/README.md

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,28 @@
44

55
# AGS02MA
66

7-
Arduino library for AGS02MA TVOC sensor.
7+
Arduino library for AGS02MA TVOC sensor.
88

9-
This library is experimental, so please use with care.
10-
Please note the warning about the I2C speed.
9+
This library is experimental, so please use with care.
10+
Note the warning about the I2C speed, the device works at 30 KHz.
1111

1212

1313
## I2C - warning low speed
1414

15-
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.
16-
Tests with ESP32, which can go as low as ~5 KHZ are underway and expected to work.
15+
The sensor uses I2C at very low speed <= 30 KHz.
16+
For an Arduino UNO the lowest speed possible is about 30.4KHz (TWBR = 255) which works.
17+
Tests with ESP32 / ESP8266 at 30 KHz look good, tests with lower clock speeds are to be done but expected to work.
18+
First runs indicate 2 failed reads in > 500 Reads, so less than 1%
19+
20+
The library sets the clock speed to 30 KHz (for non AVR) during operation and resets it to 100 KHz after operation.
21+
This is done to minimize interference with the communication of other devices. The reset clock speed can be changed with **setI2CResetSpeed()** e.g. to 200 or 400 KHz.
1722

18-
The library sets the clock speed to 25KHz (for non AVR) during operation and resets it to 100 KHz
19-
after operation. This is done to minimize interference with the communication with other devices.
2023

2124
## Interface
2225

26+
27+
### Constructor
28+
2329
- **AGS02MA(uint8_t deviceAddress = 26, TwoWire \*wire = &Wire)** constructor, with default address and default I2C interface.
2430
- **bool begin(uint8_t sda, uint8_t scl)** begin for ESP32 and ESP8266.
2531
- **bool begin()** initializer for Arduino UNO a.o.
@@ -28,58 +34,91 @@ after operation. This is done to minimize interference with the communication wi
2834

2935
### Timing
3036

31-
- **bool isHeated()** returns true if 2 minutes have passed after startup.
32-
Otherwise the device is not ready.
33-
- **uint32_t lastRead()** last time the device is read, timestamp in milliseconds since start.
37+
- **bool isHeated()** returns true if 2 minutes have passed after startup (call of **begin()** ).
38+
Otherwise the device is not optimal ready.
39+
According to the datasheet the preheating will improve the quality
40+
of the measurements.
41+
- **uint32_t lastRead()** last time the device is read, timestamp is in milliseconds since start.
42+
Returns 0 if **readPPB()** or **readUGM3()** is not called yet.
43+
This function allows to implement sort of asynchronous wait.
44+
One must keep reads ~3 seconds apart according to the datasheet.
3445

3546

3647
### Administration
3748

38-
- **bool setAddress(const uint8_t deviceAddress)** Not implemented yet.
39-
- **uint8_t getAddress()** returns set address.
49+
- **bool setAddress(const uint8_t deviceAddress)** sets a new addres for the sensor. If function succeeds the address changes immediately and will be persistent over a reboot.
50+
- **uint8_t getAddress()** returns the set address. Default the function will return 26 or 0x1A.
4051
- **uint8_t getSensorVersion()** reads sensor version from device.
52+
If the version cannot be read the function will return 255.
4153

42-
The library sets the clock speed to 25KHz (for non AVR) during operation and resets it to 100 KHz
43-
after operation. This is done to minimize interference with the communication with other devices.
44-
The following function can change this reset speed.
54+
The library sets the clock speed to 30 KHz (for non AVR) during operation and resets it to 100 KHz after operation.
55+
This is done to minimize interference with the communication of other devices.
56+
The following function can change the I2C reset speed to e.g. 200 or 400 KHz.
4557

4658
- **setI2CResetSpeed(uint32_t s)** sets the I2C speed the library need to reset the I2C speed to.
47-
- **getI2CResetSpeed()** returns the set value above. Default is 100KHz.
59+
- **getI2CResetSpeed()** returns the set value above. Default is 100 KHz.
4860

4961

5062
### setMode
5163

64+
The default mode at startup of the sensor is PPB = parts per billion.
65+
5266
- **bool setPPBMode()** sets device in PartPerBillion mode. Returns true on success.
5367
- **bool setUGM3Mode()** sets device in micro gram per cubic meter mode. Returns true on success.
5468
- **uint8_t getMode()** returns mode set. 0 = PPB, 1 = UGm3, 255 = not set.
5569

5670

71+
#### PPB versus UGM3
72+
73+
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.
74+
PPB is therefore an more an absolute indicator where uG/m3 is sort of relative indicator.
75+
If the gas is unknown, PPB is imho the preferred measurement.
76+
77+
5778
### Reading
5879

59-
WARNING: Take at least 2 seconds between reads.
80+
WARNING: Datasheet advises to take 3 seconds between reads.
81+
You might be able to squeeze time down to 1.5 second at your own risk.
6082

61-
- **uint32_t readPPB()** reads PPB from device. returns 0xFFFFFFFF if failed.
62-
Check lastStatus() to get more info
83+
- **uint32_t readPPB()** reads PPB from device.
84+
Returns 0xFFFFFFFF if failed.
85+
Typical value should be between 1..999999.
86+
Check lastStatus() to get more info about success.
6387
- **uint32_t readUGM3()** reads current value from device.
88+
Returns 0xFFFFFFFF if failed.
6489

6590

6691
### Other
6792

68-
- **bool zeroCalibration()** to be called after at least 5 minutes in fresh air.
69-
See example sketch.
93+
- **bool zeroCalibration()** to be called after at least 5 minutes in fresh air.
94+
See example sketch.
7095
- **int lastError()** returns last error.
71-
- **uint8_t lastStatus()** returns status byte from last read. Read datasheet.
96+
- **uint8_t lastStatus()** returns status byte from last read.
97+
Read datasheet for details.
7298

7399

74100
## Future
75101

76-
- improve documentation
77102
- test test test ...
78-
- add examples
79-
- optimize code
80-
- buy a sensor
81-
- elaborate error handling.
82-
- elaborate unit test.
103+
- buy a few sensors
104+
105+
### Documentation
83106

107+
- improve
108+
- add table for molecular weights
109+
- add indicative table for PPB health zone
110+
111+
### Code
112+
113+
- add examples
114+
- add **float readPPM()**
115+
- add **float readmGM3()**
116+
- add **bool RDYbit()** split the RDY bit of the status byte
117+
- check the mode bits of the status byte with internal \_mode.
118+
- optimize code where possible
119+
- optimize timing 30ms between low level I2C reads and writes.
120+
- elaborate error handling.
121+
- improve unit testing?
122+
- investigate max frequency of reads (now 3 seconds apart)
84123

85124

0 commit comments

Comments
 (0)