mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-06-28 17:20:57 +02:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
0458adb949 | |||
f51203f754 | |||
33520c18fe | |||
91563ef836 | |||
feec04f42b | |||
36fd7774f6 | |||
e0d0bb6e3b | |||
9b0d6dbcdd |
339
examples/DIY_OUTDOOR_C3_1PST/DIY_OUTDOOR_C3_1PST.ino
Normal file
339
examples/DIY_OUTDOOR_C3_1PST/DIY_OUTDOOR_C3_1PST.ino
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
/*
|
||||||
|
Important: This code is only for the AirGradient ONE Open Air Version with TVOC and CO2 sensor.
|
||||||
|
|
||||||
|
It is a high quality sensor measuring PM2.5, CO2, TVOC, NOx, Temperature and Humidity and can send data over Wifi.
|
||||||
|
|
||||||
|
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/
|
||||||
|
|
||||||
|
Kits (including a pre-soldered version) are available: https://www.airgradient.com/
|
||||||
|
|
||||||
|
The codes needs the following libraries installed:
|
||||||
|
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||||
|
"Sensirion I2C SGP41" by Sensation Version 0.1.0
|
||||||
|
"Sensirion Gas Index Algorithm" by Sensation Version 3.2.1
|
||||||
|
"Arduino-SHT" by Johannes Winkelmann Version 1.2.2
|
||||||
|
“pms” by Markusz Kakl version 1.1.0 (needs to be patched for 5003T model)
|
||||||
|
|
||||||
|
For built instructions and how to patch the PMS library: https://www.airgradient.com/open-airgradient/instructions/diy-open-air-presoldered-v11/
|
||||||
|
|
||||||
|
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||||
|
|
||||||
|
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||||
|
https://www.airgradient.com/
|
||||||
|
|
||||||
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PMS.h"
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include "s8_uart.h"
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#include <WiFiManager.h>
|
||||||
|
#include <SensirionI2CSgp41.h>
|
||||||
|
#include <NOxGasIndexAlgorithm.h>
|
||||||
|
#include <VOCGasIndexAlgorithm.h>
|
||||||
|
|
||||||
|
#define DEBUG true
|
||||||
|
|
||||||
|
#define I2C_SDA 7
|
||||||
|
#define I2C_SCL 6
|
||||||
|
|
||||||
|
HTTPClient client;
|
||||||
|
|
||||||
|
SensirionI2CSgp41 sgp41;
|
||||||
|
VOCGasIndexAlgorithm voc_algorithm;
|
||||||
|
NOxGasIndexAlgorithm nox_algorithm;
|
||||||
|
|
||||||
|
PMS pms1(Serial0);
|
||||||
|
|
||||||
|
PMS::DATA data1;
|
||||||
|
|
||||||
|
S8_UART * sensor_S8;
|
||||||
|
S8_sensor sensor;
|
||||||
|
|
||||||
|
// time in seconds needed for NOx conditioning
|
||||||
|
uint16_t conditioning_s = 10;
|
||||||
|
|
||||||
|
String APIROOT = "http://hw.airgradient.com/";
|
||||||
|
|
||||||
|
// set to true to switch from Celcius to Fahrenheit
|
||||||
|
//boolean inF = false;
|
||||||
|
|
||||||
|
// PM2.5 in US AQI (default ug/m3)
|
||||||
|
//boolean inUSAQI = false;
|
||||||
|
|
||||||
|
// Display Position
|
||||||
|
//boolean displayTop = true;
|
||||||
|
|
||||||
|
// use RGB LED Bar
|
||||||
|
//boolean useRGBledBar = true;
|
||||||
|
|
||||||
|
// set to true if you want to connect to wifi. You have 60 seconds to connect. Then it will go into an offline mode.
|
||||||
|
boolean connectWIFI = true;
|
||||||
|
|
||||||
|
int loopCount = 0;
|
||||||
|
|
||||||
|
unsigned long currentMillis = 0;
|
||||||
|
|
||||||
|
const int oledInterval = 5000;
|
||||||
|
unsigned long previousOled = 0;
|
||||||
|
|
||||||
|
const int sendToServerInterval = 10000;
|
||||||
|
unsigned long previoussendToServer = 0;
|
||||||
|
|
||||||
|
const int tvocInterval = 1000;
|
||||||
|
unsigned long previousTVOC = 0;
|
||||||
|
int TVOC = -1;
|
||||||
|
int NOX = -1;
|
||||||
|
|
||||||
|
const int co2Interval = 5000;
|
||||||
|
unsigned long previousCo2 = 0;
|
||||||
|
int Co2 = 0;
|
||||||
|
|
||||||
|
const int pmInterval = 5000;
|
||||||
|
unsigned long previousPm = 0;
|
||||||
|
int pm25 = -1;
|
||||||
|
int pm01 = -1;
|
||||||
|
int pm10 = -1;
|
||||||
|
int pm03PCount = -1;
|
||||||
|
float temp;
|
||||||
|
int hum;
|
||||||
|
|
||||||
|
//const int tempHumInterval = 2500;
|
||||||
|
//unsigned long previousTempHum = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Serial.begin(115200);
|
||||||
|
// see https://github.com/espressif/arduino-esp32/issues/6983
|
||||||
|
Serial.setTxTimeoutMs(0); // <<<====== solves the delay issue
|
||||||
|
}
|
||||||
|
|
||||||
|
Wire.begin(I2C_SDA, I2C_SCL);
|
||||||
|
|
||||||
|
Serial1.begin(9600, SERIAL_8N1, 0, 1);
|
||||||
|
Serial0.begin(9600);
|
||||||
|
|
||||||
|
sgp41.begin(Wire);
|
||||||
|
|
||||||
|
//init Watchdog
|
||||||
|
pinMode(2, OUTPUT);
|
||||||
|
digitalWrite(2, LOW);
|
||||||
|
|
||||||
|
sensor_S8 = new S8_UART(Serial1);
|
||||||
|
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
// push button
|
||||||
|
pinMode(9, INPUT_PULLUP);
|
||||||
|
|
||||||
|
countdown(3);
|
||||||
|
|
||||||
|
if (connectWIFI) {
|
||||||
|
WiFi.begin("airgradient", "cleanair");
|
||||||
|
int retries = 0;
|
||||||
|
while ((WiFi.status() != WL_CONNECTED) && (retries < 15)) {
|
||||||
|
retries++;
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
if (retries > 14) {
|
||||||
|
Serial.println(F("WiFi connection FAILED"));
|
||||||
|
connectToWifi();
|
||||||
|
}
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
sendPing();
|
||||||
|
Serial.println(F("WiFi connected!"));
|
||||||
|
Serial.println("IP address: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
currentMillis = millis();
|
||||||
|
updateTVOC();
|
||||||
|
updateCo2();
|
||||||
|
updatePm();
|
||||||
|
sendToServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTVOC() {
|
||||||
|
uint16_t error;
|
||||||
|
char errorMessage[256];
|
||||||
|
uint16_t defaultRh = 0x8000;
|
||||||
|
uint16_t defaultT = 0x6666;
|
||||||
|
uint16_t srawVoc = 0;
|
||||||
|
uint16_t srawNox = 0;
|
||||||
|
uint16_t defaultCompenstaionRh = 0x8000; // in ticks as defined by SGP41
|
||||||
|
uint16_t defaultCompenstaionT = 0x6666; // in ticks as defined by SGP41
|
||||||
|
uint16_t compensationRh = 0; // in ticks as defined by SGP41
|
||||||
|
uint16_t compensationT = 0; // in ticks as defined by SGP41
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
compensationT = static_cast < uint16_t > ((temp + 45) * 65535 / 175);
|
||||||
|
compensationRh = static_cast < uint16_t > (hum * 65535 / 100);
|
||||||
|
|
||||||
|
if (conditioning_s > 0) {
|
||||||
|
error = sgp41.executeConditioning(compensationRh, compensationT, srawVoc);
|
||||||
|
conditioning_s--;
|
||||||
|
} else {
|
||||||
|
error = sgp41.measureRawSignals(compensationRh, compensationT, srawVoc,
|
||||||
|
srawNox);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMillis - previousTVOC >= tvocInterval) {
|
||||||
|
previousTVOC += tvocInterval;
|
||||||
|
if (error) {
|
||||||
|
TVOC = -1;
|
||||||
|
NOX = -1;
|
||||||
|
//Serial.println(String(TVOC));
|
||||||
|
} else {
|
||||||
|
TVOC = voc_algorithm.process(srawVoc);
|
||||||
|
NOX = nox_algorithm.process(srawNox);
|
||||||
|
//Serial.println(String(TVOC));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateCo2() {
|
||||||
|
if (currentMillis - previousCo2 >= co2Interval) {
|
||||||
|
previousCo2 += co2Interval;
|
||||||
|
Co2 = sensor_S8 -> get_co2();
|
||||||
|
//Serial.println(String(Co2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePm() {
|
||||||
|
if (currentMillis - previousPm >= pmInterval) {
|
||||||
|
previousPm += pmInterval;
|
||||||
|
if (pms1.readUntil(data1, 2000)) {
|
||||||
|
pm01 = data1.PM_AE_UG_1_0;
|
||||||
|
pm25 = data1.PM_AE_UG_2_5;
|
||||||
|
pm10 = data1.PM_AE_UG_10_0;
|
||||||
|
pm03PCount = data1.PM_RAW_0_3;
|
||||||
|
temp = data1.AMB_TMP;
|
||||||
|
hum = data1.AMB_HUM;
|
||||||
|
} else {
|
||||||
|
pm01 = -1;
|
||||||
|
pm25 = -1;
|
||||||
|
pm10 = -1;
|
||||||
|
pm03PCount = -1;
|
||||||
|
temp = -10001;
|
||||||
|
hum = -10001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void sendPing() {
|
||||||
|
String payload = "{\"wifi\":" + String(WiFi.RSSI()) +
|
||||||
|
", \"boot\":" + loopCount +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void sendToServer() {
|
||||||
|
if (currentMillis - previoussendToServer >= sendToServerInterval) {
|
||||||
|
previoussendToServer += sendToServerInterval;
|
||||||
|
String payload = "{\"wifi\":" + String(WiFi.RSSI()) +
|
||||||
|
(Co2 < 0 ? "" : ", \"rco2\":" + String(Co2)) +
|
||||||
|
(pm01 < 0 ? "" : ", \"pm01\":" + String(pm01)) +
|
||||||
|
(pm25 < 0 ? "" : ", \"pm02\":" + String(pm25)) +
|
||||||
|
(pm10 < 0 ? "" : ", \"pm10\":" + String(pm10)) +
|
||||||
|
(pm03PCount < 0 ? "" : ", \"pm003_count\":" + String(pm03PCount)) +
|
||||||
|
(TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC)) +
|
||||||
|
(NOX < 0 ? "" : ", \"nox_index\":" + String(NOX)) +
|
||||||
|
", \"atmp\":" + String(temp/10) +
|
||||||
|
(hum < 0 ? "" : ", \"rhum\":" + String(hum/10)) +
|
||||||
|
", \"boot\":" + loopCount +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
Serial.println(payload);
|
||||||
|
String POSTURL = APIROOT + "sensors/airgradient:" + String(getNormalizedMac()) + "/measures";
|
||||||
|
Serial.println(POSTURL);
|
||||||
|
WiFiClient client;
|
||||||
|
HTTPClient http;
|
||||||
|
http.begin(client, POSTURL);
|
||||||
|
http.addHeader("content-type", "application/json");
|
||||||
|
int httpCode = http.POST(payload);
|
||||||
|
String response = http.getString();
|
||||||
|
Serial.println(httpCode);
|
||||||
|
//Serial.println(response);
|
||||||
|
http.end();
|
||||||
|
resetWatchdog();
|
||||||
|
loopCount++;
|
||||||
|
} else {
|
||||||
|
Serial.println("WiFi Disconnected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void countdown(int from) {
|
||||||
|
debug("\n");
|
||||||
|
while (from > 0) {
|
||||||
|
debug(String(from--));
|
||||||
|
debug(" ");
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetWatchdog() {
|
||||||
|
Serial.println("Watchdog reset");
|
||||||
|
digitalWrite(2, HIGH);
|
||||||
|
delay(20);
|
||||||
|
digitalWrite(2, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wifi Manager
|
||||||
|
void connectToWifi() {
|
||||||
|
WiFiManager wifiManager;
|
||||||
|
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||||
|
String HOTSPOT = "AG-" + String(getNormalizedMac());
|
||||||
|
wifiManager.setTimeout(180);
|
||||||
|
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||||
|
Serial.println("failed to connect and hit timeout");
|
||||||
|
delay(6000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug(String msg) {
|
||||||
|
if (DEBUG)
|
||||||
|
Serial.print(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug(int msg) {
|
||||||
|
if (DEBUG)
|
||||||
|
Serial.print(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debugln(String msg) {
|
||||||
|
if (DEBUG)
|
||||||
|
Serial.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debugln(int msg) {
|
||||||
|
if (DEBUG)
|
||||||
|
Serial.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getNormalizedMac() {
|
||||||
|
String mac = WiFi.macAddress();
|
||||||
|
mac.replace(":", "");
|
||||||
|
mac.toLowerCase();
|
||||||
|
return mac;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Important: This code is only for the DIY PRO PCB Version 3.7 that has a push button mounted.
|
Important: This code is only for the DIY PRO PCB Version 4.2 that has a push button mounted.
|
||||||
|
|
||||||
This is the code for the AirGradient DIY PRO Air Quality Sensor with an ESP8266 Microcontroller with the SGP40 TVOC module from AirGradient.
|
This is the code for the AirGradient DIY PRO Air Quality Sensor with an ESP8266 Microcontroller with the SGP40 TVOC module from AirGradient.
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ The codes needs the following libraries installed:
|
|||||||
“U8g2” by oliver tested with version 2.32.15
|
“U8g2” by oliver tested with version 2.32.15
|
||||||
"Sensirion I2C SGP41" by Sensation Version 0.1.0
|
"Sensirion I2C SGP41" by Sensation Version 0.1.0
|
||||||
"Sensirion Gas Index Algorithm" by Sensation Version 3.2.1
|
"Sensirion Gas Index Algorithm" by Sensation Version 3.2.1
|
||||||
|
“pms” by Markusz Kakl version 1.1.0
|
||||||
"Arduino-SHT" by Johannes Winkelmann Version 1.2.2
|
"Arduino-SHT" by Johannes Winkelmann Version 1.2.2
|
||||||
"Adafruit NeoPixel" by Adafruit Version 1.11.0
|
"Adafruit NeoPixel" by Adafruit Version 1.11.0
|
||||||
|
|
||||||
@ -28,17 +29,29 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PMS.h"
|
#include "PMS.h"
|
||||||
|
|
||||||
#include <HardwareSerial.h>
|
#include <HardwareSerial.h>
|
||||||
|
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
|
||||||
#include "s8_uart.h"
|
#include "s8_uart.h"
|
||||||
|
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
#include <WiFiManager.h>
|
#include <WiFiManager.h>
|
||||||
|
|
||||||
#include <Adafruit_NeoPixel.h>
|
#include <Adafruit_NeoPixel.h>
|
||||||
|
|
||||||
#include <EEPROM.h>
|
#include <EEPROM.h>
|
||||||
|
|
||||||
#include "SHTSensor.h"
|
#include "SHTSensor.h"
|
||||||
|
|
||||||
#include <SensirionI2CSgp41.h>
|
#include <SensirionI2CSgp41.h>
|
||||||
|
|
||||||
#include <NOxGasIndexAlgorithm.h>
|
#include <NOxGasIndexAlgorithm.h>
|
||||||
|
|
||||||
#include <VOCGasIndexAlgorithm.h>
|
#include <VOCGasIndexAlgorithm.h>
|
||||||
|
|
||||||
#include <U8g2lib.h>
|
#include <U8g2lib.h>
|
||||||
|
|
||||||
#define DEBUG true
|
#define DEBUG true
|
||||||
@ -100,8 +113,8 @@ unsigned long previoussendToServer = 0;
|
|||||||
|
|
||||||
const int tvocInterval = 1000;
|
const int tvocInterval = 1000;
|
||||||
unsigned long previousTVOC = 0;
|
unsigned long previousTVOC = 0;
|
||||||
int TVOC = 0;
|
int TVOC = -1;
|
||||||
int NOX = 0;
|
int NOX = -1;
|
||||||
|
|
||||||
const int co2Interval = 5000;
|
const int co2Interval = 5000;
|
||||||
unsigned long previousCo2 = 0;
|
unsigned long previousCo2 = 0;
|
||||||
@ -109,15 +122,15 @@ int Co2 = 0;
|
|||||||
|
|
||||||
const int pmInterval = 5000;
|
const int pmInterval = 5000;
|
||||||
unsigned long previousPm = 0;
|
unsigned long previousPm = 0;
|
||||||
int pm25 = 0;
|
int pm25 = -1;
|
||||||
int pm01 = 0;
|
int pm01 = -1;
|
||||||
int pm10 = 0;
|
int pm10 = -1;
|
||||||
int pm03PCount = 0;
|
int pm03PCount = -1;
|
||||||
|
|
||||||
const int tempHumInterval = 2500;
|
const int tempHumInterval = 5000;
|
||||||
unsigned long previousTempHum = 0;
|
unsigned long previousTempHum = 0;
|
||||||
float temp = 0;
|
float temp;
|
||||||
int hum = 0;
|
int hum;
|
||||||
|
|
||||||
int buttonConfig = 0;
|
int buttonConfig = 0;
|
||||||
int lastState = LOW;
|
int lastState = LOW;
|
||||||
@ -142,9 +155,11 @@ void setup() {
|
|||||||
|
|
||||||
updateOLED2("Warming Up", "Serial Number:", String(getNormalizedMac()));
|
updateOLED2("Warming Up", "Serial Number:", String(getNormalizedMac()));
|
||||||
sgp41.begin(Wire);
|
sgp41.begin(Wire);
|
||||||
|
delay(300);
|
||||||
|
|
||||||
sht.init(Wire);
|
sht.init(Wire);
|
||||||
sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
|
//sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
|
||||||
|
delay(300);
|
||||||
|
|
||||||
//init Watchdog
|
//init Watchdog
|
||||||
pinMode(2, OUTPUT);
|
pinMode(2, OUTPUT);
|
||||||
@ -159,10 +174,19 @@ void setup() {
|
|||||||
pinMode(9, INPUT_PULLUP);
|
pinMode(9, INPUT_PULLUP);
|
||||||
|
|
||||||
buttonConfig = String(EEPROM.read(addr)).toInt();
|
buttonConfig = String(EEPROM.read(addr)).toInt();
|
||||||
if (buttonConfig > 3) buttonConfig = 0;
|
if (buttonConfig > 7) buttonConfig = 0;
|
||||||
delay(400);
|
delay(400);
|
||||||
setConfig();
|
setConfig();
|
||||||
Serial.println("buttonConfig: " + String(buttonConfig));
|
Serial.println("buttonConfig: " + String(buttonConfig));
|
||||||
|
|
||||||
|
updateOLED2("Press Button", "for LED test &", "offline mode");
|
||||||
|
delay(2000);
|
||||||
|
currentState = digitalRead(9);
|
||||||
|
if (currentState == LOW) {
|
||||||
|
ledTest();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
updateOLED2("Press Button", "Now for", "Config Menu");
|
updateOLED2("Press Button", "Now for", "Config Menu");
|
||||||
delay(2000);
|
delay(2000);
|
||||||
currentState = digitalRead(9);
|
currentState = digitalRead(9);
|
||||||
@ -174,12 +198,13 @@ void setup() {
|
|||||||
inConf();
|
inConf();
|
||||||
}
|
}
|
||||||
|
|
||||||
countdown(3);
|
if (connectWIFI) connectToWifi();
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
if (connectWIFI) {
|
sendPing();
|
||||||
connectToWifi();
|
Serial.println(F("WiFi connected!"));
|
||||||
}
|
Serial.println("IP address: ");
|
||||||
sendPing();
|
Serial.println(WiFi.localIP());
|
||||||
|
}
|
||||||
updateOLED2("Warming Up", "Serial Number:", String(getNormalizedMac()));
|
updateOLED2("Warming Up", "Serial Number:", String(getNormalizedMac()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,9 +245,16 @@ void updateTVOC() {
|
|||||||
|
|
||||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
if (currentMillis - previousTVOC >= tvocInterval) {
|
||||||
previousTVOC += tvocInterval;
|
previousTVOC += tvocInterval;
|
||||||
TVOC = voc_algorithm.process(srawVoc);
|
if (error) {
|
||||||
NOX = nox_algorithm.process(srawNox);
|
TVOC = -1;
|
||||||
Serial.println(String(TVOC));
|
NOX = -1;
|
||||||
|
Serial.println(String(TVOC));
|
||||||
|
} else {
|
||||||
|
TVOC = voc_algorithm.process(srawVoc);
|
||||||
|
NOX = nox_algorithm.process(srawNox);
|
||||||
|
Serial.println(String(TVOC));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,6 +274,11 @@ void updatePm() {
|
|||||||
pm25 = data1.PM_AE_UG_2_5;
|
pm25 = data1.PM_AE_UG_2_5;
|
||||||
pm10 = data1.PM_AE_UG_10_0;
|
pm10 = data1.PM_AE_UG_10_0;
|
||||||
pm03PCount = data1.PM_RAW_0_3;
|
pm03PCount = data1.PM_RAW_0_3;
|
||||||
|
} else {
|
||||||
|
pm01 = -1;
|
||||||
|
pm25 = -1;
|
||||||
|
pm10 = -1;
|
||||||
|
pm03PCount = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,6 +292,8 @@ void updateTempHum() {
|
|||||||
hum = sht.getHumidity();
|
hum = sht.getHumidity();
|
||||||
} else {
|
} else {
|
||||||
Serial.print("Error in readSample()\n");
|
Serial.print("Error in readSample()\n");
|
||||||
|
temp = -10001;
|
||||||
|
hum = -10001;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,7 +341,7 @@ void inConf() {
|
|||||||
long pressDuration = releasedTime - pressedTime;
|
long pressDuration = releasedTime - pressedTime;
|
||||||
if (pressDuration < 1000) {
|
if (pressDuration < 1000) {
|
||||||
buttonConfig = buttonConfig + 1;
|
buttonConfig = buttonConfig + 1;
|
||||||
if (buttonConfig > 3) buttonConfig = 0;
|
if (buttonConfig > 7) buttonConfig = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,26 +367,52 @@ void inConf() {
|
|||||||
void setConfig() {
|
void setConfig() {
|
||||||
Serial.println("in setConfig");
|
Serial.println("in setConfig");
|
||||||
if (buttonConfig == 0) {
|
if (buttonConfig == 0) {
|
||||||
updateOLED2("Temp. in C", "PM in ug/m3", "Long Press Saves");
|
|
||||||
u8g2.setDisplayRotation(U8G2_R0);
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
updateOLED2("T:C, PM:ug/m3", "LED Bar: on", "Long Press Saves");
|
||||||
inF = false;
|
inF = false;
|
||||||
inUSAQI = false;
|
inUSAQI = false;
|
||||||
}
|
useRGBledBar = true;
|
||||||
if (buttonConfig == 1) {
|
} else if (buttonConfig == 1) {
|
||||||
updateOLED2("Temp. in C", "PM in US AQI", "Long Press Saves");
|
|
||||||
u8g2.setDisplayRotation(U8G2_R0);
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
updateOLED2("T:C, PM:US AQI", "LED Bar: on", "Long Press Saves");
|
||||||
inF = false;
|
inF = false;
|
||||||
inUSAQI = true;
|
inUSAQI = true;
|
||||||
|
useRGBledBar = true;
|
||||||
} else if (buttonConfig == 2) {
|
} else if (buttonConfig == 2) {
|
||||||
updateOLED2("Temp. in F", "PM in ug/m3", "Long Press Saves");
|
|
||||||
u8g2.setDisplayRotation(U8G2_R0);
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
updateOLED2("T:F PM:ug/m3", "LED Bar: on", "Long Press Saves");
|
||||||
inF = true;
|
inF = true;
|
||||||
inUSAQI = false;
|
inUSAQI = false;
|
||||||
|
useRGBledBar = true;
|
||||||
} else if (buttonConfig == 3) {
|
} else if (buttonConfig == 3) {
|
||||||
updateOLED2("Temp. in F", "PM in US AQI", "Long Press Saves");
|
|
||||||
u8g2.setDisplayRotation(U8G2_R0);
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
updateOLED2("T:F PM:US AQI", "LED Bar: on", "Long Press Saves");
|
||||||
inF = true;
|
inF = true;
|
||||||
inUSAQI = true;
|
inUSAQI = true;
|
||||||
|
useRGBledBar = true;
|
||||||
|
} else if (buttonConfig == 4) {
|
||||||
|
updateOLED2("T:C, PM:ug/m3", "LED Bar: off", "Long Press Saves");
|
||||||
|
inF = false;
|
||||||
|
inUSAQI = false;
|
||||||
|
useRGBledBar = false;
|
||||||
|
} else if (buttonConfig == 5) {
|
||||||
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
updateOLED2("T:C, PM:US AQI", "LED Bar: off", "Long Press Saves");
|
||||||
|
inF = false;
|
||||||
|
inUSAQI = true;
|
||||||
|
useRGBledBar = false;
|
||||||
|
} else if (buttonConfig == 6) {
|
||||||
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
updateOLED2("T:F PM:ug/m3", "LED Bar: off", "Long Press Saves");
|
||||||
|
inF = true;
|
||||||
|
inUSAQI = false;
|
||||||
|
useRGBledBar = false;
|
||||||
|
} else if (buttonConfig == 7) {
|
||||||
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
updateOLED2("T:F PM:US AQI", "LED Bar: off", "Long Press Saves");
|
||||||
|
inF = true;
|
||||||
|
inUSAQI = true;
|
||||||
|
useRGBledBar = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,101 +434,98 @@ void updateOLED2(String ln1, String ln2, String ln3) {
|
|||||||
} while (u8g2.nextPage());
|
} while (u8g2.nextPage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void updateOLED3() {
|
void updateOLED3() {
|
||||||
char buf[9];
|
char buf[9];
|
||||||
u8g2.firstPage();
|
u8g2.firstPage();
|
||||||
u8g2.firstPage();
|
u8g2.firstPage();
|
||||||
do {
|
do {
|
||||||
|
|
||||||
u8g2.setFont(u8g2_font_t0_16_tf);
|
u8g2.setFont(u8g2_font_t0_16_tf);
|
||||||
|
|
||||||
if (inF) {
|
if (inF) {
|
||||||
if (temp > -10001) {
|
if (temp > -10001) {
|
||||||
float tempF = (temp * 9 / 5) + 32;
|
float tempF = (temp * 9 / 5) + 32;
|
||||||
sprintf (buf, "%.1f°F", tempF);
|
sprintf(buf, "%.1f°F", tempF);
|
||||||
} else {
|
} else {
|
||||||
sprintf (buf, "-°F");
|
sprintf(buf, "-°F");
|
||||||
}
|
}
|
||||||
u8g2.drawUTF8(1, 10, buf);
|
u8g2.drawUTF8(1, 10, buf);
|
||||||
} else {
|
} else {
|
||||||
if (temp > -10001) {
|
if (temp > -10001) {
|
||||||
sprintf (buf, "%.1f°C", temp);
|
sprintf(buf, "%.1f°C", temp);
|
||||||
} else {
|
} else {
|
||||||
sprintf (buf, "-°C");
|
sprintf(buf, "-°C");
|
||||||
}
|
}
|
||||||
u8g2.drawUTF8(1, 10, buf);
|
u8g2.drawUTF8(1, 10, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hum >= 0) {
|
if (hum >= 0) {
|
||||||
sprintf(buf, "%d%%", hum);
|
sprintf(buf, "%d%%", hum);
|
||||||
} else {
|
|
||||||
sprintf(buf, " -%%");
|
|
||||||
}
|
|
||||||
if (hum > 99) {
|
|
||||||
u8g2.drawStr(97, 10, buf);
|
|
||||||
} else {
|
|
||||||
u8g2.drawStr(105, 10, buf);
|
|
||||||
// there might also be single digits, not considered, sprintf might actually support a leading space
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
u8g2.drawLine(1, 13, 128, 13);
|
|
||||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
|
||||||
u8g2.drawUTF8(1, 27, "CO2");
|
|
||||||
u8g2.setFont(u8g2_font_t0_22b_tf);
|
|
||||||
if (Co2 > 0) {
|
|
||||||
sprintf(buf, "%d", Co2);
|
|
||||||
} else {
|
|
||||||
sprintf(buf, "%s", "-");
|
|
||||||
}
|
|
||||||
u8g2.drawStr(1, 48, buf);
|
|
||||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
|
||||||
u8g2.drawStr(1, 61, "ppm");
|
|
||||||
u8g2.drawLine(45, 15, 45, 64);
|
|
||||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
|
||||||
u8g2.drawStr(48, 27, "PM2.5");
|
|
||||||
u8g2.setFont(u8g2_font_t0_22b_tf);
|
|
||||||
|
|
||||||
|
|
||||||
if (inUSAQI) {
|
|
||||||
if (pm25 >= 0) {
|
|
||||||
sprintf(buf, "%d", PM_TO_AQI_US(pm25));
|
|
||||||
} else {
|
|
||||||
sprintf(buf, "%s", "-");
|
|
||||||
}
|
|
||||||
u8g2.drawStr(48, 48, buf);
|
|
||||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
|
||||||
u8g2.drawUTF8(48, 61, "AQI");
|
|
||||||
} else {
|
} else {
|
||||||
if (pm25 >= 0) {
|
sprintf(buf, " -%%");
|
||||||
sprintf(buf, "%d", pm25);
|
}
|
||||||
} else {
|
if (hum > 99) {
|
||||||
sprintf(buf, "%s", "-");
|
u8g2.drawStr(97, 10, buf);
|
||||||
}
|
} else {
|
||||||
u8g2.drawStr(48, 48, buf);
|
u8g2.drawStr(105, 10, buf);
|
||||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
// there might also be single digits, not considered, sprintf might actually support a leading space
|
||||||
u8g2.drawUTF8(48, 61, "ug/m³");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u8g2.drawLine(82, 15, 82, 64);
|
u8g2.drawLine(1, 13, 128, 13);
|
||||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||||
u8g2.drawStr(85, 27, "TVOC:");
|
u8g2.drawUTF8(1, 27, "CO2");
|
||||||
if (TVOC >= 0) {
|
u8g2.setFont(u8g2_font_t0_22b_tf);
|
||||||
sprintf(buf, "%d", TVOC);
|
if (Co2 > 0) {
|
||||||
} else {
|
sprintf(buf, "%d", Co2);
|
||||||
sprintf(buf, "%s", "-");
|
} else {
|
||||||
}
|
sprintf(buf, "%s", "-");
|
||||||
u8g2.drawStr(85, 39, buf);
|
}
|
||||||
u8g2.drawStr(85, 53, "NOx:");
|
u8g2.drawStr(1, 48, buf);
|
||||||
if (NOX >= 0) {
|
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||||
sprintf(buf, "%d", NOX);
|
u8g2.drawStr(1, 61, "ppm");
|
||||||
} else {
|
u8g2.drawLine(45, 15, 45, 64);
|
||||||
sprintf(buf, "%s", "-");
|
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||||
}
|
u8g2.drawStr(48, 27, "PM2.5");
|
||||||
u8g2.drawStr(85, 63, buf);
|
u8g2.setFont(u8g2_font_t0_22b_tf);
|
||||||
|
|
||||||
} while (u8g2.nextPage());
|
if (inUSAQI) {
|
||||||
|
if (pm25 >= 0) {
|
||||||
|
sprintf(buf, "%d", PM_TO_AQI_US(pm25));
|
||||||
|
} else {
|
||||||
|
sprintf(buf, "%s", "-");
|
||||||
|
}
|
||||||
|
u8g2.drawStr(48, 48, buf);
|
||||||
|
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||||
|
u8g2.drawUTF8(48, 61, "AQI");
|
||||||
|
} else {
|
||||||
|
if (pm25 >= 0) {
|
||||||
|
sprintf(buf, "%d", pm25);
|
||||||
|
} else {
|
||||||
|
sprintf(buf, "%s", "-");
|
||||||
|
}
|
||||||
|
u8g2.drawStr(48, 48, buf);
|
||||||
|
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||||
|
u8g2.drawUTF8(48, 61, "ug/m³");
|
||||||
|
}
|
||||||
|
|
||||||
|
u8g2.drawLine(82, 15, 82, 64);
|
||||||
|
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||||
|
u8g2.drawStr(85, 27, "TVOC:");
|
||||||
|
if (TVOC >= 0) {
|
||||||
|
sprintf(buf, "%d", TVOC);
|
||||||
|
} else {
|
||||||
|
sprintf(buf, "%s", "-");
|
||||||
|
}
|
||||||
|
u8g2.drawStr(85, 39, buf);
|
||||||
|
u8g2.drawStr(85, 53, "NOx:");
|
||||||
|
if (NOX >= 0) {
|
||||||
|
sprintf(buf, "%d", NOX);
|
||||||
|
} else {
|
||||||
|
sprintf(buf, "%s", "-");
|
||||||
|
}
|
||||||
|
u8g2.drawStr(85, 63, buf);
|
||||||
|
|
||||||
|
} while (u8g2.nextPage());
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendToServer() {
|
void sendToServer() {
|
||||||
@ -525,6 +587,7 @@ void connectToWifi() {
|
|||||||
WiFiManager wifiManager;
|
WiFiManager wifiManager;
|
||||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||||
String HOTSPOT = "AG-" + String(getNormalizedMac());
|
String HOTSPOT = "AG-" + String(getNormalizedMac());
|
||||||
|
updateOLED2("180s to connect", "to Wifi Hotspot", HOTSPOT);
|
||||||
wifiManager.setTimeout(180);
|
wifiManager.setTimeout(180);
|
||||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||||
Serial.println("failed to connect and hit timeout");
|
Serial.println("failed to connect and hit timeout");
|
||||||
@ -561,12 +624,12 @@ String getNormalizedMac() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setRGBledCO2color(int co2Value) {
|
void setRGBledCO2color(int co2Value) {
|
||||||
if (co2Value < 800) setRGBledColor('g');
|
if (co2Value >= 300 && co2Value < 800) setRGBledColor('g');
|
||||||
if (co2Value >= 800 && co2Value < 1000) setRGBledColor('y');
|
if (co2Value >= 800 && co2Value < 1000) setRGBledColor('y');
|
||||||
if (co2Value >= 1000 && co2Value < 1500) setRGBledColor('o');
|
if (co2Value >= 1000 && co2Value < 1500) setRGBledColor('o');
|
||||||
if (co2Value >= 1500 && co2Value < 2000) setRGBledColor('r');
|
if (co2Value >= 1500 && co2Value < 2000) setRGBledColor('r');
|
||||||
if (co2Value >= 2000 && co2Value < 3000) setRGBledColor('p');
|
if (co2Value >= 2000 && co2Value < 3000) setRGBledColor('p');
|
||||||
if (co2Value >= 3000 && co2Value < 10000) setRGBledColor('b');
|
if (co2Value >= 3000 && co2Value < 10000) setRGBledColor('z');
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRGBledColor(char color) {
|
void setRGBledColor(char color) {
|
||||||
@ -576,42 +639,63 @@ void setRGBledColor(char color) {
|
|||||||
case 'g':
|
case 'g':
|
||||||
for (int i = 0; i < 11; i++) {
|
for (int i = 0; i < 11; i++) {
|
||||||
pixels.setPixelColor(i, pixels.Color(0, 255, 0));
|
pixels.setPixelColor(i, pixels.Color(0, 255, 0));
|
||||||
delay(100);
|
delay(30);
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'y':
|
case 'y':
|
||||||
for (int i = 0; i < 11; i++) {
|
for (int i = 0; i < 11; i++) {
|
||||||
pixels.setPixelColor(i, pixels.Color(255, 255, 0));
|
pixels.setPixelColor(i, pixels.Color(255, 255, 0));
|
||||||
delay(100);
|
delay(30);
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
for (int i = 0; i < 11; i++) {
|
for (int i = 0; i < 11; i++) {
|
||||||
pixels.setPixelColor(i, pixels.Color(255, 128, 0));
|
pixels.setPixelColor(i, pixels.Color(255, 128, 0));
|
||||||
delay(100);
|
delay(30);
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
for (int i = 0; i < 11; i++) {
|
for (int i = 0; i < 11; i++) {
|
||||||
pixels.setPixelColor(i, pixels.Color(255, 0, 0));
|
pixels.setPixelColor(i, pixels.Color(255, 0, 0));
|
||||||
delay(100);
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(0, 0, 255));
|
||||||
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(255, 255, 255));
|
||||||
|
delay(30);
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
for (int i = 0; i < 11; i++) {
|
for (int i = 0; i < 11; i++) {
|
||||||
pixels.setPixelColor(i, pixels.Color(153, 0, 153));
|
pixels.setPixelColor(i, pixels.Color(153, 0, 153));
|
||||||
delay(100);
|
delay(30);
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'z':
|
||||||
for (int i = 0; i < 11; i++) {
|
for (int i = 0; i < 11; i++) {
|
||||||
pixels.setPixelColor(i, pixels.Color(102, 0, 0));
|
pixels.setPixelColor(i, pixels.Color(102, 0, 0));
|
||||||
delay(100);
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(0, 0, 0));
|
||||||
|
delay(30);
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -623,6 +707,40 @@ void setRGBledColor(char color) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ledTest() {
|
||||||
|
updateOLED2("LED Test", "running", ".....");
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(255, 0, 0));
|
||||||
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
delay(500);
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(0, 255, 0));
|
||||||
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
delay(500);
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(0, 0, 255));
|
||||||
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
delay(500);
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(255, 255, 255));
|
||||||
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
delay(500);
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(0, 0, 0));
|
||||||
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate PM2.5 US AQI
|
// Calculate PM2.5 US AQI
|
||||||
int PM_TO_AQI_US(int pm02) {
|
int PM_TO_AQI_US(int pm02) {
|
||||||
if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name=AirGradient Air Quality Sensor
|
name=AirGradient Air Quality Sensor
|
||||||
version=2.4.7
|
version=2.4.13
|
||||||
author=AirGradient <support@airgradient.com>
|
author=AirGradient <support@airgradient.com>
|
||||||
maintainer=AirGradient <support@airgradient.com>
|
maintainer=AirGradient <support@airgradient.com>
|
||||||
sentence=ESP8266 library for an air quality sensor featuring PM2.5, CO2, Temperature, TVOC and Humidity with OLED display.
|
sentence=ESP8266 library for an air quality sensor featuring PM2.5, CO2, Temperature, TVOC and Humidity with OLED display.
|
||||||
|
Reference in New Issue
Block a user