Skip to content

Commit a7df5ee

Browse files
authored
Manual zero calibration (#15)
* Helper, interpret buffered data as two uint16 * Add manualZeroCalibration, using 0 for automatic. This should fix automatic zero calibration on v118 sensors and be compatible with v117. * Add getZeroCalibration. Returns a struct to only read the register once, as a typical application needs both values. * Version bump, add Beanow as author * Update calibration example * Include manual zero calibration example * Readme update * Base power-off notice on initial status or version + in manual calibration example * Implement error handling for getZeroCalibrationData * Include full manualZeroCalibration explanation in readme * Add ZeroCalibrationData.status warning * Version & author bump in library.json and library.properties
1 parent 6a0fa37 commit a7df5ee

File tree

7 files changed

+365
-23
lines changed

7 files changed

+365
-23
lines changed

AGS02MA.cpp

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

@@ -205,17 +205,37 @@ uint32_t AGS02MA::readUGM3()
205205
}
206206

207207

208-
bool AGS02MA::zeroCalibration()
208+
bool AGS02MA::manualZeroCalibration(uint16_t value)
209209
{
210210
_buffer[0] = 0x00;
211211
_buffer[1] = 0x0C;
212-
_buffer[2] = 0xFF;
213-
_buffer[3] = 0xF3;
214-
_buffer[4] = 0xFC;
212+
_buffer[2] = (uint8_t) (value >> 8);
213+
_buffer[3] = (uint8_t) (value & 0x00FF);
214+
_buffer[4] = _CRC8(_buffer, 4);
215215
return _writeRegister(AGS02MA_CALIBRATION);
216216
}
217217

218218

219+
bool AGS02MA::getZeroCalibrationData(AGS02MA::ZeroCalibrationData &data) {
220+
if (!_readRegister(AGS02MA_CALIBRATION))
221+
{
222+
return false;
223+
}
224+
225+
if (_CRC8(_buffer, 5) != 0)
226+
{
227+
_error = AGS02MA_ERROR_CRC;
228+
return false;
229+
}
230+
231+
_error = AGS02MA_OK;
232+
// Don't pollute the struct given to us, until we've handled all error cases.
233+
data.status = _getDataMSB();
234+
data.value = _getDataLSB();
235+
return true;
236+
}
237+
238+
219239
int AGS02MA::lastError()
220240
{
221241
int e = _error;
@@ -250,7 +270,7 @@ uint32_t AGS02MA::_readSensor()
250270
}
251271
return value;
252272
}
253-
273+
254274

255275
bool AGS02MA::_readRegister(uint8_t reg)
256276
{
@@ -301,6 +321,15 @@ bool AGS02MA::_writeRegister(uint8_t reg)
301321
return (_error == 0);
302322
}
303323

324+
uint16_t AGS02MA::_getDataMSB()
325+
{
326+
return (_buffer[0] << 8) + _buffer[1];
327+
}
328+
329+
uint16_t AGS02MA::_getDataLSB()
330+
{
331+
return (_buffer[2] << 8) + _buffer[3];
332+
}
304333

