mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-06-26 16:21:33 +02:00
Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
2211fdc7a6 | |||
44dae71298 | |||
0458adb949 | |||
f51203f754 | |||
33520c18fe | |||
91563ef836 | |||
feec04f42b | |||
36fd7774f6 | |||
e0d0bb6e3b | |||
9b0d6dbcdd | |||
d38f9e4d31 | |||
3b54adb094 | |||
336cfddacb | |||
f999ebe17c | |||
89758f92fa | |||
dee5b77d2a | |||
d4225896fe | |||
d5432630fe | |||
356e76f10e | |||
92f665d9ee | |||
a42ceb3b77 | |||
1f364a3b74 | |||
37d1140eda | |||
63e1bcdc30 | |||
c53517cadf | |||
6314e52770 | |||
43f599a0a7 | |||
cfc37d2d96 | |||
693d1f78aa | |||
ef802593a6 | |||
8ec543e9c1 | |||
9722eda5fa | |||
253d8a6810 | |||
6101429d30 | |||
47c55ae0dd |
@ -725,48 +725,6 @@ void AirGradient::CO2_Init(int rx_pin,int tx_pin,int baudRate){
|
|||||||
delay(10000);
|
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(){
|
|
||||||
// const byte CO2Command[] = {0xFE, 0X44, 0X00, 0X08, 0X02, 0X9F, 0X25};
|
|
||||||
// byte CO2Response[] = {0,0,0,0,0,0,0};
|
|
||||||
//
|
|
||||||
// _SoftSerial_CO2->write(CO2Command, 7);
|
|
||||||
// delay(100); //give the sensor a bit of time to respond
|
|
||||||
//
|
|
||||||
// if (_SoftSerial_CO2->available()){
|
|
||||||
// for (int i=0; i < 7; i++) {
|
|
||||||
// int byte = _SoftSerial_CO2->read();
|
|
||||||
// CO2Response[i] = byte;
|
|
||||||
// if (CO2Response[0] != 254) {
|
|
||||||
// return -1; //error code for debugging
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// unsigned long val = CO2Response[3]*256 + CO2Response[4];
|
|
||||||
// return val;
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// return -2; //error code for debugging
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
int AirGradient::getCO2(int numberOfSamplesToTake) {
|
int AirGradient::getCO2(int numberOfSamplesToTake) {
|
||||||
int successfulSamplesCounter = 0;
|
int successfulSamplesCounter = 0;
|
||||||
@ -800,14 +758,12 @@ int AirGradient::getCO2_Raw() {
|
|||||||
while(_SoftSerial_CO2->available()) // flush whatever we might have
|
while(_SoftSerial_CO2->available()) // flush whatever we might have
|
||||||
_SoftSerial_CO2->read();
|
_SoftSerial_CO2->read();
|
||||||
|
|
||||||
const byte CO2Command[] = {0xFE, 0X44, 0X00, 0X08, 0X02, 0X9F, 0X25};
|
const byte CO2Command[] = {0XFE, 0X04, 0X00, 0X03, 0X00, 0X01, 0XD5, 0XC5};
|
||||||
byte CO2Response[] = {0,0,0,0,0,0,0};
|
byte CO2Response[] = {0,0,0,0,0,0,0};
|
||||||
|
|
||||||
// tt
|
|
||||||
int datapos = -1;
|
int datapos = -1;
|
||||||
//
|
|
||||||
|
|
||||||
const int commandSize = 7;
|
const int commandSize = 8;
|
||||||
|
const int responseSize = 7;
|
||||||
|
|
||||||
int numberOfBytesWritten = _SoftSerial_CO2->write(CO2Command, commandSize);
|
int numberOfBytesWritten = _SoftSerial_CO2->write(CO2Command, commandSize);
|
||||||
|
|
||||||
@ -818,7 +774,7 @@ int AirGradient::getCO2_Raw() {
|
|||||||
|
|
||||||
// attempt to read response
|
// attempt to read response
|
||||||
int timeoutCounter = 0;
|
int timeoutCounter = 0;
|
||||||
while (_SoftSerial_CO2->available() < commandSize) {
|
while (_SoftSerial_CO2->available() < responseSize) {
|
||||||
timeoutCounter++;
|
timeoutCounter++;
|
||||||
if (timeoutCounter > 10) {
|
if (timeoutCounter > 10) {
|
||||||
// timeout when reading response
|
// timeout when reading response
|
||||||
@ -828,22 +784,15 @@ int AirGradient::getCO2_Raw() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we have 7 bytes ready to be read
|
// we have 7 bytes ready to be read
|
||||||
for (int i=0; i < commandSize; i++) {
|
for (int i=0; i < responseSize; i++) {
|
||||||
CO2Response[i] = _SoftSerial_CO2->read();
|
CO2Response[i] = _SoftSerial_CO2->read();
|
||||||
|
|
||||||
// tt
|
|
||||||
if ((CO2Response[i] == 0xFE) && (datapos == -1)){
|
if ((CO2Response[i] == 0xFE) && (datapos == -1)){
|
||||||
datapos = i;
|
datapos = i;
|
||||||
}
|
}
|
||||||
Serial.print (CO2Response[i],HEX);
|
Serial.print (CO2Response[i],HEX);
|
||||||
Serial.print (":");
|
Serial.print (":");
|
||||||
//
|
|
||||||
}
|
}
|
||||||
// return CO2Response[3]*256 + CO2Response[4];
|
|
||||||
// tt
|
|
||||||
return CO2Response[datapos + 3]*256 + CO2Response[datapos + 4];
|
return CO2Response[datapos + 3]*256 + CO2Response[datapos + 4];
|
||||||
//
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//END CO2 FUNCTIONS //
|
//END CO2 FUNCTIONS //
|
||||||
|
@ -17,7 +17,8 @@ https://www.airgradient.com/
|
|||||||
|
|
||||||
Kits with all required components are available at https://www.airgradient.com/open-airgradient/shop/
|
Kits with all required components are available at https://www.airgradient.com/open-airgradient/shop/
|
||||||
|
|
||||||
MIT License
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AirGradient.h>
|
#include <AirGradient.h>
|
||||||
|
@ -21,7 +21,8 @@ If you have any questions please visit our forum at https://forum.airgradient.co
|
|||||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||||
https://www.airgradient.com/
|
https://www.airgradient.com/
|
||||||
|
|
||||||
MIT License
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AirGradient.h>
|
#include <AirGradient.h>
|
||||||
|
@ -10,6 +10,7 @@ Kits (including a pre-soldered version) are available: https://www.airgradient.c
|
|||||||
The codes needs the following libraries installed:
|
The codes needs the following libraries installed:
|
||||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||||
“U8g2” by oliver tested with version 2.32.15
|
“U8g2” by oliver tested with version 2.32.15
|
||||||
|
"Arduino-SHT" by Johannes Winkelmann Version 1.2.2
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
Please set in the code below the configuration parameters.
|
Please set in the code below the configuration parameters.
|
||||||
@ -30,8 +31,10 @@ MIT License
|
|||||||
#include <ESP8266HTTPClient.h>
|
#include <ESP8266HTTPClient.h>
|
||||||
#include <WiFiClient.h>
|
#include <WiFiClient.h>
|
||||||
#include <U8g2lib.h>
|
#include <U8g2lib.h>
|
||||||
|
#include "SHTSensor.h"
|
||||||
|
|
||||||
AirGradient ag = AirGradient();
|
AirGradient ag = AirGradient();
|
||||||
|
SHTSensor sht;
|
||||||
|
|
||||||
U8G2_SSD1306_64X48_ER_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); //for DIY BASIC
|
U8G2_SSD1306_64X48_ER_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); //for DIY BASIC
|
||||||
|
|
||||||
@ -78,19 +81,19 @@ long val;
|
|||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
sht.init();
|
||||||
|
sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
|
||||||
|
u8g2.setBusClock(100000);
|
||||||
u8g2.begin();
|
u8g2.begin();
|
||||||
updateOLED();
|
updateOLED();
|
||||||
|
|
||||||
if (connectWIFI) {
|
if (connectWIFI) {
|
||||||
connectToWifi();
|
connectToWifi();
|
||||||
}
|
}
|
||||||
|
updateOLED2("Warm Up", "Serial#", String(ESP.getChipId(), HEX));
|
||||||
updateOLED2("Warming", "up the", "sensors");
|
|
||||||
|
|
||||||
ag.CO2_Init();
|
ag.CO2_Init();
|
||||||
ag.PMS_Init();
|
ag.PMS_Init();
|
||||||
ag.TMP_RH_Init(0x44);
|
//ag.TMP_RH_Init(0x44);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -126,9 +129,19 @@ void updateTempHum()
|
|||||||
{
|
{
|
||||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
if (currentMillis - previousTempHum >= tempHumInterval) {
|
||||||
previousTempHum += tempHumInterval;
|
previousTempHum += tempHumInterval;
|
||||||
TMP_RH result = ag.periodicFetchData();
|
if (sht.readSample()) {
|
||||||
temp = result.t;
|
Serial.print("SHT:\n");
|
||||||
hum = result.rh;
|
Serial.print(" RH: ");
|
||||||
|
Serial.print(sht.getHumidity(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
Serial.print(" T: ");
|
||||||
|
Serial.print(sht.getTemperature(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
temp = sht.getTemperature();
|
||||||
|
hum = sht.getHumidity();
|
||||||
|
} else {
|
||||||
|
Serial.print("Error in readSample()\n");
|
||||||
|
}
|
||||||
Serial.println(String(temp));
|
Serial.println(String(temp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,9 +220,9 @@ void sendToServer() {
|
|||||||
WiFiManager wifiManager;
|
WiFiManager wifiManager;
|
||||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||||
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
||||||
updateOLED2("Connect", "Wifi", HOTSPOT);
|
updateOLED2("Connect", "Wifi AG-", String(ESP.getChipId(), HEX));
|
||||||
delay(2000);
|
delay(2000);
|
||||||
wifiManager.setTimeout(60);
|
wifiManager.setTimeout(90);
|
||||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||||
updateOLED2("Booting", "offline", "mode");
|
updateOLED2("Booting", "offline", "mode");
|
||||||
Serial.println("failed to connect and hit timeout");
|
Serial.println("failed to connect and hit timeout");
|
||||||
|
@ -1,3 +1,22 @@
|
|||||||
|
/*
|
||||||
|
Important: This code is only for the AirGradient DIY OUTDOOR.
|
||||||
|
|
||||||
|
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-outdoor/
|
||||||
|
|
||||||
|
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
||||||
|
|
||||||
|
Configuration:
|
||||||
|
Install required libraries
|
||||||
|
Patch PMS library to accept temperature and humidity from PMS5003T
|
||||||
|
|
||||||
|
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 "PMS.h"
|
||||||
#include "SoftwareSerial.h"
|
#include "SoftwareSerial.h"
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
@ -7,21 +26,20 @@
|
|||||||
#include <ESP8266HTTPClient.h>
|
#include <ESP8266HTTPClient.h>
|
||||||
#include <WiFiClient.h>
|
#include <WiFiClient.h>
|
||||||
|
|
||||||
#include "ClosedCube_SHT31D.h"
|
SoftwareSerial softSerial(D6, D5);
|
||||||
|
SoftwareSerial soft2(D3, D7);
|
||||||
|
|
||||||
SoftwareSerial softSerial(D6, D5); //(RX, TX)
|
|
||||||
SoftwareSerial soft2(D3, D7); //(RX, TX)
|
|
||||||
|
|
||||||
ClosedCube_SHT31D sht3xd;
|
|
||||||
|
|
||||||
PMS pms(softSerial);
|
PMS pms(softSerial);
|
||||||
PMS pms2(soft2);
|
PMS pms2(soft2);
|
||||||
PMS::DATA data;
|
PMS::DATA data;
|
||||||
|
PMS::DATA data2;
|
||||||
|
|
||||||
int pm1 = 0;
|
float pm1Value=0;
|
||||||
int pm2 = 0;
|
int pm1Position = 0;
|
||||||
float temp = 0;
|
|
||||||
int hum = 0;
|
float pm2Value=0;
|
||||||
|
int pm2Position = 0;
|
||||||
|
|
||||||
float temp_pm1 = 0;
|
float temp_pm1 = 0;
|
||||||
float hum_pm1 = 0;
|
float hum_pm1 = 0;
|
||||||
@ -29,107 +47,101 @@ float hum_pm1 = 0;
|
|||||||
float temp_pm2 = 0;
|
float temp_pm2 = 0;
|
||||||
float hum_pm2 = 0;
|
float hum_pm2 = 0;
|
||||||
|
|
||||||
|
unsigned long currentMillis = 0;
|
||||||
|
|
||||||
|
const int pm1Interval = 5000;
|
||||||
|
unsigned long previousPm1 = 0;
|
||||||
|
|
||||||
|
const int pm2Interval = 5000;
|
||||||
|
unsigned long previousPm2 = 0;
|
||||||
|
|
||||||
String APIROOT = "http://hw.airgradient.com/";
|
String APIROOT = "http://hw.airgradient.com/";
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
Serial.begin(9600);
|
Serial.begin(115200);
|
||||||
Serial.println("hhi");
|
Serial.println("Chip ID: "+String(ESP.getChipId()));
|
||||||
softSerial.begin(9600);
|
softSerial.begin(9600);
|
||||||
soft2.begin(9600);
|
soft2.begin(9600);
|
||||||
Wire.begin();
|
Wire.begin();
|
||||||
sht3xd.begin(0x44);
|
|
||||||
pinMode(D7, OUTPUT);
|
pinMode(D7, OUTPUT);
|
||||||
|
|
||||||
connectToWifi();
|
connectToWifi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
|
currentMillis = millis();
|
||||||
if (digitalRead(D8) == HIGH) {
|
updatePm1();
|
||||||
digitalWrite(D7, HIGH);
|
updatePm2();
|
||||||
}
|
|
||||||
else {
|
|
||||||
digitalWrite(D7, LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pms.read(data)){
|
|
||||||
pm1 = data.PM_AE_UG_2_5;
|
|
||||||
temp_pm1 = data.AMB_TMP;
|
|
||||||
hum_pm1 = data.AMB_HUM;
|
|
||||||
Serial.print("PMS 1: PM 2.5 (ug/m3): ");
|
|
||||||
Serial.println(pm1);
|
|
||||||
Serial.print("PMS 1: Temp: ");
|
|
||||||
Serial.println(temp_pm1);
|
|
||||||
Serial.print("PMS 1: Hum: ");
|
|
||||||
Serial.println(hum_pm1);
|
|
||||||
Serial.println();
|
|
||||||
delay(1000);
|
|
||||||
}
|
|
||||||
if (pms2.read(data)){
|
|
||||||
pm2 = data.PM_AE_UG_2_5;
|
|
||||||
temp_pm2 = data.AMB_TMP ;
|
|
||||||
hum_pm2 = data.AMB_HUM;
|
|
||||||
Serial.print("PMS 2: PM 2.5 (ug/m3): ");
|
|
||||||
Serial.println(pm2);
|
|
||||||
Serial.print("PMS 2: Temp: ");
|
|
||||||
Serial.println(temp_pm2);
|
|
||||||
Serial.print("PMS 2: Hum: ");
|
|
||||||
Serial.println(hum_pm2);
|
|
||||||
Serial.println();
|
|
||||||
delay(1000);
|
|
||||||
|
|
||||||
temp = sht3xd.readTempAndHumidity(SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50).t;
|
|
||||||
hum = sht3xd.readTempAndHumidity(SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50).rh;
|
|
||||||
|
|
||||||
Serial.println(temp);
|
|
||||||
Serial.println(hum);
|
|
||||||
|
|
||||||
sendToServer();
|
|
||||||
delay(1000);
|
|
||||||
sendToServerPM1();
|
|
||||||
delay(1000);
|
|
||||||
sendToServerPM2();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void updatePm1()
|
||||||
void sendToServer() {
|
{
|
||||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
if (currentMillis - previousPm1 >= pm1Interval) {
|
||||||
+ ", \"pm02\":" + String((pm1+pm2)/2)
|
digitalWrite(D7, HIGH);
|
||||||
+ ", \"atmp\":" + String(temp)
|
delay(400);
|
||||||
+ ", \"rhum\":" + String(hum)
|
digitalWrite(D7, LOW);
|
||||||
+ "}";
|
Serial.println("updatePm1: "+String(pm1Position));
|
||||||
|
previousPm1 += pm1Interval;
|
||||||
if(WiFi.status()== WL_CONNECTED){
|
pms.requestRead();
|
||||||
digitalWrite(D7, HIGH);
|
if (pms.readUntil(data)){
|
||||||
delay(300);
|
Serial.println("success read");
|
||||||
Serial.println(payload);
|
int pm1 = data.PM_AE_UG_2_5;
|
||||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "/measures";
|
temp_pm1 = data.AMB_TMP;
|
||||||
Serial.println(POSTURL);
|
hum_pm1 = data.AMB_HUM;
|
||||||
WiFiClient client;
|
Serial.print("PMS 1: PM 2.5 (ug/m3): ");
|
||||||
HTTPClient http;
|
Serial.println(pm1);
|
||||||
http.begin(client, POSTURL);
|
Serial.print("PMS 1: Temp: ");
|
||||||
http.addHeader("content-type", "application/json");
|
Serial.println(temp_pm1);
|
||||||
int httpCode = http.POST(payload);
|
Serial.print("PMS 1: Hum: ");
|
||||||
String response = http.getString();
|
Serial.println(hum_pm1);
|
||||||
Serial.println(httpCode);
|
Serial.println();
|
||||||
Serial.println(response);
|
delay(1000);
|
||||||
http.end();
|
pm1Value=pm1Value+pm1;
|
||||||
digitalWrite(D7, LOW);
|
pm1Position++;
|
||||||
}
|
if (pm1Position==20) {
|
||||||
else {
|
sendToServerPM1(pm1Value);
|
||||||
Serial.println("WiFi Disconnected");
|
pm1Position=0;
|
||||||
}
|
pm1Value=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendToServerPM1() {
|
void updatePm2()
|
||||||
|
{
|
||||||
|
if (currentMillis - previousPm2 >= pm2Interval) {
|
||||||
|
Serial.println("updatePm2: "+String(pm2Position));
|
||||||
|
previousPm2 += pm2Interval;
|
||||||
|
pms2.requestRead();
|
||||||
|
if (pms2.readUntil(data2)){
|
||||||
|
int pm2 = data2.PM_AE_UG_2_5;
|
||||||
|
temp_pm2 = data2.AMB_TMP ;
|
||||||
|
hum_pm2 = data2.AMB_HUM;
|
||||||
|
Serial.print("PMS 2: PM 2.5 (ug/m3): ");
|
||||||
|
Serial.println(pm2);
|
||||||
|
Serial.print("PMS 2: Temp: ");
|
||||||
|
Serial.println(temp_pm2);
|
||||||
|
Serial.print("PMS 2: Hum: ");
|
||||||
|
Serial.println(hum_pm2);
|
||||||
|
Serial.println();
|
||||||
|
delay(1000);
|
||||||
|
pm2Value=pm2Value+pm2;
|
||||||
|
pm2Position++;
|
||||||
|
if (pm2Position==20) {
|
||||||
|
sendToServerPM2(pm2Value);
|
||||||
|
pm2Position=0;
|
||||||
|
pm2Value=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendToServerPM1(float pm1Value) {
|
||||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||||
+ ", \"pm02\":" + String(pm1)
|
+ ", \"pm02\":" + String(pm1Value/20)
|
||||||
+ ", \"atmp\":" + String(temp_pm1/10)
|
+ ", \"atmp\":" + String(temp_pm1/10)
|
||||||
+ ", \"rhum\":" + String(hum_pm1/10)
|
+ ", \"rhum\":" + String(hum_pm1/10)
|
||||||
+ "}";
|
+ "}";
|
||||||
@ -138,7 +150,7 @@ void sendToServerPM1() {
|
|||||||
digitalWrite(D7, HIGH);
|
digitalWrite(D7, HIGH);
|
||||||
delay(300);
|
delay(300);
|
||||||
Serial.println(payload);
|
Serial.println(payload);
|
||||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "pm1/measures";
|
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId()) + "-1/measures";
|
||||||
Serial.println(POSTURL);
|
Serial.println(POSTURL);
|
||||||
WiFiClient client;
|
WiFiClient client;
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
@ -157,9 +169,9 @@ void sendToServerPM1() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void sendToServerPM2() {
|
void sendToServerPM2(float pm2Value) {
|
||||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||||
+ ", \"pm02\":" + String(pm2)
|
+ ", \"pm02\":" + String(pm2Value/20)
|
||||||
+ ", \"atmp\":" + String(temp_pm2/10)
|
+ ", \"atmp\":" + String(temp_pm2/10)
|
||||||
+ ", \"rhum\":" + String(hum_pm2/10)
|
+ ", \"rhum\":" + String(hum_pm2/10)
|
||||||
+ "}";
|
+ "}";
|
||||||
@ -168,7 +180,7 @@ void sendToServerPM2() {
|
|||||||
digitalWrite(D7, HIGH);
|
digitalWrite(D7, HIGH);
|
||||||
delay(300);
|
delay(300);
|
||||||
Serial.println(payload);
|
Serial.println(payload);
|
||||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "pm2/measures";
|
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId()) + "-2/measures";
|
||||||
Serial.println(POSTURL);
|
Serial.println(POSTURL);
|
||||||
WiFiClient client;
|
WiFiClient client;
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
@ -191,11 +203,10 @@ void sendToServerPM2() {
|
|||||||
void connectToWifi() {
|
void connectToWifi() {
|
||||||
WiFiManager wifiManager;
|
WiFiManager wifiManager;
|
||||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||||
String HOTSPOT = "AIRGRADIENT-" + String(ESP.getChipId(), HEX);
|
String HOTSPOT = "AIRGRADIENT-" + String(ESP.getChipId());
|
||||||
wifiManager.setTimeout(60);
|
wifiManager.setTimeout(60);
|
||||||
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");
|
||||||
delay(6000);
|
delay(6000);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
277
examples/DIY_OUTDOOR_C3_1PP/DIY_OUTDOOR_C3_1PP.ino
Normal file
277
examples/DIY_OUTDOOR_C3_1PP/DIY_OUTDOOR_C3_1PP.ino
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
/*
|
||||||
|
Important: This code is only for the DIY OUTDOOR OPEN AIR Presoldered Kit with the ESP-C3.
|
||||||
|
|
||||||
|
It is a high quality outdoor air quality sensor with dual PM2.5 modules and can send data over Wifi.
|
||||||
|
|
||||||
|
Kits are available: https://www.airgradient.com/open-airgradient/kits/
|
||||||
|
|
||||||
|
The codes needs the following libraries installed:
|
||||||
|
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||||
|
“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/
|
||||||
|
|
||||||
|
Note that below code only works with both PM sensor modules connected.
|
||||||
|
|
||||||
|
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||||
|
|
||||||
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PMS.h"
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#include <WiFiManager.h>
|
||||||
|
|
||||||
|
#define DEBUG true
|
||||||
|
|
||||||
|
HTTPClient client;
|
||||||
|
|
||||||
|
PMS pms1(Serial0);
|
||||||
|
PMS::DATA data1;
|
||||||
|
|
||||||
|
float pm1Value01=0;
|
||||||
|
float pm1Value25=0;
|
||||||
|
float pm1Value10=0;
|
||||||
|
float pm1PCount=0;
|
||||||
|
float pm1temp = 0;
|
||||||
|
float pm1hum = 0;
|
||||||
|
|
||||||
|
PMS pms2(Serial1);
|
||||||
|
PMS::DATA data2;
|
||||||
|
|
||||||
|
float pm2Value01=0;
|
||||||
|
float pm2Value25=0;
|
||||||
|
float pm2Value10=0;
|
||||||
|
float pm2PCount=0;
|
||||||
|
float pm2temp = 0;
|
||||||
|
float pm2hum = 0;
|
||||||
|
|
||||||
|
int countPosition = 0;
|
||||||
|
int targetCount = 20;
|
||||||
|
|
||||||
|
|
||||||
|
String APIROOT = "http://hw.airgradient.com/";
|
||||||
|
|
||||||
|
int loopCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
void IRAM_ATTR isr() {
|
||||||
|
debugln("pushed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// select board LOLIN C3 mini to flash
|
||||||
|
void setup() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Serial.begin(115200);
|
||||||
|
// see https://github.com/espressif/arduino-esp32/issues/6983
|
||||||
|
Serial.setTxTimeoutMs(0); // <<<====== solves the delay issue
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("starting ...");
|
||||||
|
debug("Serial Number: "+ getNormalizedMac());
|
||||||
|
|
||||||
|
// default hardware serial, PMS connector on the right side of the C3 mini on the Open Air
|
||||||
|
Serial0.begin(9600);
|
||||||
|
|
||||||
|
// second hardware serial, PMS connector on the left side of the C3 mini on the Open Air
|
||||||
|
Serial1.begin(9600, SERIAL_8N1, 0, 1);
|
||||||
|
|
||||||
|
// led
|
||||||
|
pinMode(10, OUTPUT);
|
||||||
|
|
||||||
|
// push button
|
||||||
|
pinMode(9, INPUT_PULLUP);
|
||||||
|
attachInterrupt(9, isr, FALLING);
|
||||||
|
|
||||||
|
pinMode(2, OUTPUT);
|
||||||
|
digitalWrite(2, LOW);
|
||||||
|
|
||||||
|
// give the PMSs some time to start
|
||||||
|
countdown(3);
|
||||||
|
|
||||||
|
connectToWifi();
|
||||||
|
sendPing();
|
||||||
|
switchLED(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if(WiFi.status()== WL_CONNECTED) {
|
||||||
|
if (pms1.readUntil(data1, 2000) && pms2.readUntil(data2, 2000)) {
|
||||||
|
pm1Value01=pm1Value01+data1.PM_AE_UG_1_0;
|
||||||
|
pm1Value25=pm1Value25+data1.PM_AE_UG_2_5;
|
||||||
|
pm1Value10=pm1Value10+data1.PM_AE_UG_10_0;
|
||||||
|
// pm1PCount=pm1PCount+data1.PM_RAW_0_3;
|
||||||
|
pm1temp=pm1temp+data1.AMB_TMP;
|
||||||
|
pm1hum=pm1hum+data1.AMB_HUM;
|
||||||
|
pm2Value01=pm2Value01+data2.PM_AE_UG_1_0;
|
||||||
|
pm2Value25=pm2Value25+data2.PM_AE_UG_2_5;
|
||||||
|
pm2Value10=pm2Value10+data2.PM_AE_UG_10_0;
|
||||||
|
// pm2PCount=pm2PCount+data2.PM_RAW_0_3;
|
||||||
|
pm2temp=pm2temp+data2.AMB_TMP;
|
||||||
|
pm2hum=pm2hum+data2.AMB_HUM;
|
||||||
|
countPosition++;
|
||||||
|
if (countPosition==targetCount) {
|
||||||
|
pm1Value01 = pm1Value01 / targetCount;
|
||||||
|
pm1Value25 = pm1Value25 / targetCount;
|
||||||
|
pm1Value10 = pm1Value10 / targetCount;
|
||||||
|
//pm1PCount = pm1PCount / targetCount;
|
||||||
|
pm1temp = pm1temp / targetCount;
|
||||||
|
pm1hum = pm1hum / targetCount;
|
||||||
|
pm2Value01 = pm2Value01 / targetCount;
|
||||||
|
pm2Value25 = pm2Value25 / targetCount;
|
||||||
|
pm2Value10 = pm2Value10 / targetCount;
|
||||||
|
// pm2PCount = pm2PCount / targetCount;
|
||||||
|
pm2temp = pm2temp / targetCount;
|
||||||
|
pm2hum = pm2hum / targetCount;
|
||||||
|
postToServer(pm1Value01, pm1Value25,pm1Value10,pm1PCount, pm1temp,pm1hum,pm2Value01, pm2Value25,pm2Value10,pm2PCount, pm2temp,pm2hum);
|
||||||
|
|
||||||
|
countPosition=0;
|
||||||
|
pm1Value01=0;
|
||||||
|
pm1Value25=0;
|
||||||
|
pm1Value10=0;
|
||||||
|
// pm1PCount=0;
|
||||||
|
pm1temp=0;
|
||||||
|
pm1hum=0;
|
||||||
|
pm2Value01=0;
|
||||||
|
pm2Value25=0;
|
||||||
|
pm2Value10=0;
|
||||||
|
// pm2PCount=0;
|
||||||
|
pm2temp=0;
|
||||||
|
pm2hum=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
countdown(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void switchLED(boolean ledON) {
|
||||||
|
if (ledON) {
|
||||||
|
digitalWrite(10, HIGH);
|
||||||
|
} else {
|
||||||
|
digitalWrite(10, LOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendPing(){
|
||||||
|
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||||
|
+ ", \"boot\":" + loopCount
|
||||||
|
+ "}";
|
||||||
|
sendPayload(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void postToServer(int pm1Value01, int pm1Value25, int pm1Value10, int pm1PCount, float pm1temp, float pm1hum,int pm2Value01, int pm2Value25, int pm2Value10, int pm2PCount, float pm2temp, float pm2hum) {
|
||||||
|
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||||
|
+ ", \"pm01\":" + String((pm1Value01+pm2Value01)/2)
|
||||||
|
+ ", \"pm02\":" + String((pm1Value25+pm2Value25)/2)
|
||||||
|
+ ", \"pm10\":" + String((pm1Value10+pm2Value10)/2)
|
||||||
|
// + ", \"pm003_count\":" + String((pm1PCount+pm2PCount)/2)
|
||||||
|
+ ", \"atmp\":" + String((pm1temp+pm2temp)/20)
|
||||||
|
+ ", \"rhum\":" + String((pm1hum+pm2hum)/20)
|
||||||
|
+ ", \"boot\":" + loopCount
|
||||||
|
+ ", \"channels\": {"
|
||||||
|
+ "\"1\":{"
|
||||||
|
+ "\"pm01\":" + String(pm1Value01)
|
||||||
|
+ ", \"pm02\":" + String(pm1Value25)
|
||||||
|
+ ", \"pm10\":" + String(pm1Value10)
|
||||||
|
// + ", \"pm003_count\":" + String(pm1PCount)
|
||||||
|
+ ", \"atmp\":" + String(pm1temp/10)
|
||||||
|
+ ", \"rhum\":" + String(pm1hum/10)
|
||||||
|
+ "}"
|
||||||
|
+ ", \"2\":{"
|
||||||
|
+ " \"pm01\":" + String(pm1Value01)
|
||||||
|
+ ", \"pm02\":" + String(pm2Value25)
|
||||||
|
+ ", \"pm10\":" + String(pm2Value10)
|
||||||
|
// + ", \"pm003_count\":" + String(pm2PCount)
|
||||||
|
+ ", \"atmp\":" + String(pm2temp/10)
|
||||||
|
+ ", \"rhum\":" + String(pm2hum/10)
|
||||||
|
+ "}"
|
||||||
|
+ "}"
|
||||||
|
+ "}";
|
||||||
|
sendPayload(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendPayload(String payload) {
|
||||||
|
if(WiFi.status()== WL_CONNECTED){
|
||||||
|
switchLED(true);
|
||||||
|
String url = APIROOT + "sensors/airgradient:" + getNormalizedMac() + "/measures";
|
||||||
|
debugln(url);
|
||||||
|
debugln(payload);
|
||||||
|
client.setConnectTimeout(5 * 1000);
|
||||||
|
client.begin(url);
|
||||||
|
client.addHeader("content-type", "application/json");
|
||||||
|
int httpCode = client.POST(payload);
|
||||||
|
debugln(httpCode);
|
||||||
|
client.end();
|
||||||
|
resetWatchdog();
|
||||||
|
switchLED(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
debug("post skipped, not network connection");
|
||||||
|
}
|
||||||
|
loopCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void countdown(int from) {
|
||||||
|
debug("\n");
|
||||||
|
while (from > 0) {
|
||||||
|
debug(String(from--));
|
||||||
|
debug(" ");
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetWatchdog() {
|
||||||
|
digitalWrite(2, HIGH);
|
||||||
|
delay(20);
|
||||||
|
digitalWrite(2, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wifi Manager
|
||||||
|
void connectToWifi() {
|
||||||
|
WiFiManager wifiManager;
|
||||||
|
switchLED(true);
|
||||||
|
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||||
|
String HOTSPOT = "AG-" + String(getNormalizedMac());
|
||||||
|
wifiManager.setTimeout(180);
|
||||||
|
|
||||||
|
|
||||||
|
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||||
|
switchLED(false);
|
||||||
|
Serial.println("failed to connect and hit timeout");
|
||||||
|
delay(6000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
String getNormalizedMac() {
|
||||||
|
String mac = WiFi.macAddress();
|
||||||
|
mac.replace(":", "");
|
||||||
|
mac.toLowerCase();
|
||||||
|
return mac;
|
||||||
|
}
|
338
examples/DIY_OUTDOOR_C3_1PST/DIY_OUTDOOR_C3_1PST.ino
Normal file
338
examples/DIY_OUTDOOR_C3_1PST/DIY_OUTDOOR_C3_1PST.ino
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
/*
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ If you have any questions please visit our forum at https://forum.airgradient.co
|
|||||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||||
https://www.airgradient.com/
|
https://www.airgradient.com/
|
||||||
|
|
||||||
MIT License
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ int hum = 0;
|
|||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
u8g2.setBusClock(100000);
|
||||||
u8g2.begin();
|
u8g2.begin();
|
||||||
updateOLED();
|
updateOLED();
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ This is the code for the AirGradient DIY PRO Air Quality Sensor with an ESP8266
|
|||||||
|
|
||||||
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
||||||
|
|
||||||
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-pro/
|
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-pro-v37/
|
||||||
|
|
||||||
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ If you have any questions please visit our forum at https://forum.airgradient.co
|
|||||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||||
https://www.airgradient.com/
|
https://www.airgradient.com/
|
||||||
|
|
||||||
MIT License
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -52,14 +52,14 @@ NOxGasIndexAlgorithm nox_algorithm;
|
|||||||
uint16_t conditioning_s = 10;
|
uint16_t conditioning_s = 10;
|
||||||
|
|
||||||
// for peristent saving and loading
|
// for peristent saving and loading
|
||||||
int addr = 0;
|
int addr = 4;
|
||||||
byte value;
|
byte value;
|
||||||
|
|
||||||
// Display bottom right
|
// Display bottom right
|
||||||
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
||||||
|
|
||||||
// Replace above if you have display on top left
|
// Replace above if you have display on top left
|
||||||
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE);
|
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE);
|
||||||
|
|
||||||
|
|
||||||
// CONFIGURATION START
|
// CONFIGURATION START
|
||||||
@ -108,7 +108,7 @@ unsigned long previousTempHum = 0;
|
|||||||
float temp = 0;
|
float temp = 0;
|
||||||
int hum = 0;
|
int hum = 0;
|
||||||
|
|
||||||
int buttonConfig=0;
|
int buttonConfig=4;
|
||||||
int lastState = LOW;
|
int lastState = LOW;
|
||||||
int currentState;
|
int currentState;
|
||||||
unsigned long pressedTime = 0;
|
unsigned long pressedTime = 0;
|
||||||
@ -143,7 +143,7 @@ void setup() {
|
|||||||
connectToWifi();
|
connectToWifi();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOLED2("Warming up the", "sensors.", "");
|
updateOLED2("Warming Up", "Serial Number:", String(ESP.getChipId(), HEX));
|
||||||
sgp41.begin(Wire);
|
sgp41.begin(Wire);
|
||||||
ag.CO2_Init();
|
ag.CO2_Init();
|
||||||
ag.PMS_Init();
|
ag.PMS_Init();
|
||||||
@ -232,25 +232,25 @@ void setConfig() {
|
|||||||
inUSAQI = true;
|
inUSAQI = true;
|
||||||
}
|
}
|
||||||
if (buttonConfig == 4) {
|
if (buttonConfig == 4) {
|
||||||
updateOLED2("Temp. in C", "PM in ug/m3", "Display Top");
|
updateOLED2("Temp. in C", "PM in ug/m3", "Display Bottom");
|
||||||
u8g2.setDisplayRotation(U8G2_R0);
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
inF = false;
|
inF = false;
|
||||||
inUSAQI = false;
|
inUSAQI = false;
|
||||||
}
|
}
|
||||||
if (buttonConfig == 5) {
|
if (buttonConfig == 5) {
|
||||||
updateOLED2("Temp. in C", "PM in US AQI", "Display Top");
|
updateOLED2("Temp. in C", "PM in US AQI", "Display Bottom");
|
||||||
u8g2.setDisplayRotation(U8G2_R0);
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
inF = false;
|
inF = false;
|
||||||
inUSAQI = true;
|
inUSAQI = true;
|
||||||
}
|
}
|
||||||
if (buttonConfig == 6) {
|
if (buttonConfig == 6) {
|
||||||
updateOLED2("Temp. in F", "PM in ug/m3", "Display Top");
|
updateOLED2("Temp. in F", "PM in ug/m3", "Display Bottom");
|
||||||
u8g2.setDisplayRotation(U8G2_R0);
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
inF = true;
|
inF = true;
|
||||||
inUSAQI = false;
|
inUSAQI = false;
|
||||||
}
|
}
|
||||||
if (buttonConfig == 7) {
|
if (buttonConfig == 7) {
|
||||||
updateOLED2("Temp. in F", "PM in US AQI", "Display Top");
|
updateOLED2("Temp. in F", "PM in US AQI", "Display Bottom");
|
||||||
u8g2.setDisplayRotation(U8G2_R0);
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
inF = true;
|
inF = true;
|
||||||
inUSAQI = true;
|
inUSAQI = true;
|
||||||
@ -400,19 +400,8 @@ void sendToServer() {
|
|||||||
WiFiManager wifiManager;
|
WiFiManager wifiManager;
|
||||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||||
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
||||||
updateOLED2("60s to connect", "to Wifi Hotspot", HOTSPOT);
|
updateOLED2("90s to connect", "to Wifi Hotspot", HOTSPOT);
|
||||||
wifiManager.setTimeout(60);
|
wifiManager.setTimeout(90);
|
||||||
|
|
||||||
|
|
||||||
WiFiManagerParameter custom_text("<p>This is just a text paragraph</p>");
|
|
||||||
wifiManager.addParameter(&custom_text);
|
|
||||||
|
|
||||||
WiFiManagerParameter parameter("parameterId", "Parameter Label", "default value", 40);
|
|
||||||
wifiManager.addParameter(¶meter);
|
|
||||||
|
|
||||||
|
|
||||||
Serial.println("Parameter 1:");
|
|
||||||
Serial.println(parameter.getValue());
|
|
||||||
|
|
||||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||||
updateOLED2("booting into", "offline mode", "");
|
updateOLED2("booting into", "offline mode", "");
|
||||||
@ -420,9 +409,6 @@ void sendToServer() {
|
|||||||
delay(6000);
|
delay(6000);
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial.println("Parameter 2:");
|
|
||||||
Serial.println(parameter.getValue());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate PM2.5 US AQI
|
// Calculate PM2.5 US AQI
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
||||||
|
|
||||||
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-pro/
|
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-pro-v42/
|
||||||
|
|
||||||
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
||||||
|
|
||||||
@ -12,6 +14,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
|
||||||
|
"Arduino-SHT" by Johannes Winkelmann Version 1.2.2
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
Please set in the code below the configuration parameters.
|
Please set in the code below the configuration parameters.
|
||||||
@ -21,7 +24,7 @@ If you have any questions please visit our forum at https://forum.airgradient.co
|
|||||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||||
https://www.airgradient.com/
|
https://www.airgradient.com/
|
||||||
|
|
||||||
MIT License
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -32,6 +35,9 @@ MIT License
|
|||||||
#include <ESP8266HTTPClient.h>
|
#include <ESP8266HTTPClient.h>
|
||||||
#include <WiFiClient.h>
|
#include <WiFiClient.h>
|
||||||
|
|
||||||
|
#include <EEPROM.h>
|
||||||
|
#include "SHTSensor.h"
|
||||||
|
|
||||||
//#include "SGP30.h"
|
//#include "SGP30.h"
|
||||||
#include <SensirionI2CSgp41.h>
|
#include <SensirionI2CSgp41.h>
|
||||||
#include <NOxGasIndexAlgorithm.h>
|
#include <NOxGasIndexAlgorithm.h>
|
||||||
@ -44,9 +50,15 @@ AirGradient ag = AirGradient();
|
|||||||
SensirionI2CSgp41 sgp41;
|
SensirionI2CSgp41 sgp41;
|
||||||
VOCGasIndexAlgorithm voc_algorithm;
|
VOCGasIndexAlgorithm voc_algorithm;
|
||||||
NOxGasIndexAlgorithm nox_algorithm;
|
NOxGasIndexAlgorithm nox_algorithm;
|
||||||
|
SHTSensor sht;
|
||||||
|
|
||||||
// time in seconds needed for NOx conditioning
|
// time in seconds needed for NOx conditioning
|
||||||
uint16_t conditioning_s = 10;
|
uint16_t conditioning_s = 10;
|
||||||
|
|
||||||
|
// for peristent saving and loading
|
||||||
|
int addr = 4;
|
||||||
|
byte value;
|
||||||
|
|
||||||
// Display bottom right
|
// Display bottom right
|
||||||
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
||||||
|
|
||||||
@ -62,6 +74,12 @@ String APIROOT = "http://hw.airgradient.com/";
|
|||||||
// set to true to switch from Celcius to Fahrenheit
|
// set to true to switch from Celcius to Fahrenheit
|
||||||
boolean inF = false;
|
boolean inF = false;
|
||||||
|
|
||||||
|
// PM2.5 in US AQI (default ug/m3)
|
||||||
|
boolean inUSAQI = false;
|
||||||
|
|
||||||
|
// Display Position
|
||||||
|
boolean displayTop = 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.
|
// 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;
|
boolean connectWIFI=true;
|
||||||
|
|
||||||
@ -85,45 +103,159 @@ const int co2Interval = 5000;
|
|||||||
unsigned long previousCo2 = 0;
|
unsigned long previousCo2 = 0;
|
||||||
int Co2 = 0;
|
int Co2 = 0;
|
||||||
|
|
||||||
const int pm25Interval = 5000;
|
const int pmInterval = 5000;
|
||||||
unsigned long previousPm25 = 0;
|
unsigned long previousPm = 0;
|
||||||
int pm25 = 0;
|
int pm25 = 0;
|
||||||
|
int pm01 = 0;
|
||||||
|
int pm10 = 0;
|
||||||
|
int pm03PCount = 0;
|
||||||
|
|
||||||
const int tempHumInterval = 2500;
|
const int tempHumInterval = 2500;
|
||||||
unsigned long previousTempHum = 0;
|
unsigned long previousTempHum = 0;
|
||||||
float temp = 0;
|
float temp = 0;
|
||||||
int hum = 0;
|
int hum = 0;
|
||||||
|
|
||||||
void setup()
|
int buttonConfig=0;
|
||||||
{
|
int lastState = LOW;
|
||||||
|
int currentState;
|
||||||
|
unsigned long pressedTime = 0;
|
||||||
|
unsigned long releasedTime = 0;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
Serial.println("Hello");
|
||||||
u8g2.begin();
|
u8g2.begin();
|
||||||
updateOLED();
|
sht.init();
|
||||||
|
sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
|
||||||
|
//u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
|
||||||
if (connectWIFI) {
|
EEPROM.begin(512);
|
||||||
connectToWifi();
|
delay(500);
|
||||||
|
|
||||||
|
buttonConfig = String(EEPROM.read(addr)).toInt();
|
||||||
|
if (buttonConfig>3) buttonConfig=0;
|
||||||
|
delay(400);
|
||||||
|
setConfig();
|
||||||
|
Serial.println("buttonConfig: "+String(buttonConfig));
|
||||||
|
updateOLED2("Press Button", "Now for", "Config Menu");
|
||||||
|
delay(2000);
|
||||||
|
pinMode(D7, INPUT_PULLUP);
|
||||||
|
currentState = digitalRead(D7);
|
||||||
|
if (currentState == LOW)
|
||||||
|
{
|
||||||
|
updateOLED2("Entering", "Config Menu", "");
|
||||||
|
delay(3000);
|
||||||
|
lastState = HIGH;
|
||||||
|
setConfig();
|
||||||
|
inConf();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOLED2("Warming up the", "sensors.", "");
|
if (connectWIFI)
|
||||||
|
{
|
||||||
|
connectToWifi();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOLED2("Warming Up", "Serial Number:", String(ESP.getChipId(), HEX));
|
||||||
sgp41.begin(Wire);
|
sgp41.begin(Wire);
|
||||||
ag.CO2_Init();
|
ag.CO2_Init();
|
||||||
ag.PMS_Init();
|
ag.PMS_Init();
|
||||||
ag.TMP_RH_Init(0x44);
|
ag.TMP_RH_Init(0x44);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
currentMillis = millis();
|
currentMillis = millis();
|
||||||
updateTVOC();
|
updateTVOC();
|
||||||
updateOLED();
|
updateOLED();
|
||||||
updateCo2();
|
updateCo2();
|
||||||
updatePm25();
|
updatePm();
|
||||||
updateTempHum();
|
updateTempHum();
|
||||||
sendToServer();
|
sendToServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void inConf(){
|
||||||
|
setConfig();
|
||||||
|
currentState = digitalRead(D7);
|
||||||
|
|
||||||
|
if (currentState){
|
||||||
|
Serial.println("currentState: high");
|
||||||
|
} else {
|
||||||
|
Serial.println("currentState: low");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lastState == HIGH && currentState == LOW) {
|
||||||
|
pressedTime = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(lastState == LOW && currentState == HIGH) {
|
||||||
|
releasedTime = millis();
|
||||||
|
long pressDuration = releasedTime - pressedTime;
|
||||||
|
if( pressDuration < 1000 ) {
|
||||||
|
buttonConfig=buttonConfig+1;
|
||||||
|
if (buttonConfig>3) buttonConfig=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastState == LOW && currentState == LOW){
|
||||||
|
long passedDuration = millis() - pressedTime;
|
||||||
|
if( passedDuration > 4000 ) {
|
||||||
|
// to do
|
||||||
|
// if (buttonConfig==4) {
|
||||||
|
// updateOLED2("Saved", "Release", "Button Now");
|
||||||
|
// delay(1000);
|
||||||
|
// updateOLED2("Starting", "CO2", "Calibration");
|
||||||
|
// delay(1000);
|
||||||
|
// Co2Calibration();
|
||||||
|
// } else {
|
||||||
|
updateOLED2("Saved", "Release", "Button Now");
|
||||||
|
delay(1000);
|
||||||
|
updateOLED2("Rebooting", "in", "5 seconds");
|
||||||
|
delay(5000);
|
||||||
|
EEPROM.write(addr, char(buttonConfig));
|
||||||
|
EEPROM.commit();
|
||||||
|
delay(1000);
|
||||||
|
ESP.restart();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
lastState = currentState;
|
||||||
|
delay(100);
|
||||||
|
inConf();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setConfig() {
|
||||||
|
if (buttonConfig == 0) {
|
||||||
|
updateOLED2("Temp. in C", "PM in ug/m3", "Long Press Saves");
|
||||||
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
inF = false;
|
||||||
|
inUSAQI = false;
|
||||||
|
}
|
||||||
|
if (buttonConfig == 1) {
|
||||||
|
updateOLED2("Temp. in C", "PM in US AQI", "Long Press Saves");
|
||||||
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
inF = false;
|
||||||
|
inUSAQI = true;
|
||||||
|
} else if (buttonConfig == 2) {
|
||||||
|
updateOLED2("Temp. in F", "PM in ug/m3", "Long Press Saves");
|
||||||
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
inF = true;
|
||||||
|
inUSAQI = false;
|
||||||
|
} else if (buttonConfig == 3) {
|
||||||
|
updateOLED2("Temp. in F", "PM in US AQI", "Long Press Saves");
|
||||||
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
inF = true;
|
||||||
|
inUSAQI = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// to do
|
||||||
|
// if (buttonConfig == 8) {
|
||||||
|
// updateOLED2("CO2", "Manual", "Calibration");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
void updateTVOC()
|
void updateTVOC()
|
||||||
{
|
{
|
||||||
uint16_t error;
|
uint16_t error;
|
||||||
@ -139,39 +271,21 @@ void updateTVOC()
|
|||||||
|
|
||||||
delay(1000);
|
delay(1000);
|
||||||
|
|
||||||
compensationT = static_cast<uint16_t>((temp + 45) * 65535 / 175);
|
compensationT = static_cast<uint16_t>((temp + 45) * 65535 / 175);
|
||||||
compensationRh = static_cast<uint16_t>(hum * 65535 / 100);
|
compensationRh = static_cast<uint16_t>(hum * 65535 / 100);
|
||||||
|
|
||||||
// 3. Measure SGP4x signals
|
|
||||||
|
|
||||||
if (conditioning_s > 0) {
|
if (conditioning_s > 0) {
|
||||||
// During NOx conditioning (10s) SRAW NOx will remain 0
|
|
||||||
error = sgp41.executeConditioning(compensationRh, compensationT, srawVoc);
|
error = sgp41.executeConditioning(compensationRh, compensationT, srawVoc);
|
||||||
conditioning_s--;
|
conditioning_s--;
|
||||||
} else {
|
} else {
|
||||||
// Read Measurement
|
|
||||||
error = sgp41.measureRawSignals(compensationRh, compensationT, srawVoc,
|
error = sgp41.measureRawSignals(compensationRh, compensationT, srawVoc,
|
||||||
srawNox);
|
srawNox);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
|
||||||
Serial.print("Error trying to execute measureRawSignals(): ");
|
|
||||||
errorToString(error, errorMessage, 256);
|
|
||||||
Serial.println(errorMessage);
|
|
||||||
} else {
|
|
||||||
Serial.print("SRAW_VOC:");
|
|
||||||
Serial.print(srawVoc);
|
|
||||||
Serial.print("\t");
|
|
||||||
Serial.print("SRAW_NOx:");
|
|
||||||
Serial.println(srawNox);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
if (currentMillis - previousTVOC >= tvocInterval) {
|
||||||
previousTVOC += tvocInterval;
|
previousTVOC += tvocInterval;
|
||||||
TVOC = voc_algorithm.process(srawVoc);
|
TVOC = voc_algorithm.process(srawVoc);
|
||||||
NOX = nox_algorithm.process(srawNox);
|
NOX = nox_algorithm.process(srawNox);
|
||||||
// TVOC = sgp40.getVoclndex();
|
|
||||||
Serial.println(String(TVOC));
|
Serial.println(String(TVOC));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,11 +299,14 @@ void updateCo2()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updatePm25()
|
void updatePm()
|
||||||
{
|
{
|
||||||
if (currentMillis - previousPm25 >= pm25Interval) {
|
if (currentMillis - previousPm >= pmInterval) {
|
||||||
previousPm25 += pm25Interval;
|
previousPm += pmInterval;
|
||||||
|
pm01 = ag.getPM1_Raw();
|
||||||
pm25 = ag.getPM2_Raw();
|
pm25 = ag.getPM2_Raw();
|
||||||
|
pm10 = ag.getPM10_Raw();
|
||||||
|
pm03PCount = ag.getPM0_3Count();
|
||||||
Serial.println(String(pm25));
|
Serial.println(String(pm25));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,9 +315,20 @@ void updateTempHum()
|
|||||||
{
|
{
|
||||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
if (currentMillis - previousTempHum >= tempHumInterval) {
|
||||||
previousTempHum += tempHumInterval;
|
previousTempHum += tempHumInterval;
|
||||||
TMP_RH result = ag.periodicFetchData();
|
|
||||||
temp = result.t;
|
if (sht.readSample()) {
|
||||||
hum = result.rh;
|
Serial.print("SHT:\n");
|
||||||
|
Serial.print(" RH: ");
|
||||||
|
Serial.print(sht.getHumidity(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
Serial.print(" T: ");
|
||||||
|
Serial.print(sht.getTemperature(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
temp = sht.getTemperature();
|
||||||
|
hum = sht.getHumidity();
|
||||||
|
} else {
|
||||||
|
Serial.print("Error in readSample()\n");
|
||||||
|
}
|
||||||
Serial.println(String(temp));
|
Serial.println(String(temp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,8 +338,14 @@ void updateOLED() {
|
|||||||
previousOled += oledInterval;
|
previousOled += oledInterval;
|
||||||
|
|
||||||
String ln3;
|
String ln3;
|
||||||
String ln1 = "PM:" + String(pm25) + " CO2:" + String(Co2);
|
String ln1;
|
||||||
// String ln2 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " TVOC:" + String(TVOC);
|
|
||||||
|
if (inUSAQI) {
|
||||||
|
ln1 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " CO2:" + String(Co2);
|
||||||
|
} else {
|
||||||
|
ln1 = "PM:" + String(pm25) + " CO2:" + String(Co2);
|
||||||
|
}
|
||||||
|
|
||||||
String ln2 = "TVOC:" + String(TVOC) + " NOX:" + String(NOX);
|
String ln2 = "TVOC:" + String(TVOC) + " NOX:" + String(NOX);
|
||||||
|
|
||||||
if (inF) {
|
if (inF) {
|
||||||
@ -224,15 +358,15 @@ void updateOLED() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateOLED2(String ln1, String ln2, String ln3) {
|
void updateOLED2(String ln1, String ln2, String ln3) {
|
||||||
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);
|
||||||
u8g2.drawStr(1, 10, String(ln1).c_str());
|
u8g2.drawStr(1, 10, String(ln1).c_str());
|
||||||
u8g2.drawStr(1, 30, String(ln2).c_str());
|
u8g2.drawStr(1, 30, String(ln2).c_str());
|
||||||
u8g2.drawStr(1, 50, String(ln3).c_str());
|
u8g2.drawStr(1, 50, String(ln3).c_str());
|
||||||
} while ( u8g2.nextPage() );
|
} while ( u8g2.nextPage() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendToServer() {
|
void sendToServer() {
|
||||||
@ -240,7 +374,10 @@ void sendToServer() {
|
|||||||
previoussendToServer += sendToServerInterval;
|
previoussendToServer += sendToServerInterval;
|
||||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||||
+ (Co2 < 0 ? "" : ", \"rco2\":" + String(Co2))
|
+ (Co2 < 0 ? "" : ", \"rco2\":" + String(Co2))
|
||||||
|
+ (pm01 < 0 ? "" : ", \"pm01\":" + String(pm01))
|
||||||
+ (pm25 < 0 ? "" : ", \"pm02\":" + String(pm25))
|
+ (pm25 < 0 ? "" : ", \"pm02\":" + String(pm25))
|
||||||
|
+ (pm10 < 0 ? "" : ", \"pm10\":" + String(pm10))
|
||||||
|
+ (pm03PCount < 0 ? "" : ", \"pm003_count\":" + String(pm03PCount))
|
||||||
+ (TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC))
|
+ (TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC))
|
||||||
+ (NOX < 0 ? "" : ", \"nox_index\":" + String(NOX))
|
+ (NOX < 0 ? "" : ", \"nox_index\":" + String(NOX))
|
||||||
+ ", \"atmp\":" + String(temp)
|
+ ", \"atmp\":" + String(temp)
|
||||||
@ -272,19 +409,8 @@ void sendToServer() {
|
|||||||
WiFiManager wifiManager;
|
WiFiManager wifiManager;
|
||||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||||
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
||||||
updateOLED2("60s to connect", "to Wifi Hotspot", HOTSPOT);
|
updateOLED2("90s to connect", "to Wifi Hotspot", HOTSPOT);
|
||||||
wifiManager.setTimeout(60);
|
wifiManager.setTimeout(90);
|
||||||
|
|
||||||
|
|
||||||
WiFiManagerParameter custom_text("<p>This is just a text paragraph</p>");
|
|
||||||
wifiManager.addParameter(&custom_text);
|
|
||||||
|
|
||||||
WiFiManagerParameter parameter("parameterId", "Parameter Label", "default value", 40);
|
|
||||||
wifiManager.addParameter(¶meter);
|
|
||||||
|
|
||||||
|
|
||||||
Serial.println("Parameter 1:");
|
|
||||||
Serial.println(parameter.getValue());
|
|
||||||
|
|
||||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||||
updateOLED2("booting into", "offline mode", "");
|
updateOLED2("booting into", "offline mode", "");
|
||||||
@ -292,9 +418,6 @@ void sendToServer() {
|
|||||||
delay(6000);
|
delay(6000);
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial.println("Parameter 2:");
|
|
||||||
Serial.println(parameter.getValue());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate PM2.5 US AQI
|
// Calculate PM2.5 US AQI
|
@ -1,248 +0,0 @@
|
|||||||
/*
|
|
||||||
This is the code for the AirGradient DIY PRO Air Quality Sensor with an ESP8266 Microcontroller.
|
|
||||||
|
|
||||||
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
|
||||||
|
|
||||||
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-pro/
|
|
||||||
|
|
||||||
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
|
||||||
|
|
||||||
IMPORTANT: If you use SGP30 modules, you need to remove the Pull up resistors to make it work.
|
|
||||||
See https://www.airgradient.com/open-airgradient/instructions/tvoc-on-airgradient-diy-sensor/
|
|
||||||
|
|
||||||
If you want to use TVOC we strongly recommend you use the AirGradient SGP4x module available in our online shop.
|
|
||||||
|
|
||||||
The codes needs the following libraries installed:
|
|
||||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
|
||||||
“U8g2” by oliver tested with version 2.32.15
|
|
||||||
“SGP30” by Rob Tilaart tested with Version 0.1.5
|
|
||||||
|
|
||||||
Configuration:
|
|
||||||
Please set in the code below the configuration parameters.
|
|
||||||
|
|
||||||
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/
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <AirGradient.h>
|
|
||||||
#include <WiFiManager.h>
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <ESP8266HTTPClient.h>
|
|
||||||
#include <WiFiClient.h>
|
|
||||||
|
|
||||||
#include "SGP30.h"
|
|
||||||
#include <U8g2lib.h>
|
|
||||||
|
|
||||||
AirGradient ag = AirGradient();
|
|
||||||
SGP30 SGP;
|
|
||||||
|
|
||||||
// Display bottom right
|
|
||||||
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
|
||||||
|
|
||||||
// Replace above if you have display on top left
|
|
||||||
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE);
|
|
||||||
|
|
||||||
|
|
||||||
// CONFIGURATION START
|
|
||||||
|
|
||||||
//set to the endpoint you would like to use
|
|
||||||
String APIROOT = "http://hw.airgradient.com/";
|
|
||||||
|
|
||||||
// set to true to switch from Celcius to Fahrenheit
|
|
||||||
boolean inF = false;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// CONFIGURATION END
|
|
||||||
|
|
||||||
|
|
||||||
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 = 0;
|
|
||||||
|
|
||||||
const int co2Interval = 5000;
|
|
||||||
unsigned long previousCo2 = 0;
|
|
||||||
int Co2 = 0;
|
|
||||||
|
|
||||||
const int pm25Interval = 5000;
|
|
||||||
unsigned long previousPm25 = 0;
|
|
||||||
int pm25 = 0;
|
|
||||||
|
|
||||||
const int tempHumInterval = 2500;
|
|
||||||
unsigned long previousTempHum = 0;
|
|
||||||
float temp = 0;
|
|
||||||
int hum = 0;
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
Serial.begin(115200);
|
|
||||||
|
|
||||||
u8g2.begin();
|
|
||||||
updateOLED();
|
|
||||||
|
|
||||||
if (connectWIFI) {
|
|
||||||
connectToWifi();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateOLED2("Warming up the", "sensors.", "");
|
|
||||||
|
|
||||||
Serial.println(SGP.begin());
|
|
||||||
SGP.GenericReset();
|
|
||||||
|
|
||||||
ag.CO2_Init();
|
|
||||||
ag.PMS_Init();
|
|
||||||
ag.TMP_RH_Init(0x44);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
currentMillis = millis();
|
|
||||||
updateTVOC();
|
|
||||||
updateOLED();
|
|
||||||
updateCo2();
|
|
||||||
updatePm25();
|
|
||||||
updateTempHum();
|
|
||||||
sendToServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateTVOC()
|
|
||||||
{
|
|
||||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
|
||||||
previousTVOC += tvocInterval;
|
|
||||||
SGP.measure(true);
|
|
||||||
TVOC = SGP.getTVOC();
|
|
||||||
Serial.println(String(TVOC));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateCo2()
|
|
||||||
{
|
|
||||||
if (currentMillis - previousCo2 >= co2Interval) {
|
|
||||||
previousCo2 += co2Interval;
|
|
||||||
Co2 = ag.getCO2_Raw();
|
|
||||||
Serial.println(String(Co2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updatePm25()
|
|
||||||
{
|
|
||||||
if (currentMillis - previousPm25 >= pm25Interval) {
|
|
||||||
previousPm25 += pm25Interval;
|
|
||||||
pm25 = ag.getPM2_Raw();
|
|
||||||
Serial.println(String(pm25));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateTempHum()
|
|
||||||
{
|
|
||||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
|
||||||
previousTempHum += tempHumInterval;
|
|
||||||
TMP_RH result = ag.periodicFetchData();
|
|
||||||
temp = result.t;
|
|
||||||
hum = result.rh;
|
|
||||||
Serial.println(String(temp));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateOLED() {
|
|
||||||
if (currentMillis - previousOled >= oledInterval) {
|
|
||||||
previousOled += oledInterval;
|
|
||||||
|
|
||||||
String ln3;
|
|
||||||
String ln1 = "PM:" + String(pm25) + " CO2:" + String(Co2);
|
|
||||||
String ln2 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " TVOC:" + String(TVOC);
|
|
||||||
|
|
||||||
if (inF) {
|
|
||||||
ln3 = "F:" + String((temp* 9 / 5) + 32) + " H:" + String(hum)+"%";
|
|
||||||
} else {
|
|
||||||
ln3 = "C:" + String(temp) + " H:" + String(hum)+"%";
|
|
||||||
}
|
|
||||||
updateOLED2(ln1, ln2, ln3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateOLED2(String ln1, String ln2, String ln3) {
|
|
||||||
char buf[9];
|
|
||||||
u8g2.firstPage();
|
|
||||||
u8g2.firstPage();
|
|
||||||
do {
|
|
||||||
u8g2.setFont(u8g2_font_t0_16_tf);
|
|
||||||
u8g2.drawStr(1, 10, String(ln1).c_str());
|
|
||||||
u8g2.drawStr(1, 30, String(ln2).c_str());
|
|
||||||
u8g2.drawStr(1, 50, String(ln3).c_str());
|
|
||||||
} while ( u8g2.nextPage() );
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendToServer() {
|
|
||||||
if (currentMillis - previoussendToServer >= sendToServerInterval) {
|
|
||||||
previoussendToServer += sendToServerInterval;
|
|
||||||
|
|
||||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
|
||||||
+ (Co2 < 0 ? "" : ", \"rco2\":" + String(Co2))
|
|
||||||
+ (pm25 < 0 ? "" : ", \"pm02\":" + String(pm25))
|
|
||||||
+ (TVOC < 0 ? "" : ", \"tvoc\":" + String(TVOC))
|
|
||||||
+ ", \"atmp\":" + String(temp)
|
|
||||||
+ (hum < 0 ? "" : ", \"rhum\":" + String(hum))
|
|
||||||
+ "}";
|
|
||||||
|
|
||||||
if(WiFi.status()== WL_CONNECTED){
|
|
||||||
Serial.println(payload);
|
|
||||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "/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();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Serial.println("WiFi Disconnected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wifi Manager
|
|
||||||
void connectToWifi() {
|
|
||||||
WiFiManager wifiManager;
|
|
||||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
|
||||||
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
|
||||||
updateOLED2("60s to connect", "to Wifi Hotspot", HOTSPOT);
|
|
||||||
wifiManager.setTimeout(60);
|
|
||||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
|
||||||
updateOLED2("booting into", "offline mode", "");
|
|
||||||
Serial.println("failed to connect and hit timeout");
|
|
||||||
delay(6000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate PM2.5 US AQI
|
|
||||||
int PM_TO_AQI_US(int pm02) {
|
|
||||||
if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
|
||||||
else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
|
|
||||||
else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
|
|
||||||
else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
|
|
||||||
else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
|
|
||||||
else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
|
|
||||||
else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
|
|
||||||
else return 500;
|
|
||||||
};
|
|
@ -1,255 +0,0 @@
|
|||||||
/*
|
|
||||||
This is the code for the AirGradient DIY PRO Air Quality Sensor with an ESP8266 Microcontroller with the SGP40 TVOC module from AirGradient.
|
|
||||||
|
|
||||||
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
|
||||||
|
|
||||||
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-pro/
|
|
||||||
|
|
||||||
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
|
||||||
|
|
||||||
The codes needs the following libraries installed:
|
|
||||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
|
||||||
“U8g2” by oliver tested with version 2.32.15
|
|
||||||
“DFRobot_SGP40” by DFRobot tested with Version 1.0.3
|
|
||||||
|
|
||||||
Configuration:
|
|
||||||
Please set in the code below the configuration parameters.
|
|
||||||
|
|
||||||
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/
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <AirGradient.h>
|
|
||||||
#include <WiFiManager.h>
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <ESP8266HTTPClient.h>
|
|
||||||
#include <WiFiClient.h>
|
|
||||||
|
|
||||||
//#include "SGP30.h"
|
|
||||||
#include <DFRobot_SGP40.h>
|
|
||||||
#include <U8g2lib.h>
|
|
||||||
|
|
||||||
AirGradient ag = AirGradient();
|
|
||||||
DFRobot_SGP40 sgp40;
|
|
||||||
|
|
||||||
// Display bottom right
|
|
||||||
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
|
||||||
|
|
||||||
// Replace above if you have display on top left
|
|
||||||
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE);
|
|
||||||
|
|
||||||
|
|
||||||
// CONFIGURATION START
|
|
||||||
|
|
||||||
//set to the endpoint you would like to use
|
|
||||||
String APIROOT = "http://hw.airgradient.com/";
|
|
||||||
|
|
||||||
// set to true to switch from Celcius to Fahrenheit
|
|
||||||
boolean inF = false;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// CONFIGURATION END
|
|
||||||
|
|
||||||
|
|
||||||
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 = 0;
|
|
||||||
|
|
||||||
const int co2Interval = 5000;
|
|
||||||
unsigned long previousCo2 = 0;
|
|
||||||
int Co2 = 0;
|
|
||||||
|
|
||||||
const int pm25Interval = 5000;
|
|
||||||
unsigned long previousPm25 = 0;
|
|
||||||
int pm25 = 0;
|
|
||||||
|
|
||||||
const int tempHumInterval = 2500;
|
|
||||||
unsigned long previousTempHum = 0;
|
|
||||||
float temp = 0;
|
|
||||||
int hum = 0;
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
Serial.begin(115200);
|
|
||||||
|
|
||||||
u8g2.begin();
|
|
||||||
updateOLED();
|
|
||||||
|
|
||||||
if (connectWIFI) {
|
|
||||||
connectToWifi();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateOLED2("Warming up the", "sensors.", "");
|
|
||||||
sgp40.begin();
|
|
||||||
ag.CO2_Init();
|
|
||||||
ag.PMS_Init();
|
|
||||||
ag.TMP_RH_Init(0x44);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
currentMillis = millis();
|
|
||||||
updateTVOC();
|
|
||||||
updateOLED();
|
|
||||||
updateCo2();
|
|
||||||
updatePm25();
|
|
||||||
updateTempHum();
|
|
||||||
sendToServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateTVOC()
|
|
||||||
{
|
|
||||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
|
||||||
previousTVOC += tvocInterval;
|
|
||||||
TVOC = sgp40.getVoclndex();
|
|
||||||
Serial.println(String(TVOC));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateCo2()
|
|
||||||
{
|
|
||||||
if (currentMillis - previousCo2 >= co2Interval) {
|
|
||||||
previousCo2 += co2Interval;
|
|
||||||
Co2 = ag.getCO2_Raw();
|
|
||||||
Serial.println(String(Co2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updatePm25()
|
|
||||||
{
|
|
||||||
if (currentMillis - previousPm25 >= pm25Interval) {
|
|
||||||
previousPm25 += pm25Interval;
|
|
||||||
pm25 = ag.getPM2_Raw();
|
|
||||||
Serial.println(String(pm25));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateTempHum()
|
|
||||||
{
|
|
||||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
|
||||||
previousTempHum += tempHumInterval;
|
|
||||||
TMP_RH result = ag.periodicFetchData();
|
|
||||||
temp = result.t;
|
|
||||||
hum = result.rh;
|
|
||||||
Serial.println(String(temp));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateOLED() {
|
|
||||||
if (currentMillis - previousOled >= oledInterval) {
|
|
||||||
previousOled += oledInterval;
|
|
||||||
|
|
||||||
String ln3;
|
|
||||||
String ln1 = "PM:" + String(pm25) + " CO2:" + String(Co2);
|
|
||||||
String ln2 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " TVOC:" + String(TVOC);
|
|
||||||
|
|
||||||
if (inF) {
|
|
||||||
ln3 = "F:" + String((temp* 9 / 5) + 32) + " H:" + String(hum)+"%";
|
|
||||||
} else {
|
|
||||||
ln3 = "C:" + String(temp) + " H:" + String(hum)+"%";
|
|
||||||
}
|
|
||||||
updateOLED2(ln1, ln2, ln3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateOLED2(String ln1, String ln2, String ln3) {
|
|
||||||
char buf[9];
|
|
||||||
u8g2.firstPage();
|
|
||||||
u8g2.firstPage();
|
|
||||||
do {
|
|
||||||
u8g2.setFont(u8g2_font_t0_16_tf);
|
|
||||||
u8g2.drawStr(1, 10, String(ln1).c_str());
|
|
||||||
u8g2.drawStr(1, 30, String(ln2).c_str());
|
|
||||||
u8g2.drawStr(1, 50, String(ln3).c_str());
|
|
||||||
} while ( u8g2.nextPage() );
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendToServer() {
|
|
||||||
if (currentMillis - previoussendToServer >= sendToServerInterval) {
|
|
||||||
previoussendToServer += sendToServerInterval;
|
|
||||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
|
||||||
+ (Co2 < 0 ? "" : ", \"rco2\":" + String(Co2))
|
|
||||||
+ (pm25 < 0 ? "" : ", \"pm02\":" + String(pm25))
|
|
||||||
+ (TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC))
|
|
||||||
+ ", \"atmp\":" + String(temp)
|
|
||||||
+ (hum < 0 ? "" : ", \"rhum\":" + String(hum))
|
|
||||||
+ "}";
|
|
||||||
|
|
||||||
if(WiFi.status()== WL_CONNECTED){
|
|
||||||
Serial.println(payload);
|
|
||||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "/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();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Serial.println("WiFi Disconnected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wifi Manager
|
|
||||||
void connectToWifi() {
|
|
||||||
WiFiManager wifiManager;
|
|
||||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
|
||||||
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
|
||||||
updateOLED2("60s to connect", "to Wifi Hotspot", HOTSPOT);
|
|
||||||
wifiManager.setTimeout(60);
|
|
||||||
|
|
||||||
|
|
||||||
WiFiManagerParameter custom_text("<p>This is just a text paragraph</p>");
|
|
||||||
wifiManager.addParameter(&custom_text);
|
|
||||||
|
|
||||||
WiFiManagerParameter parameter("parameterId", "Parameter Label", "default value", 40);
|
|
||||||
wifiManager.addParameter(¶meter);
|
|
||||||
|
|
||||||
|
|
||||||
Serial.println("Parameter 1:");
|
|
||||||
Serial.println(parameter.getValue());
|
|
||||||
|
|
||||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
|
||||||
updateOLED2("booting into", "offline mode", "");
|
|
||||||
Serial.println("failed to connect and hit timeout");
|
|
||||||
delay(6000);
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("Parameter 2:");
|
|
||||||
Serial.println(parameter.getValue());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate PM2.5 US AQI
|
|
||||||
int PM_TO_AQI_US(int pm02) {
|
|
||||||
if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
|
||||||
else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
|
|
||||||
else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
|
|
||||||
else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
|
|
||||||
else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
|
|
||||||
else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
|
|
||||||
else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
|
|
||||||
else return 500;
|
|
||||||
};
|
|
@ -10,7 +10,7 @@ The codes needs the following libraries installed:
|
|||||||
"WifiManager by tzapu, tablatronix" tested with Version 2.0.5-alpha
|
"WifiManager by tzapu, tablatronix" tested with Version 2.0.5-alpha
|
||||||
"Adafruit_ILI9341" tested with Version 1.5.10
|
"Adafruit_ILI9341" tested with Version 1.5.10
|
||||||
"Adafruit GFX library" tested with Version 1.10.12 (often automatically installed with above ILI9341 library)
|
"Adafruit GFX library" tested with Version 1.10.12 (often automatically installed with above ILI9341 library)
|
||||||
"ArduinoJSON" by Benoit Blanchon tested with Version 6.18.5
|
"ArduinoJSON" by Benoit Blanchon tested with Version 5.13.5
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
Please set in the code below (line 90-) if you want to display the PM2.5 values in US AQI and temperature in F.
|
Please set in the code below (line 90-) if you want to display the PM2.5 values in US AQI and temperature in F.
|
||||||
@ -21,7 +21,7 @@ If you have any questions please visit our forum at https://forum.airgradient.co
|
|||||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||||
https://www.airgradient.com/
|
https://www.airgradient.com/
|
||||||
|
|
||||||
MIT License
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <WiFiManager.h>
|
#include <WiFiManager.h>
|
||||||
|
754
examples/ONE_V9/ONE_V9.ino
Normal file
754
examples/ONE_V9/ONE_V9.ino
Normal file
@ -0,0 +1,754 @@
|
|||||||
|
/*
|
||||||
|
Important: This code is only for the DIY PRO / AirGradient ONE PCB Version 9 with the ESP-C3 MCU.
|
||||||
|
|
||||||
|
It is a high quality sensor showing PM2.5, CO2, TVOC, NOx, Temperature and Humidity on a small display and LEDbar 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/indoor/
|
||||||
|
|
||||||
|
The codes needs the following libraries installed:
|
||||||
|
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||||
|
“U8g2” by oliver tested with version 2.32.15
|
||||||
|
"Sensirion I2C SGP41" by Sensation Version 0.1.0
|
||||||
|
"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
|
||||||
|
"Adafruit NeoPixel" by Adafruit Version 1.11.0
|
||||||
|
|
||||||
|
Configuration:
|
||||||
|
Please set in the code below the configuration parameters.
|
||||||
|
|
||||||
|
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 <Adafruit_NeoPixel.h>
|
||||||
|
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
|
#include "SHTSensor.h"
|
||||||
|
|
||||||
|
#include <SensirionI2CSgp41.h>
|
||||||
|
|
||||||
|
#include <NOxGasIndexAlgorithm.h>
|
||||||
|
|
||||||
|
#include <VOCGasIndexAlgorithm.h>
|
||||||
|
|
||||||
|
#include <U8g2lib.h>
|
||||||
|
|
||||||
|
#define DEBUG true
|
||||||
|
|
||||||
|
#define I2C_SDA 7
|
||||||
|
#define I2C_SCL 6
|
||||||
|
|
||||||
|
HTTPClient client;
|
||||||
|
|
||||||
|
Adafruit_NeoPixel pixels(11, 10, NEO_GRB + NEO_KHZ800);
|
||||||
|
SensirionI2CSgp41 sgp41;
|
||||||
|
VOCGasIndexAlgorithm voc_algorithm;
|
||||||
|
NOxGasIndexAlgorithm nox_algorithm;
|
||||||
|
SHTSensor sht;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// for peristent saving and loading
|
||||||
|
int addr = 4;
|
||||||
|
byte value;
|
||||||
|
|
||||||
|
// Display bottom right
|
||||||
|
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
const int tempHumInterval = 5000;
|
||||||
|
unsigned long previousTempHum = 0;
|
||||||
|
float temp;
|
||||||
|
int hum;
|
||||||
|
|
||||||
|
int buttonConfig = 0;
|
||||||
|
int lastState = LOW;
|
||||||
|
int currentState;
|
||||||
|
unsigned long pressedTime = 0;
|
||||||
|
unsigned long releasedTime = 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);
|
||||||
|
pixels.begin();
|
||||||
|
pixels.clear();
|
||||||
|
|
||||||
|
Serial1.begin(9600, SERIAL_8N1, 0, 1);
|
||||||
|
Serial0.begin(9600);
|
||||||
|
u8g2.begin();
|
||||||
|
|
||||||
|
updateOLED2("Warming Up", "Serial Number:", String(getNormalizedMac()));
|
||||||
|
sgp41.begin(Wire);
|
||||||
|
delay(300);
|
||||||
|
|
||||||
|
sht.init(Wire);
|
||||||
|
//sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
|
||||||
|
delay(300);
|
||||||
|
|
||||||
|
//init Watchdog
|
||||||
|
pinMode(2, OUTPUT);
|
||||||
|
digitalWrite(2, LOW);
|
||||||
|
|
||||||
|
sensor_S8 = new S8_UART(Serial1);
|
||||||
|
|
||||||
|
EEPROM.begin(512);
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
// push button
|
||||||
|
pinMode(9, INPUT_PULLUP);
|
||||||
|
|
||||||
|
buttonConfig = String(EEPROM.read(addr)).toInt();
|
||||||
|
if (buttonConfig > 7) buttonConfig = 0;
|
||||||
|
delay(400);
|
||||||
|
setConfig();
|
||||||
|
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");
|
||||||
|
delay(2000);
|
||||||
|
currentState = digitalRead(9);
|
||||||
|
if (currentState == LOW) {
|
||||||
|
updateOLED2("Entering", "Config Menu", "");
|
||||||
|
delay(3000);
|
||||||
|
lastState = HIGH;
|
||||||
|
setConfig();
|
||||||
|
inConf();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connectWIFI) connectToWifi();
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
sendPing();
|
||||||
|
Serial.println(F("WiFi connected!"));
|
||||||
|
Serial.println("IP address: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
}
|
||||||
|
updateOLED2("Warming Up", "Serial Number:", String(getNormalizedMac()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
currentMillis = millis();
|
||||||
|
updateTVOC();
|
||||||
|
updateOLED();
|
||||||
|
updateCo2();
|
||||||
|
updatePm();
|
||||||
|
updateTempHum();
|
||||||
|
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;
|
||||||
|
} else {
|
||||||
|
pm01 = -1;
|
||||||
|
pm25 = -1;
|
||||||
|
pm10 = -1;
|
||||||
|
// pm03PCount = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTempHum() {
|
||||||
|
if (currentMillis - previousTempHum >= tempHumInterval) {
|
||||||
|
previousTempHum += tempHumInterval;
|
||||||
|
|
||||||
|
if (sht.readSample()) {
|
||||||
|
temp = sht.getTemperature();
|
||||||
|
hum = sht.getHumidity();
|
||||||
|
} else {
|
||||||
|
Serial.print("Error in readSample()\n");
|
||||||
|
temp = -10001;
|
||||||
|
hum = -10001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateOLED() {
|
||||||
|
if (currentMillis - previousOled >= oledInterval) {
|
||||||
|
previousOled += oledInterval;
|
||||||
|
|
||||||
|
String ln3;
|
||||||
|
String ln1;
|
||||||
|
|
||||||
|
if (inUSAQI) {
|
||||||
|
ln1 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " CO2:" + String(Co2);
|
||||||
|
} else {
|
||||||
|
ln1 = "PM:" + String(pm25) + " CO2:" + String(Co2);
|
||||||
|
}
|
||||||
|
|
||||||
|
String ln2 = "TVOC:" + String(TVOC) + " NOX:" + String(NOX);
|
||||||
|
|
||||||
|
if (inF) {
|
||||||
|
ln3 = "F:" + String((temp * 9 / 5) + 32) + " H:" + String(hum) + "%";
|
||||||
|
} else {
|
||||||
|
ln3 = "C:" + String(temp) + " H:" + String(hum) + "%";
|
||||||
|
}
|
||||||
|
//updateOLED2(ln1, ln2, ln3);
|
||||||
|
updateOLED3();
|
||||||
|
setRGBledCO2color(Co2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inConf() {
|
||||||
|
setConfig();
|
||||||
|
currentState = digitalRead(9);
|
||||||
|
|
||||||
|
if (currentState) {
|
||||||
|
Serial.println("currentState: high");
|
||||||
|
} else {
|
||||||
|
Serial.println("currentState: low");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastState == HIGH && currentState == LOW) {
|
||||||
|
pressedTime = millis();
|
||||||
|
} else if (lastState == LOW && currentState == HIGH) {
|
||||||
|
releasedTime = millis();
|
||||||
|
long pressDuration = releasedTime - pressedTime;
|
||||||
|
if (pressDuration < 1000) {
|
||||||
|
buttonConfig = buttonConfig + 1;
|
||||||
|
if (buttonConfig > 7) buttonConfig = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastState == LOW && currentState == LOW) {
|
||||||
|
long passedDuration = millis() - pressedTime;
|
||||||
|
if (passedDuration > 4000) {
|
||||||
|
updateOLED2("Saved", "Release", "Button Now");
|
||||||
|
delay(1000);
|
||||||
|
updateOLED2("Rebooting", "in", "5 seconds");
|
||||||
|
delay(5000);
|
||||||
|
EEPROM.write(addr, char(buttonConfig));
|
||||||
|
EEPROM.commit();
|
||||||
|
delay(1000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
lastState = currentState;
|
||||||
|
delay(100);
|
||||||
|
inConf();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setConfig() {
|
||||||
|
Serial.println("in setConfig");
|
||||||
|
if (buttonConfig == 0) {
|
||||||
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
updateOLED2("T:C, PM:ug/m3", "LED Bar: on", "Long Press Saves");
|
||||||
|
inF = false;
|
||||||
|
inUSAQI = false;
|
||||||
|
useRGBledBar = true;
|
||||||
|
} else if (buttonConfig == 1) {
|
||||||
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
updateOLED2("T:C, PM:US AQI", "LED Bar: on", "Long Press Saves");
|
||||||
|
inF = false;
|
||||||
|
inUSAQI = true;
|
||||||
|
useRGBledBar = true;
|
||||||
|
} else if (buttonConfig == 2) {
|
||||||
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
updateOLED2("T:F PM:ug/m3", "LED Bar: on", "Long Press Saves");
|
||||||
|
inF = true;
|
||||||
|
inUSAQI = false;
|
||||||
|
useRGBledBar = true;
|
||||||
|
} else if (buttonConfig == 3) {
|
||||||
|
u8g2.setDisplayRotation(U8G2_R0);
|
||||||
|
updateOLED2("T:F PM:US AQI", "LED Bar: on", "Long Press Saves");
|
||||||
|
inF = 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendPing() {
|
||||||
|
String payload = "{\"wifi\":" + String(WiFi.RSSI()) +
|
||||||
|
", \"boot\":" + loopCount +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateOLED2(String ln1, String ln2, String ln3) {
|
||||||
|
char buf[9];
|
||||||
|
u8g2.firstPage();
|
||||||
|
u8g2.firstPage();
|
||||||
|
do {
|
||||||
|
u8g2.setFont(u8g2_font_t0_16_tf);
|
||||||
|
u8g2.drawStr(1, 10, String(ln1).c_str());
|
||||||
|
u8g2.drawStr(1, 30, String(ln2).c_str());
|
||||||
|
u8g2.drawStr(1, 50, String(ln3).c_str());
|
||||||
|
} while (u8g2.nextPage());
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateOLED3() {
|
||||||
|
char buf[9];
|
||||||
|
u8g2.firstPage();
|
||||||
|
u8g2.firstPage();
|
||||||
|
do {
|
||||||
|
|
||||||
|
u8g2.setFont(u8g2_font_t0_16_tf);
|
||||||
|
|
||||||
|
if (inF) {
|
||||||
|
if (temp > -10001) {
|
||||||
|
float tempF = (temp * 9 / 5) + 32;
|
||||||
|
sprintf(buf, "%.1f°F", tempF);
|
||||||
|
} else {
|
||||||
|
sprintf(buf, "-°F");
|
||||||
|
}
|
||||||
|
u8g2.drawUTF8(1, 10, buf);
|
||||||
|
} else {
|
||||||
|
if (temp > -10001) {
|
||||||
|
sprintf(buf, "%.1f°C", temp);
|
||||||
|
} else {
|
||||||
|
sprintf(buf, "-°C");
|
||||||
|
}
|
||||||
|
u8g2.drawUTF8(1, 10, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hum >= 0) {
|
||||||
|
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 {
|
||||||
|
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() {
|
||||||
|
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) +
|
||||||
|
(hum < 0 ? "" : ", \"rhum\":" + String(hum)) +
|
||||||
|
", \"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());
|
||||||
|
updateOLED2("180s to connect", "to Wifi Hotspot", HOTSPOT);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRGBledCO2color(int co2Value) {
|
||||||
|
if (co2Value >= 300 && co2Value < 800) setRGBledColor('g');
|
||||||
|
if (co2Value >= 800 && co2Value < 1000) setRGBledColor('y');
|
||||||
|
if (co2Value >= 1000 && co2Value < 1500) setRGBledColor('o');
|
||||||
|
if (co2Value >= 1500 && co2Value < 2000) setRGBledColor('r');
|
||||||
|
if (co2Value >= 2000 && co2Value < 3000) setRGBledColor('p');
|
||||||
|
if (co2Value >= 3000 && co2Value < 10000) setRGBledColor('z');
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRGBledColor(char color) {
|
||||||
|
if (useRGBledBar) {
|
||||||
|
//pixels.clear();
|
||||||
|
switch (color) {
|
||||||
|
case 'g':
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(0, 255, 0));
|
||||||
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(255, 255, 0));
|
||||||
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(255, 128, 0));
|
||||||
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(255, 0, 0));
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(153, 0, 153));
|
||||||
|
delay(30);
|
||||||
|
pixels.show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
pixels.setPixelColor(i, pixels.Color(102, 0, 0));
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// if nothing else matches, do the default
|
||||||
|
// default is optional
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
int PM_TO_AQI_US(int pm02) {
|
||||||
|
if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
||||||
|
else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
|
||||||
|
else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
|
||||||
|
else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
|
||||||
|
else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
|
||||||
|
else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
|
||||||
|
else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
|
||||||
|
else return 500;
|
||||||
|
};
|
@ -17,7 +17,7 @@ https://www.airgradient.com/
|
|||||||
|
|
||||||
Kits with all required components are available at https://www.airgradient.com/open-airgradient/shop/
|
Kits with all required components are available at https://www.airgradient.com/open-airgradient/shop/
|
||||||
|
|
||||||
MIT License
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AirGradient.h>
|
#include <AirGradient.h>
|
||||||
|
@ -17,7 +17,7 @@ Kits with all required components are available at https://www.airgradient.com/o
|
|||||||
|
|
||||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||||
|
|
||||||
MIT License
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AirGradient.h>
|
#include <AirGradient.h>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
name=AirGradient Air Quality Sensor
|
name=AirGradient Air Quality Sensor
|
||||||
version=2.2.0
|
version=2.4.14
|
||||||
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.
|
||||||
paragraph=Air quality monitoring library supporting the Plantower PMS5003 particle sensor, the Senseair S8 CO2 sensor and the SHT30/31 sensor for humidity and temperature. Kits with all components including a nice enclosure are available in our online shop. You can also connect an OLED display or send the air quality data to the AirGradient platform or any other backend. Optionally you can connect the Sensirion SGP4x TVOC module from AirGradient.
|
paragraph=Air quality monitoring library supporting the Plantower PMS5003 particle sensor, the Senseair S8 CO2 sensor and the SHT3x/SHT4x sensor for humidity and temperature. Kits with all components including a nice enclosure are available in our online shop. You can also connect an OLED display or send the air quality data to the AirGradient platform or any other backend. Optionally you can connect the Sensirion SGP4x TVOC module from AirGradient.
|
||||||
category=Sensors
|
category=Sensors
|
||||||
url=https://www.airgradient.com/open-airgradient/instructions/
|
url=https://www.airgradient.com/open-airgradient/instructions/
|
||||||
architectures=*
|
architectures=*
|
||||||
|
Reference in New Issue
Block a user