mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
386 lines
7.2 KiB
C++
386 lines
7.2 KiB
C++
//
|
|
// FILE: AGS02MA.cpp
|
|
// AUTHOR: Rob Tillaart, Viktor Balint, Beanow
|
|
// DATE: 2021-08-12
|
|
// VERSION: 0.4.0
|
|
// PURPOSE: Arduino library for AGS02MA TVOC sensor
|
|
// 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;
|
|
reset();
|
|
}
|
|
|
|
|
|
bool AGS02MA::begin()
|
|
{
|
|
_startTime = millis(); // PREHEAT TIMING
|
|
return isConnected();
|
|
}
|
|
|
|
|
|
bool AGS02MA::isConnected()
|
|
{
|
|
#if defined (__AVR__)
|
|
// TWBR = 255; // == 30.4 KHz with TWSR = 0x00
|
|
TWBR = 78; // == 25.0 KHZ
|
|
TWSR = 0x01; // pre-scaler = 4
|
|
#else
|
|
_wire->setClock(AGS02MA_I2C_CLOCK);
|
|
#endif
|
|
|
|
_wire->beginTransmission(_address);
|
|
bool rv = ( _wire->endTransmission(true) == 0);
|
|
|
|
#if defined (__AVR__)
|
|
TWSR = 0x00;
|
|
#endif
|
|
_wire->setClock(_I2CResetSpeed);
|
|
return rv;
|
|
}
|
|
|
|
|
|
void AGS02MA::reset()
|
|
{
|
|
_I2CResetSpeed = 100000;
|
|
_startTime = millis();
|
|
_lastRead = 0;
|
|
_lastPPB = 0;
|
|
_mode = 255;
|
|
_status = AGS02MA_OK;
|
|
_error = AGS02MA_OK;
|
|
}
|
|
|
|
|
|
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))
|
|
{
|
|
// for (int i = 0; i < 5; i++)
|
|
// {
|
|
// Serial.print(_buffer[i]);
|
|
// Serial.print('\t');
|
|
// }
|
|
// Serial.println();
|
|
// unclear what the other bytes have for information.
|
|
// datasheet names these 3 bytes as KEEP.
|
|
// BUFFER VALUE MEANING
|
|
// buffer [0] == 20 year ?
|
|
// buffer [1] == 07 month ?
|
|
// buffer [2] == 28 day ?
|
|
// buffer [3] == 117 VERSION
|
|
// buffer [4] == CRC
|
|
version = _buffer[3];
|
|
if (_CRC8(_buffer, 5) != 0)
|
|
{
|
|
_error = AGS02MA_ERROR_CRC;
|
|
}
|
|
}
|
|
return version;
|
|
}
|
|
|
|
|
|
uint32_t AGS02MA::getSensorDate()
|
|
{
|
|
uint32_t date = 0xFFFFFFFF;
|
|
if (_readRegister(AGS02MA_VERSION))
|
|
{
|
|
date = 0x20;
|
|
date <<= 8;
|
|
date += _bin2bcd(_buffer[0]);
|
|
date <<= 8;
|
|
date += _bin2bcd(_buffer[1]);
|
|
date <<= 8;
|
|
date += _bin2bcd(_buffer[2]);
|
|
// version = _buffer[3];
|
|
if (_CRC8(_buffer, 5) != 0)
|
|
{
|
|
_error = AGS02MA_ERROR_CRC;
|
|
}
|
|
}
|
|
return date;
|
|
}
|
|
|
|
|
|
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 = _readSensor();
|
|
if (_error == AGS02MA_OK)
|
|
{
|
|
_lastRead = millis();
|
|
_lastPPB = val;
|
|
}
|
|
else
|
|
{
|
|
val = _lastPPB;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
|
|
uint32_t AGS02MA::readUGM3()
|
|
{
|
|
uint32_t val = _readSensor();
|
|
if (_error == AGS02MA_OK)
|
|
{
|
|
_lastRead = millis();
|
|
_lastUGM3 = val;
|
|
}
|
|
else
|
|
{
|
|
val = _lastUGM3;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
|
|
bool AGS02MA::manualZeroCalibration(uint16_t value)
|
|
{
|
|
_buffer[0] = 0x00;
|
|
_buffer[1] = 0x0C;
|
|
_buffer[2] = (uint8_t) (value >> 8);
|
|
_buffer[3] = (uint8_t) (value & 0x00FF);
|
|
_buffer[4] = _CRC8(_buffer, 4);
|
|
return _writeRegister(AGS02MA_CALIBRATION);
|
|
}
|
|
|
|
|
|
bool AGS02MA::getZeroCalibrationData(AGS02MA::ZeroCalibrationData &data) {
|
|
if (!_readRegister(AGS02MA_CALIBRATION))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (_CRC8(_buffer, 5) != 0)
|
|
{
|
|
_error = AGS02MA_ERROR_CRC;
|
|
return false;
|
|
}
|
|
|
|
_error = AGS02MA_OK;
|
|
// Don't pollute the struct given to us, until we've handled all error cases.
|
|
data.status = _getDataMSB();
|
|
data.value = _getDataLSB();
|
|
return true;
|
|
}
|
|
|
|
|
|
int AGS02MA::lastError()
|
|
{
|
|
int e = _error;
|
|
_error = AGS02MA_OK; // reset error after read
|
|
return e;
|
|
}
|
|
|
|
|
|
bool AGS02MA::readRegister(uint8_t address, AGS02MA::RegisterData ®) {
|
|
if (!_readRegister(address))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_error = AGS02MA_OK;
|
|
// Don't pollute the struct given to us, until we've handled all error cases.
|
|
reg.data[0] = _buffer[0];
|
|
reg.data[1] = _buffer[1];
|
|
reg.data[2] = _buffer[2];
|
|
reg.data[3] = _buffer[3];
|
|
reg.crc = _buffer[4];
|
|
reg.crcValid = _CRC8(_buffer, 5) == 0;
|
|
return true;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
//
|
|
// PRIVATE
|
|
//
|
|
uint32_t AGS02MA::_readSensor()
|
|
{
|
|
_error = AGS02MA_ERROR_READ;
|
|
uint32_t value = 0;
|
|
if (_readRegister(AGS02MA_DATA))
|
|
{
|
|
_error = AGS02MA_OK;
|
|
_status = _buffer[0];
|
|
if (_status & 0x01)
|
|
{
|
|
_error = AGS02MA_ERROR_NOT_READY;
|
|
}
|
|
value = _buffer[1] * 65536UL;
|
|
value += _buffer[2] * 256;
|
|
value += _buffer[3];
|
|
if (_CRC8(_buffer, 5) != 0)
|
|
{
|
|
_error = AGS02MA_ERROR_CRC;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
|
|
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; // pre-scaler = 4
|
|
#else
|
|
_wire->setClock(AGS02MA_I2C_CLOCK);
|
|
#endif
|
|
|
|
_wire->beginTransmission(_address);
|
|
_wire->write(reg);
|
|
_error = _wire->endTransmission(true);
|
|
|
|
// TODO investigate async interface
|
|
delay(30);
|
|
|
|
if (_wire->requestFrom(_address, (uint8_t)5) != 5)
|
|
{
|
|
_error = AGS02MA_ERROR_READ;
|
|
#if defined (__AVR__)
|
|
TWSR = 0x00; // reset pre-scaler = 1
|
|
#endif
|
|
_wire->setClock(_I2CResetSpeed);
|
|
return false;
|
|
}
|
|
for (uint8_t i = 0; i < 5; i++)
|
|
{
|
|
_buffer[i] = _wire->read();
|
|
}
|
|
#if defined (__AVR__)
|
|
TWSR = 0x00; // reset pre-scaler = 1
|
|
#endif
|
|
_wire->setClock(_I2CResetSpeed);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool AGS02MA::_writeRegister(uint8_t reg)
|
|
{
|
|
while (millis() - _lastRegTime < 30) yield();
|
|
_lastRegTime = millis();
|
|
|
|
#if defined (__AVR__)
|
|
// TWBR = 255; // == 30.4 KHz with TWSR = 0x00
|
|
TWBR = 78; // == 25.0 KHZ
|
|
TWSR = 0x01; // pre-scaler = 4
|
|
#else
|
|
_wire->setClock(AGS02MA_I2C_CLOCK);
|
|
#endif
|
|
_wire->beginTransmission(_address);
|
|
_wire->write(reg);
|
|
for (uint8_t i = 0; i < 5; i++)
|
|
{
|
|
_wire->write(_buffer[i]);
|
|
}
|
|
_error = _wire->endTransmission(true);
|
|
#if defined (__AVR__)
|
|
TWSR = 0x00;
|
|
#endif
|
|
_wire->setClock(_I2CResetSpeed);
|
|
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
|
|
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;
|
|
}
|
|
|
|
|
|
uint8_t AGS02MA::_bin2bcd (uint8_t value)
|
|
{
|
|
return value + 6 * (value / 10);
|
|
}
|
|
|
|
|
|
// -- END OF FILE --
|
|
|