From e052f62dd3b7d4363a8a08b50b6d46ee6f936a2b Mon Sep 17 00:00:00 2001 From: Cristhian Macoh Musada Date: Fri, 24 Jul 2020 21:52:28 +0800 Subject: [PATCH] v1.0.0 --- AirGradient/.DS_Store | Bin 0 -> 6148 bytes AirGradient/AirGradient.cpp | 833 ++++++++++++++++++ AirGradient/AirGradient.h | 300 +++++++ AirGradient/LICENSE.md | 21 + AirGradient/README.md | 8 + AirGradient/examples/.DS_Store | Bin 0 -> 6148 bytes AirGradient/examples/C02_PM_SHT/.DS_Store | Bin 0 -> 6148 bytes .../examples/C02_PM_SHT/C02_PM_SHT.ino | 27 + .../C02_PM_SHT_OLED/C02_PM_SHT_OLED.ino | 41 + .../C02_PM_SHT_OLED_WIFI.ino | 77 ++ AirGradient/examples/C02_Test/C02_Test.ino | 16 + .../examples/MHZ19_Test/MHZ19_Test.ino | 16 + AirGradient/examples/PM2_Test/PM2_Test.ino | 16 + .../examples/TMP_RH_Test/TMP_RH_Test.ino | 16 + AirGradient/keywords.txt | 65 ++ AirGradient/library.properties | 9 + AirGradient/readme.txt | 39 + 17 files changed, 1484 insertions(+) create mode 100644 AirGradient/.DS_Store create mode 100644 AirGradient/AirGradient.cpp create mode 100644 AirGradient/AirGradient.h create mode 100644 AirGradient/LICENSE.md create mode 100644 AirGradient/README.md create mode 100644 AirGradient/examples/.DS_Store create mode 100644 AirGradient/examples/C02_PM_SHT/.DS_Store create mode 100644 AirGradient/examples/C02_PM_SHT/C02_PM_SHT.ino create mode 100644 AirGradient/examples/C02_PM_SHT_OLED/C02_PM_SHT_OLED.ino create mode 100644 AirGradient/examples/C02_PM_SHT_OLED_WIFI/C02_PM_SHT_OLED_WIFI.ino create mode 100644 AirGradient/examples/C02_Test/C02_Test.ino create mode 100644 AirGradient/examples/MHZ19_Test/MHZ19_Test.ino create mode 100644 AirGradient/examples/PM2_Test/PM2_Test.ino create mode 100644 AirGradient/examples/TMP_RH_Test/TMP_RH_Test.ino create mode 100644 AirGradient/keywords.txt create mode 100644 AirGradient/library.properties create mode 100644 AirGradient/readme.txt diff --git a/AirGradient/.DS_Store b/AirGradient/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d98352b4e543a45fbdb006a4b075e3f3a8a549c0 GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8z3~dZp0Z1N%F(jFwB8(vOz-AOM45$%WMg|6O7l4t0fdv*GjNpC%14s^}6-0xyf@qLd z21XDIYz9~>10z%`Be)v^(g*62fM~FG21c;WAU;?-10&dG1_nlmb_PbM&5Y0<3L`{2 z10zH`10&dWnCnLA(GVC70ayqyLl^>}`rnm-0ayPYqH2^J4S~@R7?vTx$l?<0;smag zvHK5H*MjQP1gM$=P-TpudKn@HQUa>l!PPMnWKdB8sthC!(hj1*)iEOj1GqLHZ4AId MXp|le0s4jj0HHAzq5uE@ literal 0 HcmV?d00001 diff --git a/AirGradient/AirGradient.cpp b/AirGradient/AirGradient.cpp new file mode 100644 index 0000000..72b9421 --- /dev/null +++ b/AirGradient/AirGradient.cpp @@ -0,0 +1,833 @@ +/* + 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 +#include "Arduino.h" +#include + + +// 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(5,6); +} +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); + } + } + +} + + + +int AirGradient::getPM2(){ +int pm02; +DATA data; + requestRead(); + if (readUntil(data)) { + pm02 = data.PM_AE_UG_2_5; + return pm02; + } else { + //Serial.println("no PMS data"); + return NULL; + } +} + + +// 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// +uint32_t AirGradient::getTemp(){ + TMP_RH result = periodicFetchData(); + return result.t; +} +int AirGradient::getRhum(){ + TMP_RH result = periodicFetchData(); + return result.rh; + +} + +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_ErrorCode error = writeCommand(SHT3XD_CMD_FETCH_DATA); + if (error == SHT3XD_NO_ERROR) + return readTemperatureAndHumidity(); + 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)// +{ + return 175.0f * (float)rawValue / 65535.0f - 45.0f; +} + + +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.error = error; + return result; +} + +//END TMP_RH FUNCTIONS // + +//START C02 FUNCTIONS // +void AirGradient::C02_Init(){ + C02_Init(4,3); +} +void AirGradient::C02_Init(int rx_pin,int tx_pin){ + C02_Init(rx_pin,tx_pin,9600); + +} +void AirGradient::C02_Init(int rx_pin,int tx_pin,int baudRate){ + if (_debugMsg) { + Serial.println("Initializing C02..."); + } + _SoftSerial_C02 = new SoftwareSerial(rx_pin,tx_pin); + _SoftSerial_C02->begin(baudRate); + + if(getC02() == -1){ + if (_debugMsg) { + Serial.println("C02 Sensor Failed to Initialize "); + } + } + else{ + Serial.println("C02 Successfully Initialized. Heating up for 10s"); + delay(10000); + } +} +int AirGradient::getC02(int retryLimit) { + int ctr = 0; + int result_c02 = 0; + while(result_c02 == -1){ + result_c02 = get_C02_values(); + if(ctr == retryLimit){ + return NULL; + } + ctr++; + } + return result_c02; +} +int AirGradient::get_C02_values(){ + int retry = 0; + CO2_READ_RESULT result; + const byte C02Command[] = {0xFE, 0X44, 0X00, 0X08, 0X02, 0X9F, 0X25}; + byte C02Response[] = {0,0,0,0,0,0,0}; + + while(!(_SoftSerial_C02->available())) { + retry++; + // keep sending request until we start to get a response + _SoftSerial_C02->write(C02Command, 7); + delay(50); + if (retry > 10) { + return -1; + } + } + + int timeout = 0; + + while (_SoftSerial_C02->available() < 7) { + timeout++; + if (timeout > 10) { + while(_SoftSerial_C02->available()) + _SoftSerial_C02->read(); + break; + } + delay(50); + } + + for (int i=0; i < 7; i++) { + int byte = _SoftSerial_C02->read(); + if (byte == -1) { + result.success = false; + return -1; + } + C02Response[i] = byte; + } + int valMultiplier = 1; + int high = C02Response[3]; + int low = C02Response[4]; + unsigned long val = high*256 + low; + + return val; +} + +//END C02 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(getC02() == -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; +} + + + +byte AirGradient::getCheckSum_MHZ19(byte* 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 // diff --git a/AirGradient/AirGradient.h b/AirGradient/AirGradient.h new file mode 100644 index 0000000..50870c1 --- /dev/null +++ b/AirGradient/AirGradient.h @@ -0,0 +1,300 @@ +/* + Test.h - Test library for Wiring - description + Copyright (c) 2006 John Doe. All right reserved. +*/ + +// ensure this library description is only included once +#ifndef AirGradient_h +#define AirGradient_h + +#include +#include +#include "Stream.h" +//MHZ19 CONSTANTS START +// types of sensors. +extern const int MHZ14A; +extern const int MHZ19B; + +// status codes +extern const int STATUS_NO_RESPONSE; +extern const int STATUS_CHECKSUM_MISMATCH; +extern const int STATUS_INCOMPLETE; +extern const int STATUS_NOT_READY; +//MHZ19 CONSTANTS END + +//ENUMS AND STRUCT FOR TMP_RH START +typedef enum { + SHT3XD_CMD_READ_SERIAL_NUMBER = 0x3780, + + SHT3XD_CMD_READ_STATUS = 0xF32D, + SHT3XD_CMD_CLEAR_STATUS = 0x3041, + + SHT3XD_CMD_HEATER_ENABLE = 0x306D, + SHT3XD_CMD_HEATER_DISABLE = 0x3066, + + SHT3XD_CMD_SOFT_RESET = 0x30A2, + + SHT3XD_CMD_CLOCK_STRETCH_H = 0x2C06, + SHT3XD_CMD_CLOCK_STRETCH_M = 0x2C0D, + SHT3XD_CMD_CLOCK_STRETCH_L = 0x2C10, + + SHT3XD_CMD_POLLING_H = 0x2400, + SHT3XD_CMD_POLLING_M = 0x240B, + SHT3XD_CMD_POLLING_L = 0x2416, + + SHT3XD_CMD_ART = 0x2B32, + + SHT3XD_CMD_PERIODIC_HALF_H = 0x2032, + SHT3XD_CMD_PERIODIC_HALF_M = 0x2024, + SHT3XD_CMD_PERIODIC_HALF_L = 0x202F, + SHT3XD_CMD_PERIODIC_1_H = 0x2130, + SHT3XD_CMD_PERIODIC_1_M = 0x2126, + SHT3XD_CMD_PERIODIC_1_L = 0x212D, + SHT3XD_CMD_PERIODIC_2_H = 0x2236, + SHT3XD_CMD_PERIODIC_2_M = 0x2220, + SHT3XD_CMD_PERIODIC_2_L = 0x222B, + SHT3XD_CMD_PERIODIC_4_H = 0x2334, + SHT3XD_CMD_PERIODIC_4_M = 0x2322, + SHT3XD_CMD_PERIODIC_4_L = 0x2329, + SHT3XD_CMD_PERIODIC_10_H = 0x2737, + SHT3XD_CMD_PERIODIC_10_M = 0x2721, + SHT3XD_CMD_PERIODIC_10_L = 0x272A, + + SHT3XD_CMD_FETCH_DATA = 0xE000, + SHT3XD_CMD_STOP_PERIODIC = 0x3093, + + SHT3XD_CMD_READ_ALR_LIMIT_LS = 0xE102, + SHT3XD_CMD_READ_ALR_LIMIT_LC = 0xE109, + SHT3XD_CMD_READ_ALR_LIMIT_HS = 0xE11F, + SHT3XD_CMD_READ_ALR_LIMIT_HC = 0xE114, + + SHT3XD_CMD_WRITE_ALR_LIMIT_HS = 0x611D, + SHT3XD_CMD_WRITE_ALR_LIMIT_HC = 0x6116, + SHT3XD_CMD_WRITE_ALR_LIMIT_LC = 0x610B, + SHT3XD_CMD_WRITE_ALR_LIMIT_LS = 0x6100, + + SHT3XD_CMD_NO_SLEEP = 0x303E, + } TMP_RH_Commands; + + + typedef enum { + SHT3XD_REPEATABILITY_HIGH, + SHT3XD_REPEATABILITY_MEDIUM, + SHT3XD_REPEATABILITY_LOW, + } TMP_RH_Repeatability; + + typedef enum { + SHT3XD_MODE_CLOCK_STRETCH, + SHT3XD_MODE_POLLING, + } TMP_RH_Mode; + + typedef enum { + SHT3XD_FREQUENCY_HZ5, + SHT3XD_FREQUENCY_1HZ, + SHT3XD_FREQUENCY_2HZ, + SHT3XD_FREQUENCY_4HZ, + SHT3XD_FREQUENCY_10HZ + } TMP_RH_Frequency; + + typedef enum { + SHT3XD_NO_ERROR = 0, + + SHT3XD_CRC_ERROR = -101, + SHT3XD_TIMEOUT_ERROR = -102, + + SHT3XD_PARAM_WRONG_MODE = -501, + SHT3XD_PARAM_WRONG_REPEATABILITY = -502, + SHT3XD_PARAM_WRONG_FREQUENCY = -503, + SHT3XD_PARAM_WRONG_ALERT = -504, + + // Wire I2C translated error codes + SHT3XD_WIRE_I2C_DATA_TOO_LOG = -10, + SHT3XD_WIRE_I2C_RECEIVED_NACK_ON_ADDRESS = -20, + SHT3XD_WIRE_I2C_RECEIVED_NACK_ON_DATA = -30, + SHT3XD_WIRE_I2C_UNKNOW_ERROR = -40 + } TMP_RH_ErrorCode; + + typedef union { + uint16_t rawData; + struct { + uint8_t WriteDataChecksumStatus : 1; + uint8_t CommandStatus : 1; + uint8_t Reserved0 : 2; + uint8_t SystemResetDetected : 1; + uint8_t Reserved1 : 5; + uint8_t T_TrackingAlert : 1; + uint8_t RH_TrackingAlert : 1; + uint8_t Reserved2 : 1; + uint8_t HeaterStatus : 1; + uint8_t Reserved3 : 1; + uint8_t AlertPending : 1; + }; + } TMP_RH_RegisterStatus; + + struct TMP_RH { + float t; + int rh; + TMP_RH_ErrorCode error; + }; +// ENUMS AND STRUCTS FOR TMP_RH END + +//ENUMS STRUCTS FOR C02 START + struct CO2_READ_RESULT { + int co2 = -1; + bool success = false; +}; +//ENUMS STRUCTS FOR C02 END + +// library interface description +class AirGradient +{ + // user-accessible "public" interface + public: + AirGradient(bool displayMsg=false,int baudRate=9600); + //void begin(int baudRate=9600); + + static void setOutput(Print& debugOut, bool verbose = true); + + void beginC02(void); + void beginC02(int,int); + void PMS_Init(void); + void PMS_Init(int,int); + void PMS_Init(int,int,int); + + bool _debugMsg; + + + //PMS VARIABLES PUBLIC_START + static const uint16_t SINGLE_RESPONSE_TIME = 1000; + static const uint16_t TOTAL_RESPONSE_TIME = 1000 * 10; + static const uint16_t STEADY_RESPONSE_TIME = 1000 * 30; + + static const uint16_t BAUD_RATE = 9600; + + struct DATA { + // Standard Particles, CF=1 + uint16_t PM_SP_UG_1_0; + uint16_t PM_SP_UG_2_5; + uint16_t PM_SP_UG_10_0; + + // Atmospheric environment + uint16_t PM_AE_UG_1_0; + uint16_t PM_AE_UG_2_5; + uint16_t PM_AE_UG_10_0; + }; + + void PMS(Stream&); + void sleep(); + void wakeUp(); + void activeMode(); + void passiveMode(); + + void requestRead(); + bool read_PMS(DATA& data); + bool readUntil(DATA& data, uint16_t timeout = SINGLE_RESPONSE_TIME); + int getPM2(); + + //PMS VARIABLES PUBLIC_END + + //TMP_RH VARIABLES PUBLIC START + void ClosedCube_TMP_RH(); + uint32_t getTemp(); + int getRhum(); + TMP_RH_ErrorCode TMP_RH_Init(uint8_t address); + TMP_RH_ErrorCode clearAll(); + + TMP_RH_ErrorCode softReset(); + TMP_RH_ErrorCode reset(); // same as softReset + + uint32_t readSerialNumber(); + uint32_t testTMP_RH(); + + TMP_RH_ErrorCode periodicStart(TMP_RH_Repeatability repeatability, TMP_RH_Frequency frequency); + TMP_RH periodicFetchData(); + TMP_RH_ErrorCode periodicStop(); + + //TMP_RH VARIABLES PUBLIC END + + //C02 VARIABLES PUBLIC START + void C02_Init(); + void C02_Init(int,int); + void C02_Init(int,int,int); + int getC02(int retryLimit = 5); + int get_C02_values(); + SoftwareSerial *_SoftSerial_C02; + + //CO2 VARIABLES PUBLIC END + + //MHZ19 VARIABLES PUBLIC START + void MHZ19_Init(uint8_t); + void MHZ19_Init(int,int,uint8_t); + void MHZ19_Init(int,int,int,uint8_t); + void setDebug_MHZ19(bool enable); + bool isPreHeating_MHZ19(); + bool isReady_MHZ19(); + + int readMHZ19(); + + //MHZ19 VARIABLES PUBLIC END + + + + // library-accessible "private" interface + private: + int value; + + + //PMS VARIABLES PRIVATE START + enum STATUS { STATUS_WAITING, STATUS_OK }; + enum MODE { MODE_ACTIVE, MODE_PASSIVE }; + + uint8_t _payload[12]; + Stream* _stream; + DATA* _data; + STATUS _PMSstatus; + MODE _mode = MODE_ACTIVE; + + uint8_t _index = 0; + uint16_t _frameLen; + uint16_t _checksum; + uint16_t _calculatedChecksum; + SoftwareSerial *_SoftSerial_PMS; + void loop(); + //PMS VARIABLES PRIVATE END + + //TMP_RH VARIABLES PRIVATE START + uint8_t _address; + TMP_RH_RegisterStatus _status; + + TMP_RH_ErrorCode writeCommand(TMP_RH_Commands command); + TMP_RH_ErrorCode writeAlertData(TMP_RH_Commands command, float temperature, float humidity); + + uint8_t checkCrc(uint8_t data[], uint8_t checksum); + uint8_t calculateCrc(uint8_t data[]); + + float calculateHumidity(uint16_t rawValue); + float calculateTemperature(uint16_t rawValue); + + TMP_RH readTemperatureAndHumidity(); + TMP_RH_ErrorCode read_TMP_RH(uint16_t* data, uint8_t numOfPair); + + TMP_RH returnError(TMP_RH_ErrorCode command); + //TMP_RH VARIABLES PRIVATE END + + //MHZ19 VARABLES PUBLIC START + + int readInternal_MHZ19(); + + uint8_t _type_MHZ19, temperature_MHZ19; + boolean debug_MHZ19 = false; + + Stream * _serial_MHZ19; + SoftwareSerial *_SoftSerial_MHZ19; + byte getCheckSum_MHZ19(byte *packet); + //MHZ19 VARABLES PUBLIC END + +}; + + +#endif + diff --git a/AirGradient/LICENSE.md b/AirGradient/LICENSE.md new file mode 100644 index 0000000..22a2de0 --- /dev/null +++ b/AirGradient/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 AirGradient + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/AirGradient/README.md b/AirGradient/README.md new file mode 100644 index 0000000..b84870d --- /dev/null +++ b/AirGradient/README.md @@ -0,0 +1,8 @@ +AirGradient Arduino Library for ESP8266 (Wemos D1 MINI) +===================================================================================================== + +Build your own low cost air quality sensor with optional display measuring PM2.5, CO2, Temperature and Humidity. + +This library makes it easy to read the sensor data from the Plantower PMS5003 PM2.5 sensor, the Senseair S8 and the SHT30/31 Temperature and Humidity sensor. Visit our blog for detailed build instructions and PCB layout. + +https://www.airgradient.com/blog/ \ No newline at end of file diff --git a/AirGradient/examples/.DS_Store b/AirGradient/examples/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3cd175f4d64a9a7f35f8cce009267473f935f9c8 GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8zj35RBCIAV8Fop~hRD=y=9@vZk249AFhF}H{h7hRS zV5dNC=VEYXFkmoZh-W}@e@bz3PEvk;4kH5t13xwe4Dk&93_c953@*q@Mer+Sh-V0A z@MLgf@I=s&0rnkkT_Kk+XXb24Y5CD}A3Xrxvg9DUqfY2Z*21W)3a2J4)fq@0) z8AfnFfB_^2(h8zMT0t~OD+42l1vUe$m4Okel@Z(x0qFyENkBAMI|C!wW)L5&oq-W- zGXn!7L^}f`)Mi-cg%P5iff1sefe~yw%ypymXb6mk04xNUAq)Xf{qM@a0LwxU(NS_V z1V%$(M1%k%i%YPJ6Sz{w?q5({3#v~Opwgf^7*ZXBDrVG1025?DQ39$IRNaHLf@pA6 W%*en1);ih{fQ8T~JsJY^4*>vwvNq}f literal 0 HcmV?d00001 diff --git a/AirGradient/examples/C02_PM_SHT/.DS_Store b/AirGradient/examples/C02_PM_SHT/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e4db637d0c9192e661b159d9db37d9e9317ba2da GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8z3~dZp0Z1N%F(jFwB8(vOz-9z6_%g&Z1T%Orgh1s8 zQ6Rk{49*M&3`Pv`49NQ98T=W17+e`#7~&bi89W)>7(7#olXH^t^K(F&2pK#oKNKwT0L4c5-U2(}r- z2Ww|w1l!EOzzEUKzzDUO5!ypxglK1AglK1A1ltaC-6%a80;3@S3jt;bLjY9&yD~7~ z>i +AirGradient ag = AirGradient(); + +void setup(){ + Serial.begin(9600); + ag.PMS_Init(); + ag.C02_Init(); + ag.TMP_RH_Init(0x44); //check for SHT sensor with address 0x44 +} + +void loop(){ + +int PM2 = ag.getPM2(); +Serial.print("PM2: "); +Serial.println(PM2); + +int CO2 = ag.getC02(); +Serial.print("C02: "); +Serial.println(CO2); + +TMP_RH result = ag.periodicFetchData(); +Serial.print("Humidity: "); +Serial.print(result.rh); +Serial.print(" Temperature: "); +Serial.println(result.t); +delay(5000); +} diff --git a/AirGradient/examples/C02_PM_SHT_OLED/C02_PM_SHT_OLED.ino b/AirGradient/examples/C02_PM_SHT_OLED/C02_PM_SHT_OLED.ino new file mode 100644 index 0000000..8b1c6f9 --- /dev/null +++ b/AirGradient/examples/C02_PM_SHT_OLED/C02_PM_SHT_OLED.ino @@ -0,0 +1,41 @@ +#include +#include +#include + +AirGradient ag = AirGradient(); +#define OLED_RESET 0 +Adafruit_SSD1306 display(OLED_RESET); + +void setup(){ + Serial.begin(9600); + display.begin(SSD1306_SWITCHCAPVCC, 0x3C); + ag.PMS_Init(); + ag.C02_Init(); + ag.TMP_RH_Init(0x44); //check for SHT sensor with address 0x44 + showTextRectangle("Init", String(ESP.getChipId(),HEX),"AirGradient"); + delay(2000); +} + +void loop(){ + int PM2 = ag.getPM2(); + int CO2 = ag.getC02(); + TMP_RH result = ag.periodicFetchData(); + showTextRectangle(String(result.t)+"c "+String(result.rh)+"%", "PM2: "+ String(PM2), "CO2: "+String(CO2)+""); + delay(5000); +} + +// DISPLAY +void showTextRectangle(String ln1, String ln2, String ln3) { + display.clearDisplay(); + display.setTextColor(WHITE); + display.setTextSize(1); + display.setCursor(32,8); + display.println(ln1); + display.setTextSize(1); + display.setCursor(32,16); + display.println(ln2); + display.setTextSize(1); + display.setCursor(32,24); + display.println(ln3); + display.display(); +} diff --git a/AirGradient/examples/C02_PM_SHT_OLED_WIFI/C02_PM_SHT_OLED_WIFI.ino b/AirGradient/examples/C02_PM_SHT_OLED_WIFI/C02_PM_SHT_OLED_WIFI.ino new file mode 100644 index 0000000..32389fd --- /dev/null +++ b/AirGradient/examples/C02_PM_SHT_OLED_WIFI/C02_PM_SHT_OLED_WIFI.ino @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include + +AirGradient ag = AirGradient(); +#define OLED_RESET 0 +Adafruit_SSD1306 display(OLED_RESET); + +String APIROOT = "http://hw.airgradient.com/"; + +void setup(){ + Serial.begin(9600); + display.begin(SSD1306_SWITCHCAPVCC, 0x3C); + ag.PMS_Init(); + ag.C02_Init(); + ag.TMP_RH_Init(0x44); //check for SHT sensor with address 0x44 + showTextRectangle("Init", String(ESP.getChipId(),HEX),"AirGradient"); + connectToWifi(); + delay(2000); +} + +void loop(){ + int PM2 = ag.getPM2(); + int CO2 = ag.getC02(); + TMP_RH result = ag.periodicFetchData(); + showTextRectangle(String(result.t)+"c "+String(result.rh)+"%", "PM2: "+ String(PM2), "CO2: "+String(CO2)+""); + + // send payload + String payload = "{\"pm02\":" + String(PM2) + ",\"wifi\":" + String(WiFi.RSSI()) + ",\"rco2\":" + String(CO2) + ",\"atmp\":" + String(result.t) + ",\"rhum\":" + String(result.rh) + "}"; + Serial.println(payload); + String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(),HEX) + "/measures"; + Serial.println(POSTURL); + HTTPClient http; + http.begin(POSTURL); + http.addHeader("content-type", "application/json"); + int httpCode = http.POST(payload); + String response = http.getString(); + Serial.println(httpCode); + Serial.println(response); + http.end(); + + delay(15000); +} + +// DISPLAY +void showTextRectangle(String ln1, String ln2, String ln3) { + display.clearDisplay(); + display.setTextColor(WHITE); + display.setTextSize(1); + display.setCursor(32,8); + display.println(ln1); + display.setTextSize(1); + display.setCursor(32,16); + display.println(ln2); + display.setTextSize(1); + display.setCursor(32,24); + display.println(ln3); + display.display(); +} + +// Wifi Manager +void connectToWifi(){ + WiFiManager wifiManager; + //chWiFi.disconnect(); //to delete previous saved hotspot + String HOTSPOT = "AIRGRADIENT-"+String(ESP.getChipId(),HEX); + wifiManager.setTimeout(120); + if(!wifiManager.autoConnect((const char*)HOTSPOT.c_str())) { + //Serial.println("failed to connect and hit timeout"); + delay(3000); + ESP.restart(); + delay(5000); + } + +} diff --git a/AirGradient/examples/C02_Test/C02_Test.ino b/AirGradient/examples/C02_Test/C02_Test.ino new file mode 100644 index 0000000..bcec75d --- /dev/null +++ b/AirGradient/examples/C02_Test/C02_Test.ino @@ -0,0 +1,16 @@ +#include +AirGradient ag = AirGradient(); + +void setup(){ + Serial.begin(9600); + ag.C02_Init(); +} + +void loop(){ + +int CO2 = ag.getC02(); +Serial.print("C02: "); +Serial.println(CO2); + +delay(5000); +} \ No newline at end of file diff --git a/AirGradient/examples/MHZ19_Test/MHZ19_Test.ino b/AirGradient/examples/MHZ19_Test/MHZ19_Test.ino new file mode 100644 index 0000000..4262150 --- /dev/null +++ b/AirGradient/examples/MHZ19_Test/MHZ19_Test.ino @@ -0,0 +1,16 @@ +#include +AirGradient ag = AirGradient(); + +void setup(){ + Serial.begin(9600); + ag.MHZ19_Init(MHZ19B); +} + +void loop(){ + +int MHZ19_C02 = ag.readMHZ19(); +Serial.print("C02: "); +Serial.println(MHZ19_C02); + +delay(5000); +} diff --git a/AirGradient/examples/PM2_Test/PM2_Test.ino b/AirGradient/examples/PM2_Test/PM2_Test.ino new file mode 100644 index 0000000..398d0bd --- /dev/null +++ b/AirGradient/examples/PM2_Test/PM2_Test.ino @@ -0,0 +1,16 @@ +#include +AirGradient ag = AirGradient(); + +void setup(){ + Serial.begin(9600); + ag.PMS_Init(); +} + +void loop(){ + +int PM2 = ag.getPM2(); +Serial.print("PM2: "); +Serial.println(PM2); + +delay(5000); +} \ No newline at end of file diff --git a/AirGradient/examples/TMP_RH_Test/TMP_RH_Test.ino b/AirGradient/examples/TMP_RH_Test/TMP_RH_Test.ino new file mode 100644 index 0000000..4d55bf6 --- /dev/null +++ b/AirGradient/examples/TMP_RH_Test/TMP_RH_Test.ino @@ -0,0 +1,16 @@ +#include +AirGradient ag = AirGradient(); + +void setup(){ + Serial.begin(9600); + ag.TMP_RH_Init(0x44); //check for SHT sensor with address 0x44 +} + +void loop(){ + TMP_RH result = ag.periodicFetchData(); + Serial.print("Humidity: "); + Serial.print(result.rh); + Serial.print(" Temperature: "); + Serial.println(result.t); + delay(5000); +} diff --git a/AirGradient/keywords.txt b/AirGradient/keywords.txt new file mode 100644 index 0000000..147783a --- /dev/null +++ b/AirGradient/keywords.txt @@ -0,0 +1,65 @@ +####################################### +# Syntax Coloring Map For AirGradient +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +AirGradient KEYWORD1 + + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +setOutput KEYWORD2 +beginC02 KEYWORD2 +PMS_Init KEYWORD2 +PMS KEYWORD2 +sleep KEYWORD2 +wakeUp KEYWORD2 +activeMode KEYWORD2 +passiveMode KEYWORD2 +requestRead KEYWORD2 +read_PMS KEYWORD2 +readUntil KEYWORD2 +getPM2 KEYWORD2 + + +ClosedCube_TMP_RH KEYWORD2 +getTemp KEYWORD2 +getRhum KEYWORD2 +TMP_RH_Init KEYWORD2 +clearAll KEYWORD2 +softReset KEYWORD2 +reset KEYWORD2 +readSerialNumber KEYWORD2 +testTMP_RH KEYWORD2 +periodicStart KEYWORD2 +periodicFetchData KEYWORD2 +periodicStop KEYWORD2 + + +C02_Init KEYWORD2 +getC02 KEYWORD2 +get_C02_values KEYWORD2 + + +MHZ19_Init KEYWORD2 +setDebug_MHZ19 KEYWORD2 +isPreHeating_MHZ19 KEYWORD2 +readMHZ19 KEYWORD2 + + + +####################################### +# Instances (KEYWORD2) +####################################### + +####################################### +# Constants (LITERAL1) +####################################### + +MHZ14A LITERAL1 +MHZ19B LITERAL1 diff --git a/AirGradient/library.properties b/AirGradient/library.properties new file mode 100644 index 0000000..d476644 --- /dev/null +++ b/AirGradient/library.properties @@ -0,0 +1,9 @@ +name=AirGradient Air Quality Sensor +version=1.0.0 +author=AirGradient +maintainer=AirGradient +sentence=ESP8266 library for an air quality sensor featuring PM2.5, CO2, Temperature and Humidity with OLED display. +paragraph=The library is very robust and works with the Plantower PMS5003 particle sensor, the Senseair S8 CO2 sensor and the SHT30/31 sensor for humidity and temperature. You can also connect an OLED display or send the air quality data to the AirGradient platform or any other backend. +category=Sensors +url=https://github.com/airgradienthq/arduino +architectures=* \ No newline at end of file diff --git a/AirGradient/readme.txt b/AirGradient/readme.txt new file mode 100644 index 0000000..c6112ba --- /dev/null +++ b/AirGradient/readme.txt @@ -0,0 +1,39 @@ +This is an example C++ library for Arduino 0004+, based on one created by +Nicholas Zambetti for Wiring 0006+ + +Installation +-------------------------------------------------------------------------------- + +To install this library, just place this entire folder as a subfolder in your +Arduino/lib/targets/libraries folder. + +When installed, this library should look like: + +Arduino/lib/targets/libraries/Test (this library's folder) +Arduino/lib/targets/libraries/Test/Test.cpp (the library implementation file) +Arduino/lib/targets/libraries/Test/Test.h (the library description file) +Arduino/lib/targets/libraries/Test/keywords.txt (the syntax coloring file) +Arduino/lib/targets/libraries/Test/examples (the examples in the "open" menu) +Arduino/lib/targets/libraries/Test/readme.txt (this file) + +Building +-------------------------------------------------------------------------------- + +After this library is installed, you just have to start the Arduino application. +You may see a few warning messages as it's built. + +To use this library in a sketch, go to the Sketch | Import Library menu and +select Test. This will add a corresponding line to the top of your sketch: +#include + +To stop using this library, delete that line from your sketch. + +Geeky information: +After a successful build of this library, a new file named "Test.o" will appear +in "Arduino/lib/targets/libraries/Test". This file is the built/compiled library +code. + +If you choose to modify the code for this library (i.e. "Test.cpp" or "Test.h"), +then you must first 'unbuild' this library by deleting the "Test.o" file. The +new "Test.o" with your code will appear after the next press of "verify" +