305334
uint8_t AGS02MA::_CRC8(uint8_t * buf, uint8_t size)
306335
{

AGS02MA.h

Lines changed: 28 additions & 4 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, Viktor Balint
4+
// AUTHOR: Rob Tillaart, Viktor Balint, Beanow
55
// DATE: 2021-08-12
6-
// VERSION: 0.1.4
6+
// VERSION: 0.2.0
77
// PURPOSE: Arduino library for AGS02MA TVOC
88
// URL: https://github.com/RobTillaart/AGS02MA
99
//
@@ -13,7 +13,7 @@
1313
#include "Wire.h"
1414

1515

16-
#define AGS02MA_LIB_VERSION (F("0.1.4"))
16+
#define AGS02MA_LIB_VERSION (F("0.2.0"))
1717

1818
#define AGS02MA_OK 0
1919
#define AGS02MA_ERROR -10
@@ -28,6 +28,19 @@
2828
class AGS02MA
2929
{
3030
public:
31+
struct ZeroCalibrationData
32+
{
33+
/**
34+
* Warning, the exact meaning of this status is not fully documented.
35+
* It seems like it's a bitmask:
36+
* 0000 1100 | 0x0C | 12 | Typical value
37+
* 0000 1101 | 0x0D | 13 | Sometimes seen on v117
38+
* 0111 1101 | 0x7D | 125 | Seen on v118, after power-off (gives different data than 12!)
39+
*/
40+
uint16_t status;
41+
uint16_t value;
42+
};
43+
3144
// address 26 = 0x1A
3245
explicit AGS02MA(const uint8_t deviceAddress = 26, TwoWire *wire = &Wire);
3346

@@ -53,7 +66,16 @@ class AGS02MA
5366
uint32_t getI2CResetSpeed() { return _I2CResetSpeed; };
5467

5568
// to be called after at least 5 minutes in fresh air.
56-
bool zeroCalibration();
69+
bool zeroCalibration() { return manualZeroCalibration(0); };
70+
71+
/**
72+
* Set the zero calibration value manually.
73+
* To be called after at least 5 minutes in fresh air.
74+
* For v117: 0-65535 = automatic calibration.
75+
* For v118: 0 = automatic calibration, 1-65535 manual calibration.
76+
*/
77+
bool manualZeroCalibration(uint16_t value = 0);
78+
bool getZeroCalibrationData(ZeroCalibrationData &data);
5779

5880

5981
// MODE
@@ -99,6 +121,8 @@ class AGS02MA
99121
uint8_t _status = 0;
100122
uint8_t _buffer[5];
101123

124+
uint16_t _getDataMSB();
125+
uint16_t _getDataLSB();
102126
uint8_t _CRC8(uint8_t * buf, uint8_t size);
103127
uint8_t _bin2bcd(uint8_t val);
104128

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ Typical value should be between 0.01 .. 999.99
170170

171171
- **bool zeroCalibration()** to be called after at least 5 minutes in fresh air.
172172
See example sketch.
173+
- **bool manualZeroCalibration(uint16_t value = 0)** Set the zero calibration value manually.
174+
To be called after at least 5 minutes in fresh air.
175+
For v117: 0-65535 = automatic calibration.
176+
For v118: 0 = automatic calibration, 1-65535 manual calibration.
177+
- **bool getZeroCalibrationData(ZeroCalibrationData &data);** fills a data struct with the current zero calibration status and value.
178+
Returns true on success.
173179
- **int lastError()** returns last error.
174180
- **uint8_t lastStatus()** returns status byte from last read.
175181
Read datasheet or table below for details. A new read is needed to update this.
Lines changed: 116 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,165 @@
11
//
22
// FILE: AGS02MA_calibrate.ino
3-
// AUTHOR: Rob Tillaart
4-
// VERSION: 0.1.0
3+
// AUTHOR: Rob Tillaart, Beanow
4+
// VERSION: 0.2.0
55
// PURPOSE: test application
66
// DATE: 2021-08-12
77
// URL: https://github.com/RobTillaart/AGS02MA
88
//
99

1010
#include "AGS02MA.h"
1111

12+
// You can decrease/disable warmup when you're certain the chip already warmed up.
13+
#define WARMUP_MINUTES 6
14+
#define READ_INTERVAL 3000
1215

13-
uint32_t start, stop;
1416

17+
uint32_t start, stop;
18+
uint8_t version;
1519

1620
AGS02MA AGS(26);
1721

1822

1923
void setup()
2024
{
25+
// ESP devices typically mis the first serial log lines after flashing.
26+
// Delay somewhat to include all output.
27+
delay(1000);
28+
2129
Serial.begin(115200);
2230
Serial.println(__FILE__);
2331

2432
Serial.print("AGS02MA_LIB_VERSION: ");
2533
Serial.println(AGS02MA_LIB_VERSION);
2634
Serial.println();
2735

36+
Serial.print("WARMUP:\t\t");
37+
Serial.println(WARMUP_MINUTES);
38+
Serial.print("INTERVAL:\t");
39+
Serial.println(READ_INTERVAL);
40+
2841
Wire.begin();
2942

3043
bool b = AGS.begin();
31-
Serial.print("BEGIN:\t");
44+
Serial.print("BEGIN:\t\t");
3245
Serial.println(b);
3346

34-
Serial.println("Place the device outside in open air for 6 minutes");
35-
Serial.println("Take a drink and relax ;)");
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+
b = AGS.setPPBMode();
63+
uint8_t m = AGS.getMode();
64+
Serial.print("MODE:\t\t");
65+
Serial.print(b);
66+
Serial.print("\t");
67+
Serial.println(m);
68+
69+
Serial.println();
70+
Serial.print("Place the device outside in open air for ");
71+
Serial.print(WARMUP_MINUTES);
72+
Serial.println(" minute(s).");
73+
Serial.println("Make sure your device has warmed up sufficiently for the best results.");
74+
Serial.println("The PPB values should be stable (may include noise) not constantly decreasing.");
3675
Serial.println();
3776

3877
start = millis();
39-
while(millis() - start < 360000UL)
78+
stop = WARMUP_MINUTES * 60000UL;
79+
while(millis() - start < stop)
80+
{
81+
Serial.print("[PRE ]\t");
82+
printPPB();
83+
delay(READ_INTERVAL);
84+
}
85+
86+
Serial.println();
87+
Serial.println("About to perform calibration now.");
88+
89+
AGS02MA::ZeroCalibrationData initialValue;
90+
if (!AGS.getZeroCalibrationData(initialValue))
4091
{
41-
delay(15000);
42-
Serial.println(millis() - start);
92+
Serial.print("Error reading zero calibration data:\t");
93+
Serial.println(AGS.lastError());
94+
Serial.println("Won't attempt to calibrate. Reset when connection with the sensor is stable.");
95+
Serial.println();
96+
return;
4397
}
4498

99+
Serial.println("Your previous calibration data was:");
100+
printZeroCalibrationData(initialValue);
101+
102+
delay(1000);
103+
45104
// returns 1 if successful written
46105
b = AGS.zeroCalibration();
106+
Serial.println();
47107
Serial.print("CALIB:\t");
48108
Serial.println(b);
109+
Serial.println();
110+
Serial.println("Calibration done.");
111+
112+
AGS02MA::ZeroCalibrationData zc;
113+
while (!AGS.getZeroCalibrationData(zc))
114+
{
115+
Serial.print("Error:\t");
116+
Serial.print(AGS.lastError());
117+
Serial.println("\tretrying...");
118+
delay(READ_INTERVAL);
119+
}
49120

50-
Serial.println("Calibration done");
121+
Serial.println("Your new calibration data is:");
122+
printZeroCalibrationData(zc);
123+
124+
Serial.println();
125+
Serial.println("Showing what PPB values look like post calibration.");
126+
// A 125 status is typically shown on v118's after they've been powered off.
127+
// Either having this version at all, or seeing this status, we'll display a notice.
128+
if (version == 118 || initialValue.status == 125)
129+
{
130+
Serial.println("NOTICE: v118 sensors are known to give different results after powering off!");
131+
Serial.println("You may need to manually set your calibration value every time power was lost.");
132+
}
133+
Serial.println();
51134
}
52135

53136

54137
void loop()
55138
{
139+
Serial.print("[POST]\t");
140+
printPPB();
141+
delay(READ_INTERVAL);
142+
}
143+
144+
145+
void printZeroCalibrationData(AGS02MA::ZeroCalibrationData &zc) {
146+
Serial.print("Status:\t");
147+
Serial.println(zc.status);
148+
Serial.print("Value:\t");
149+
Serial.println(zc.value);
56150
}
57151

58152

153+
void printPPB()
154+
{
155+
uint32_t value = AGS.readPPB();
156+
Serial.print("PPB:\t");
157+
Serial.print(value);
158+
Serial.print("\t");
159+
Serial.print(AGS.lastStatus(), HEX);
160+
Serial.print("\t");
161+
Serial.print(AGS.lastError(), HEX);
162+
Serial.println();
163+
}
164+
59165
// -- END OF FILE --

0 commit comments

Comments
 (0)