forked from airgradienthq/arduino
864 lines
21 KiB
C++
864 lines
21 KiB
C++
/*
|
|
Test.h - Test library for Wiring - implementation
|
|
Copyright (c) 2006 John Doe. All right reserved.
|
|
*/
|
|
|
|
|
|
// include this library's description file
|
|
#include "AirGradient.h"
|
|
|
|
// include description files for other libraries used (if any)
|
|
#include <SoftwareSerial.h>
|
|
#include "Arduino.h"
|
|
#include <Wire.h>
|
|
#include <math.h>
|
|
|
|
|
|
// Constructor /////////////////////////////////////////////////////////////////
|
|
// Function that handles the creation and setup of instances
|
|
|
|
const int MHZ14A = 14;
|
|
const int MHZ19B = 19; // this one we use for AQI whatever
|
|
|
|
const int MHZ14A_PREHEATING_TIME = 3 * 60 * 1000;
|
|
const int MHZ19B_PREHEATING_TIME = 3 * 60 * 1000;
|
|
|
|
const int MHZ14A_RESPONSE_TIME = 60 * 1000;
|
|
const int MHZ19B_RESPONSE_TIME = 120 * 1000;
|
|
|
|
const int STATUS_NO_RESPONSE = -2;
|
|
const int STATUS_CHECKSUM_MISMATCH = -3;
|
|
const int STATUS_INCOMPLETE = -4;
|
|
const int STATUS_NOT_READY = -5;
|
|
const int STATUS_PWM_NOT_CONFIGURED = -6;
|
|
const int STATUS_serial_MHZ19_NOT_CONFIGURED = -7;
|
|
|
|
unsigned long lastRequest = 0;
|
|
|
|
bool SerialConfigured = true;
|
|
bool PwmConfigured = true;
|
|
|
|
|
|
AirGradient::AirGradient(bool displayMsg,int baudRate)
|
|
{
|
|
_debugMsg = displayMsg;
|
|
Wire.begin();
|
|
Serial.begin(baudRate);
|
|
if (_debugMsg) {
|
|
Serial.println("AirGradiant Library instantiated successfully.");
|
|
}
|
|
}
|
|
|
|
// Public Methods //////////////////////////////////////////////////////////////
|
|
// Functions available in Wiring sketches, this library, and other libraries
|
|
|
|
|
|
void AirGradient::PMS_Init(){
|
|
if (_debugMsg) {
|
|
Serial.println("Initializing PMS...");
|
|
}
|
|
PMS_Init(D5,D6);
|
|
}
|
|
void AirGradient::PMS_Init(int rx_pin,int tx_pin){
|
|
PMS_Init(rx_pin,tx_pin,9600);
|
|
}
|
|
void AirGradient::PMS_Init(int rx_pin,int tx_pin,int baudRate){
|
|
_SoftSerial_PMS = new SoftwareSerial(rx_pin,tx_pin);
|
|
PMS(*_SoftSerial_PMS);
|
|
_SoftSerial_PMS->begin(baudRate);
|
|
|
|
if(getPM2() <= 0){
|
|
|
|
if (_debugMsg) {
|
|
Serial.println("PMS Sensor Failed to Initialize ");
|
|
}
|
|
else{
|
|
Serial.println("PMS Successfully Initialized. Heating up for 10s");
|
|
delay(10000);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* AirGradient::getPM2(){
|
|
if (getPM2_Raw()) {
|
|
int result_raw = getPM2_Raw();
|
|
sprintf(Char_PM2,"%d", result_raw);
|
|
return Char_PM2;
|
|
} else {
|
|
//Serial.println("no PMS data");
|
|
Char_PM2[0] = 'N';
|
|
Char_PM2[1] = 'U';
|
|
Char_PM2[2] = 'L';
|
|
Char_PM2[3] = 'L';
|
|
return Char_PM2;
|
|
}
|
|
}
|
|
|
|
int AirGradient::getPM2_Raw(){
|
|
int pm02;
|
|
DATA data;
|
|
requestRead();
|
|
if (readUntil(data)) {
|
|
pm02 = data.PM_AE_UG_2_5;
|
|
return pm02;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
// Private Methods /////////////////////////////////////////////////////////////
|
|
// Functions only available to other functions in this library
|
|
|
|
//START PMS FUNCTIONS //
|
|
|
|
void AirGradient::PMS(Stream& stream)
|
|
{
|
|
this->_stream = &stream;
|
|
}
|
|
|
|
// Standby mode. For low power consumption and prolong the life of the sensor.
|
|
void AirGradient::sleep()
|
|
{
|
|
uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x00, 0x01, 0x73 };
|
|
_stream->write(command, sizeof(command));
|
|
}
|
|
|
|
// Operating mode. Stable data should be got at least 30 seconds after the sensor wakeup from the sleep mode because of the fan's performance.
|
|
void AirGradient::wakeUp()
|
|
{
|
|
uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x74 };
|
|
_stream->write(command, sizeof(command));
|
|
}
|
|
|
|
// Active mode. Default mode after power up. In this mode sensor would send serial data to the host automatically.
|
|
void AirGradient::activeMode()
|
|
{
|
|
|
|
uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 };
|
|
_stream->write(command, sizeof(command));
|
|
_mode = MODE_ACTIVE;
|
|
}
|
|
|
|
// Passive mode. In this mode sensor would send serial data to the host only for request.
|
|
void AirGradient::passiveMode()
|
|
{
|
|
uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70 };
|
|
_stream->write(command, sizeof(command));
|
|
_mode = MODE_PASSIVE;
|
|
}
|
|
|
|
// Request read in Passive Mode.
|
|
void AirGradient::requestRead()
|
|
{
|
|
if (_mode == MODE_PASSIVE)
|
|
{
|
|
uint8_t command[] = { 0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71 };
|
|
_stream->write(command, sizeof(command));
|
|
}
|
|
}
|
|
|
|
// Non-blocking function for parse response.
|
|
bool AirGradient::read_PMS(DATA& data)
|
|
{
|
|
_data = &data;
|
|
loop();
|
|
|
|
return _PMSstatus == STATUS_OK;
|
|
}
|
|
|
|
// Blocking function for parse response. Default timeout is 1s.
|
|
bool AirGradient::readUntil(DATA& data, uint16_t timeout)
|
|
{
|
|
_data = &data;
|
|
uint32_t start = millis();
|
|
do
|
|
{
|
|
loop();
|
|
if (_PMSstatus == STATUS_OK) break;
|
|
} while (millis() - start < timeout);
|
|
|
|
return _PMSstatus == STATUS_OK;
|
|
}
|
|
|
|
void AirGradient::loop()
|
|
{
|
|
_PMSstatus = STATUS_WAITING;
|
|
if (_stream->available())
|
|
{
|
|
uint8_t ch = _stream->read();
|
|
|
|
switch (_index)
|
|
{
|
|
case 0:
|
|
if (ch != 0x42)
|
|
{
|
|
return;
|
|
}
|
|
_calculatedChecksum = ch;
|
|
break;
|
|
|
|
case 1:
|
|
if (ch != 0x4D)
|
|
{
|
|
_index = 0;
|
|
return;
|
|
}
|
|
_calculatedChecksum += ch;
|
|
break;
|
|
|
|
case 2:
|
|
_calculatedChecksum += ch;
|
|
_frameLen = ch << 8;
|
|
break;
|
|
|
|
case 3:
|
|
_frameLen |= ch;
|
|
// Unsupported sensor, different frame length, transmission error e.t.c.
|
|
if (_frameLen != 2 * 9 + 2 && _frameLen != 2 * 13 + 2)
|
|
{
|
|
_index = 0;
|
|
return;
|
|
}
|
|
_calculatedChecksum += ch;
|
|
break;
|
|
|
|
default:
|
|
if (_index == _frameLen + 2)
|
|
{
|
|
_checksum = ch << 8;
|
|
}
|
|
else if (_index == _frameLen + 2 + 1)
|
|
{
|
|
_checksum |= ch;
|
|
|
|
if (_calculatedChecksum == _checksum)
|
|
{
|
|
_PMSstatus = STATUS_OK;
|
|
|
|
// Standard Particles, CF=1.
|
|
_data->PM_SP_UG_1_0 = makeWord(_payload[0], _payload[1]);
|
|
_data->PM_SP_UG_2_5 = makeWord(_payload[2], _payload[3]);
|
|
_data->PM_SP_UG_10_0 = makeWord(_payload[4], _payload[5]);
|
|
|
|
// Atmospheric Environment.
|
|
_data->PM_AE_UG_1_0 = makeWord(_payload[6], _payload[7]);
|
|
_data->PM_AE_UG_2_5 = makeWord(_payload[8], _payload[9]);
|
|
_data->PM_AE_UG_10_0 = makeWord(_payload[10], _payload[11]);
|
|
}
|
|
|
|
_index = 0;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
_calculatedChecksum += ch;
|
|
uint8_t payloadIndex = _index - 4;
|
|
|
|
// Payload is common to all sensors (first 2x6 bytes).
|
|
if (payloadIndex < sizeof(_payload))
|
|
{
|
|
_payload[payloadIndex] = ch;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
_index++;
|
|
}
|
|
}
|
|
|
|
//END PMS FUNCTIONS //
|
|
|
|
//START TMP_RH FUNCTIONS//
|
|
|
|
TMP_RH_ErrorCode AirGradient::TMP_RH_Init(uint8_t address) {
|
|
if (_debugMsg) {
|
|
Serial.println("Initializing TMP_RH...");
|
|
}
|
|
TMP_RH_ErrorCode error = SHT3XD_NO_ERROR;
|
|
_address = address;
|
|
periodicStart(SHT3XD_REPEATABILITY_HIGH, SHT3XD_FREQUENCY_10HZ);
|
|
return error;
|
|
}
|
|
|
|
TMP_RH_ErrorCode AirGradient::reset()
|
|
{
|
|
return softReset();
|
|
}
|
|
|
|
TMP_RH AirGradient::periodicFetchData() //
|
|
{
|
|
TMP_RH result;
|
|
TMP_RH_ErrorCode error = writeCommand(SHT3XD_CMD_FETCH_DATA);
|
|
if (error == SHT3XD_NO_ERROR){
|
|
result = readTemperatureAndHumidity();
|
|
sprintf(result.t_char,"%d", result.t);
|
|
sprintf(result.rh_char,"%f", result.rh);
|
|
|
|
return result;
|
|
}
|
|
else
|
|
returnError(error);
|
|
}
|
|
|
|
TMP_RH_ErrorCode AirGradient::periodicStop() {
|
|
return writeCommand(SHT3XD_CMD_STOP_PERIODIC);
|
|
}
|
|
|
|
TMP_RH_ErrorCode AirGradient::periodicStart(TMP_RH_Repeatability repeatability, TMP_RH_Frequency frequency) //
|
|
{
|
|
TMP_RH_ErrorCode error;
|
|
|
|
switch (repeatability)
|
|
{
|
|
case SHT3XD_REPEATABILITY_LOW:
|
|
switch (frequency)
|
|
{
|
|
case SHT3XD_FREQUENCY_HZ5:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_HALF_L);
|
|
break;
|
|
case SHT3XD_FREQUENCY_1HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_1_L);
|
|
break;
|
|
case SHT3XD_FREQUENCY_2HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_2_L);
|
|
break;
|
|
case SHT3XD_FREQUENCY_4HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_4_L);
|
|
break;
|
|
case SHT3XD_FREQUENCY_10HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_10_L);
|
|
break;
|
|
default:
|
|
error = SHT3XD_PARAM_WRONG_FREQUENCY;
|
|
break;
|
|
}
|
|
break;
|
|
case SHT3XD_REPEATABILITY_MEDIUM:
|
|
switch (frequency)
|
|
{
|
|
case SHT3XD_FREQUENCY_HZ5:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_HALF_M);
|
|
break;
|
|
case SHT3XD_FREQUENCY_1HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_1_M);
|
|
break;
|
|
case SHT3XD_FREQUENCY_2HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_2_M);
|
|
break;
|
|
case SHT3XD_FREQUENCY_4HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_4_M);
|
|
break;
|
|
case SHT3XD_FREQUENCY_10HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_10_M);
|
|
break;
|
|
default:
|
|
error = SHT3XD_PARAM_WRONG_FREQUENCY;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SHT3XD_REPEATABILITY_HIGH:
|
|
switch (frequency)
|
|
{
|
|
case SHT3XD_FREQUENCY_HZ5:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_HALF_H);
|
|
break;
|
|
case SHT3XD_FREQUENCY_1HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_1_H);
|
|
break;
|
|
case SHT3XD_FREQUENCY_2HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_2_H);
|
|
break;
|
|
case SHT3XD_FREQUENCY_4HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_4_H);
|
|
break;
|
|
case SHT3XD_FREQUENCY_10HZ:
|
|
error = writeCommand(SHT3XD_CMD_PERIODIC_10_H);
|
|
break;
|
|
default:
|
|
error = SHT3XD_PARAM_WRONG_FREQUENCY;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
error = SHT3XD_PARAM_WRONG_REPEATABILITY;
|
|
break;
|
|
}
|
|
|
|
delay(100);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
TMP_RH_ErrorCode AirGradient::writeCommand(TMP_RH_Commands command)
|
|
{
|
|
Wire.beginTransmission(_address);
|
|
Wire.write(command >> 8);
|
|
Wire.write(command & 0xFF);
|
|
return (TMP_RH_ErrorCode)(-10 * Wire.endTransmission());
|
|
}
|
|
|
|
TMP_RH_ErrorCode AirGradient::softReset() {
|
|
return writeCommand(SHT3XD_CMD_SOFT_RESET);
|
|
}
|
|
|
|
|
|
uint32_t AirGradient::readSerialNumber()
|
|
{
|
|
uint32_t result = SHT3XD_NO_ERROR;
|
|
uint16_t buf[2];
|
|
|
|
if (writeCommand(SHT3XD_CMD_READ_SERIAL_NUMBER) == SHT3XD_NO_ERROR) {
|
|
if (read_TMP_RH(buf, 2) == SHT3XD_NO_ERROR) {
|
|
result = (buf[0] << 16) | buf[1];
|
|
}
|
|
}
|
|
else if(writeCommand(SHT3XD_CMD_READ_SERIAL_NUMBER) != SHT3XD_NO_ERROR){
|
|
if (_debugMsg) {
|
|
Serial.println("TMP_RH Failed to Initialize.");
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
}
|
|
uint32_t AirGradient::testTMP_RH()
|
|
{
|
|
uint32_t result = SHT3XD_NO_ERROR;
|
|
uint16_t buf[2];
|
|
|
|
if (writeCommand(SHT3XD_CMD_READ_SERIAL_NUMBER) == SHT3XD_NO_ERROR) {
|
|
if (read_TMP_RH(buf, 2) == SHT3XD_NO_ERROR) {
|
|
result = (buf[0] << 16) | buf[1];
|
|
}
|
|
if (_debugMsg) {
|
|
Serial.print("TMP_RH successfully initialized with serial number: ");
|
|
Serial.println(result);
|
|
}
|
|
|
|
}
|
|
else if(writeCommand(SHT3XD_CMD_READ_SERIAL_NUMBER) != SHT3XD_NO_ERROR){
|
|
if (_debugMsg) {
|
|
Serial.println("TMP_RH Failed to Initialize.");
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
TMP_RH_ErrorCode AirGradient::clearAll() {
|
|
return writeCommand(SHT3XD_CMD_CLEAR_STATUS);
|
|
}
|
|
|
|
|
|
TMP_RH AirGradient::readTemperatureAndHumidity()//
|
|
{
|
|
TMP_RH result;
|
|
|
|
result.t = 0;
|
|
result.rh = 0;
|
|
|
|
TMP_RH_ErrorCode error;
|
|
uint16_t buf[2];
|
|
|
|
if (error == SHT3XD_NO_ERROR)
|
|
error = read_TMP_RH(buf, 2);
|
|
|
|
if (error == SHT3XD_NO_ERROR) {
|
|
result.t = calculateTemperature(buf[0]);
|
|
result.rh = calculateHumidity(buf[1]);
|
|
}
|
|
result.error = error;
|
|
|
|
return result;
|
|
}
|
|
|
|
TMP_RH_ErrorCode AirGradient::read_TMP_RH(uint16_t* data, uint8_t numOfPair)//
|
|
{
|
|
uint8_t buf[2];
|
|
uint8_t checksum;
|
|
|
|
const uint8_t numOfBytes = numOfPair * 3;
|
|
Wire.requestFrom(_address, numOfBytes);
|
|
|
|
int counter = 0;
|
|
|
|
for (counter = 0; counter < numOfPair; counter++) {
|
|
Wire.readBytes(buf, (uint8_t)2);
|
|
checksum = Wire.read();
|
|
|
|
if (checkCrc(buf, checksum) != 0)
|
|
return SHT3XD_CRC_ERROR;
|
|
|
|
data[counter] = (buf[0] << 8) | buf[1];
|
|
}
|
|
|
|
return SHT3XD_NO_ERROR;
|
|
}
|
|
|
|
|
|
uint8_t AirGradient::checkCrc(uint8_t data[], uint8_t checksum)//
|
|
{
|
|
return calculateCrc(data) != checksum;
|
|
}
|
|
|
|
float AirGradient::calculateTemperature(uint16_t rawValue)//
|
|
{
|
|
float value = 175.0f * (float)rawValue / 65535.0f - 45.0f;
|
|
return round(value*10)/10;
|
|
}
|
|
|
|
|
|
float AirGradient::calculateHumidity(uint16_t rawValue)//
|
|
{
|
|
return 100.0f * rawValue / 65535.0f;
|
|
}
|
|
|
|
uint8_t AirGradient::calculateCrc(uint8_t data[])
|
|
{
|
|
uint8_t bit;
|
|
uint8_t crc = 0xFF;
|
|
uint8_t dataCounter = 0;
|
|
|
|
for (; dataCounter < 2; dataCounter++)
|
|
{
|
|
crc ^= (data[dataCounter]);
|
|
for (bit = 8; bit > 0; --bit)
|
|
{
|
|
if (crc & 0x80)
|
|
crc = (crc << 1) ^ 0x131;
|
|
else
|
|
crc = (crc << 1);
|
|
}
|
|
}
|
|
|
|
return crc;
|
|
}
|
|
|
|
TMP_RH AirGradient::returnError(TMP_RH_ErrorCode error) {
|
|
TMP_RH result;
|
|
result.t = NULL;
|
|
result.rh = NULL;
|
|
|
|
result.t_char[0] = 'N';
|
|
result.t_char[1] = 'U';
|
|
result.t_char[2] = 'L';
|
|
result.t_char[3] = 'L';
|
|
|
|
result.rh_char[0] = 'N';
|
|
result.rh_char[1] = 'U';
|
|
result.rh_char[2] = 'L';
|
|
result.rh_char[3] = 'L';
|
|
|
|
result.error = error;
|
|
return result;
|
|
}
|
|
|
|
//END TMP_RH FUNCTIONS //
|
|
|
|
//START CO2 FUNCTIONS //
|
|
void AirGradient::CO2_Init(){
|
|
CO2_Init(D4,D3);
|
|
}
|
|
void AirGradient::CO2_Init(int rx_pin,int tx_pin){
|
|
CO2_Init(rx_pin,tx_pin,9600);
|
|
|
|
}
|
|
void AirGradient::CO2_Init(int rx_pin,int tx_pin,int baudRate){
|
|
if (_debugMsg) {
|
|
Serial.println("Initializing CO2...");
|
|
}
|
|
_SoftSerial_CO2 = new SoftwareSerial(rx_pin,tx_pin);
|
|
_SoftSerial_CO2->begin(baudRate);
|
|
|
|
if(getCO2_Raw() == -1){
|
|
if (_debugMsg) {
|
|
Serial.println("CO2 Sensor Failed to Initialize ");
|
|
}
|
|
}
|
|
else{
|
|
Serial.println("CO2 Successfully Initialized. Heating up for 10s");
|
|
delay(10000);
|
|
}
|
|
}
|
|
const char* AirGradient::getCO2(int retryLimit) {
|
|
int ctr = 0;
|
|
int result_CO2 = getCO2_Raw();
|
|
while(result_CO2 == -1){
|
|
result_CO2 = getCO2_Raw();
|
|
if((ctr == retryLimit) || (result_CO2 == -1)){
|
|
Char_CO2[0] = 'N';
|
|
Char_CO2[1] = 'U';
|
|
Char_CO2[2] = 'L';
|
|
Char_CO2[3] = 'L';
|
|
return Char_CO2;
|
|
}
|
|
ctr++;
|
|
}
|
|
sprintf(Char_CO2,"%d", result_CO2);
|
|
return Char_CO2;
|
|
}
|
|
int AirGradient::getCO2_Raw(){
|
|
int retry = 0;
|
|
CO2_READ_RESULT result;
|
|
const byte CO2Command[] = {0xFE, 0X44, 0X00, 0X08, 0X02, 0X9F, 0X25};
|
|
byte CO2Response[] = {0,0,0,0,0,0,0};
|
|
|
|
while(!(_SoftSerial_CO2->available())) {
|
|
retry++;
|
|
// keep sending request until we start to get a response
|
|
_SoftSerial_CO2->write(CO2Command, 7);
|
|
delay(50);
|
|
if (retry > 10) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int timeout = 0;
|
|
|
|
while (_SoftSerial_CO2->available() < 7) {
|
|
timeout++;
|
|
if (timeout > 10) {
|
|
while(_SoftSerial_CO2->available())
|
|
_SoftSerial_CO2->read();
|
|
break;
|
|
}
|
|
delay(50);
|
|
}
|
|
|
|
for (int i=0; i < 7; i++) {
|
|
int byte = _SoftSerial_CO2->read();
|
|
if (byte == -1) {
|
|
result.success = false;
|
|
return -1;
|
|
}
|
|
CO2Response[i] = byte;
|
|
}
|
|
int valMultiplier = 1;
|
|
int high = CO2Response[3];
|
|
int low = CO2Response[4];
|
|
unsigned long val = high*256 + low;
|
|
|
|
return val;
|
|
}
|
|
|
|
//END CO2 FUNCTIONS //
|
|
|
|
//START MHZ19 FUNCTIONS //
|
|
void AirGradient::MHZ19_Init(uint8_t type) {
|
|
MHZ19_Init(9,10,type);
|
|
}
|
|
void AirGradient::MHZ19_Init(int rx_pin,int tx_pin, uint8_t type) {
|
|
MHZ19_Init(rx_pin,tx_pin,9600,type);
|
|
}
|
|
void AirGradient::MHZ19_Init(int rx_pin,int tx_pin, int baudRate, uint8_t type) {
|
|
if (_debugMsg) {
|
|
Serial.println("Initializing MHZ19...");
|
|
}
|
|
_SoftSerial_MHZ19 = new SoftwareSerial(rx_pin,tx_pin);
|
|
_SoftSerial_MHZ19->begin(baudRate);
|
|
|
|
if(readMHZ19() == -1){
|
|
if (_debugMsg) {
|
|
Serial.println("MHZ19 Sensor Failed to Initialize ");
|
|
}
|
|
}
|
|
else{
|
|
Serial.println("MHZ19 Successfully Initialized. Heating up for 10s");
|
|
delay(10000);
|
|
}
|
|
|
|
_type_MHZ19 = type;
|
|
|
|
PwmConfigured = false;
|
|
}
|
|
|
|
/**
|
|
* Enables or disables the debug mode (more logging).
|
|
*/
|
|
void AirGradient::setDebug_MHZ19(bool enable) {
|
|
debug_MHZ19 = enable;
|
|
if (debug_MHZ19) {
|
|
Serial.println(F("MHZ: debug mode ENABLED"));
|
|
} else {
|
|
Serial.println(F("MHZ: debug mode DISABLED"));
|
|
}
|
|
}
|
|
|
|
bool AirGradient::isPreHeating_MHZ19() {
|
|
if (_type_MHZ19 == MHZ14A) {
|
|
return millis() < (MHZ14A_PREHEATING_TIME);
|
|
} else if (_type_MHZ19 == MHZ19B) {
|
|
return millis() < (MHZ19B_PREHEATING_TIME);
|
|
} else {
|
|
Serial.println(F("MHZ::isPreheating_MHZ19() => UNKNOWN SENSOR"));
|
|
return false;
|
|
}//
|
|
}
|
|
|
|
bool AirGradient::isReady_MHZ19() {
|
|
if (isPreHeating_MHZ19()) return false;
|
|
if (_type_MHZ19 == MHZ14A)
|
|
return lastRequest < millis() - MHZ14A_RESPONSE_TIME;
|
|
else if (_type_MHZ19 == MHZ19B)
|
|
return lastRequest < millis() - MHZ19B_RESPONSE_TIME;
|
|
else {
|
|
Serial.print(F("MHZ::isReady_MHZ19() => UNKNOWN SENSOR \""));
|
|
Serial.print(_type_MHZ19);
|
|
Serial.println(F("\""));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
int AirGradient::readMHZ19() {
|
|
|
|
int firstRead = readInternal_MHZ19();
|
|
int secondRead = readInternal_MHZ19();
|
|
|
|
if (abs(secondRead - firstRead) > 50) {
|
|
// we arrive here sometimes when the CO2 sensor is not connected
|
|
// could possibly also be fixed with a pull-up resistor on Rx but if we forget this then ...
|
|
Serial.println("MHZ::read() inconsistent values");
|
|
return -1;
|
|
}
|
|
|
|
Serial.println("MHZ::read(1) " + String(firstRead));
|
|
Serial.println("MHZ::read(2) " + String(secondRead));
|
|
|
|
// TODO: return average?
|
|
return secondRead;
|
|
}
|
|
|
|
int AirGradient::readInternal_MHZ19() {
|
|
if (!SerialConfigured) {
|
|
if (debug_MHZ19) Serial.println(F("-- serial is not configured"));
|
|
return STATUS_serial_MHZ19_NOT_CONFIGURED;
|
|
}
|
|
// if (!isReady_MHZ19()) return STATUS_NOT_READY;
|
|
if (debug_MHZ19) Serial.println(F("-- read CO2 uart ---"));
|
|
byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
|
|
unsigned char response[9]; // for answer
|
|
|
|
if (debug_MHZ19) Serial.print(F(" >> Sending CO2 request"));
|
|
_SoftSerial_MHZ19->write(cmd, 9); // request PPM CO2
|
|
lastRequest = millis();
|
|
|
|
// clear the buffer
|
|
memset(response, 0, 9);
|
|
|
|
int waited = 0;
|
|
while (_SoftSerial_MHZ19->available() == 0) {
|
|
if (debug_MHZ19) Serial.print(".");
|
|
delay(100); // wait a short moment to avoid false reading
|
|
if (waited++ > 10) {
|
|
if (debug_MHZ19) Serial.println(F("No response after 10 seconds"));
|
|
_SoftSerial_MHZ19->flush();
|
|
return STATUS_NO_RESPONSE;
|
|
}
|
|
}
|
|
if (debug_MHZ19) Serial.println();
|
|
|
|
// The serial stream can get out of sync. The response starts with 0xff, try
|
|
// to resync.
|
|
// TODO: I think this might be wrong any only happens during initialization?
|
|
boolean skip = false;
|
|
while (_SoftSerial_MHZ19->available() > 0 && (unsigned char)_SoftSerial_MHZ19->peek() != 0xFF) {
|
|
if (!skip) {
|
|
Serial.print(F("MHZ: - skipping unexpected readings:"));
|
|
skip = true;
|
|
}
|
|
Serial.print(" ");
|
|
Serial.print(_SoftSerial_MHZ19->peek(), HEX);
|
|
_SoftSerial_MHZ19->read();
|
|
}
|
|
if (skip) Serial.println();
|
|
|
|
if (_SoftSerial_MHZ19->available() > 0) {
|
|
int count = _SoftSerial_MHZ19->readBytes(response, 9);
|
|
if (count < 9) {
|
|
_SoftSerial_MHZ19->flush();
|
|
return STATUS_INCOMPLETE;
|
|
}
|
|
} else {
|
|
_SoftSerial_MHZ19->flush();
|
|
return STATUS_INCOMPLETE;
|
|
}
|
|
|
|
if (debug_MHZ19) {
|
|
// print out the response in hexa
|
|
Serial.print(F(" << "));
|
|
for (int i = 0; i < 9; i++) {
|
|
Serial.print(response[i], HEX);
|
|
Serial.print(F(" "));
|
|
}
|
|
Serial.println(F(""));
|
|
}
|
|
|
|
// checksum
|
|
byte check = getCheckSum_MHZ19(response);
|
|
if (response[8] != check) {
|
|
Serial.println(F("MHZ: Checksum not OK!"));
|
|
Serial.print(F("MHZ: Received: "));
|
|
Serial.println(response[8], HEX);
|
|
Serial.print(F("MHZ: Should be: "));
|
|
Serial.println(check, HEX);
|
|
temperature_MHZ19 = STATUS_CHECKSUM_MISMATCH;
|
|
_SoftSerial_MHZ19->flush();
|
|
return STATUS_CHECKSUM_MISMATCH;
|
|
}
|
|
|
|
int ppm_uart = 256 * (unsigned int)response[2] + (unsigned int)response[3];
|
|
|
|
temperature_MHZ19 = response[4] - 44; // - 40;
|
|
|
|
byte status = response[5];
|
|
if (debug_MHZ19) {
|
|
Serial.print(F(" # PPM UART: "));
|
|
Serial.println(ppm_uart);
|
|
Serial.print(F(" # temperature_MHZ19? "));
|
|
Serial.println(temperature_MHZ19);
|
|
}
|
|
|
|
// Is always 0 for version 14a and 19b
|
|
// Version 19a?: status != 0x40
|
|
if (debug_MHZ19 && status != 0) {
|
|
Serial.print(F(" ! Status maybe not OK ! "));
|
|
Serial.println(status, HEX);
|
|
} else if (debug_MHZ19) {
|
|
Serial.print(F(" Status OK: "));
|
|
Serial.println(status, HEX);
|
|
}
|
|
|
|
_SoftSerial_MHZ19->flush();
|
|
return ppm_uart;
|
|
}
|
|
|
|
|
|
|
|
uint8_t AirGradient::getCheckSum_MHZ19(unsigned char* packet) {
|
|
if (!SerialConfigured) {
|
|
if (debug_MHZ19) Serial.println(F("-- serial is not configured"));
|
|
return STATUS_serial_MHZ19_NOT_CONFIGURED;
|
|
}
|
|
if (debug_MHZ19) Serial.println(F(" getCheckSum_MHZ19()"));
|
|
byte i;
|
|
unsigned char checksum = 0;
|
|
for (i = 1; i < 8; i++) {
|
|
checksum += packet[i];
|
|
}
|
|
checksum = 0xff - checksum;
|
|
checksum += 1;
|
|
return checksum;
|
|
}
|
|
|
|
//END MHZ19 FUNCTIONS //
|