first commit
This commit is contained in:
parent
44eb267449
commit
11ab5d1bc4
|
@ -0,0 +1,497 @@
|
|||
// Works with AirGradient library version 2.4.15
|
||||
|
||||
|
||||
#include <AirGradient.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFiClient.h>
|
||||
|
||||
#include <EEPROM.h>
|
||||
|
||||
//#include "SGP30.h"
|
||||
#include <SensirionI2CSgp41.h>
|
||||
#include <NOxGasIndexAlgorithm.h>
|
||||
#include <VOCGasIndexAlgorithm.h>
|
||||
|
||||
|
||||
#include <U8g2lib.h>
|
||||
|
||||
AirGradient ag = AirGradient();
|
||||
SensirionI2CSgp41 sgp41;
|
||||
VOCGasIndexAlgorithm voc_algorithm;
|
||||
NOxGasIndexAlgorithm nox_algorithm;
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
|
||||
// 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.
|
||||
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;
|
||||
int NOX = 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;
|
||||
|
||||
int buttonConfig=4;
|
||||
int lastState = LOW;
|
||||
int currentState;
|
||||
unsigned long pressedTime = 0;
|
||||
unsigned long releasedTime = 0;
|
||||
|
||||
String payload = "";
|
||||
|
||||
//Custom stuff
|
||||
int port = 9091;
|
||||
ESP8266WebServer server(port);
|
||||
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Hello");
|
||||
u8g2.begin();
|
||||
//u8g2.setDisplayRotation(U8G2_R0);
|
||||
|
||||
EEPROM.begin(512);
|
||||
delay(500);
|
||||
|
||||
buttonConfig = String(EEPROM.read(addr)).toInt();
|
||||
setConfig();
|
||||
|
||||
updateOLED2("Press Button", "Now for", "Config Menu");
|
||||
delay(2000);
|
||||
|
||||
currentState = digitalRead(D7);
|
||||
if (currentState == HIGH)
|
||||
{
|
||||
updateOLED2("Entering", "Config Menu", "");
|
||||
delay(3000);
|
||||
lastState = LOW;
|
||||
inConf();
|
||||
}
|
||||
|
||||
if (connectWIFI)
|
||||
{
|
||||
connectToWifi();
|
||||
}
|
||||
|
||||
updateOLED2("Warming Up", "Serial Number:", String(ESP.getChipId(), HEX));
|
||||
sgp41.begin(Wire);
|
||||
ag.CO2_Init();
|
||||
ag.PMS_Init();
|
||||
ag.TMP_RH_Init(0x44);
|
||||
|
||||
|
||||
//Custom
|
||||
server.on("/", HandleRoot);
|
||||
server.on("/metrics", HandleRoot);
|
||||
server.onNotFound(HandleNotFound);
|
||||
server.begin();
|
||||
Serial.println("HTTP server started at ip " + WiFi.localIP().toString() + ":" + String(port));
|
||||
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
currentMillis = millis();
|
||||
updateTVOC();
|
||||
updateOLED();
|
||||
updateCo2();
|
||||
updatePm25();
|
||||
updateTempHum();
|
||||
sendToServer();
|
||||
//Custom
|
||||
server.handleClient();
|
||||
}
|
||||
|
||||
|
||||
//Custom
|
||||
void HandleRoot() {
|
||||
server.send(200, "text/plain", GenerateMetrics() );
|
||||
}
|
||||
void HandleNotFound() {
|
||||
String message = "File Not Found\n\n";
|
||||
message += "URI: ";
|
||||
message += server.uri();
|
||||
message += "\nMethod: ";
|
||||
message += (server.method() == HTTP_GET) ? "GET" : "POST";
|
||||
message += "\nArguments: ";
|
||||
message += server.args();
|
||||
message += "\n";
|
||||
for (uint i = 0; i < server.args(); i++) {
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
server.send(404, "text/html", message);
|
||||
}
|
||||
String GenerateMetrics() {
|
||||
String message = "";
|
||||
String idString = "{id=\"" + String(ESP.getChipId(), HEX) + "\",mac=\"" + WiFi.macAddress().c_str() + "\"}";
|
||||
|
||||
|
||||
//PM
|
||||
message += "# HELP pm02 Particulate Matter PM2.5 value, in micrograms per cubic meter\n";
|
||||
message += "# TYPE pm02 gauge\n";
|
||||
message += "pm02";
|
||||
message += idString;
|
||||
message += String(pm25);
|
||||
message += "\n";
|
||||
|
||||
//co2
|
||||
message += "# HELP rco2 CO2 value, in ppm\n";
|
||||
message += "# TYPE rco2 gauge\n";
|
||||
message += "rco2";
|
||||
message += idString;
|
||||
message += String(Co2);
|
||||
message += "\n";
|
||||
|
||||
//Temp
|
||||
message += "# HELP atmp Temperature, in degrees Celsius\n";
|
||||
message += "# TYPE atmp gauge\n";
|
||||
message += "atmp";
|
||||
message += idString;
|
||||
message += String(temp);
|
||||
message += "\n";
|
||||
|
||||
//Humidity
|
||||
message += "# HELP rhum Relative humidity, in percent\n";
|
||||
message += "# TYPE rhum gauge\n";
|
||||
message += "rhum";
|
||||
message += idString;
|
||||
message += String(hum);
|
||||
message += "\n";
|
||||
|
||||
//NOX
|
||||
message += "# HELP nox Exhaust gases, in ppmv\n";
|
||||
message += "# TYPE nox gauge\n";
|
||||
message += "nox";
|
||||
message += idString;
|
||||
message += String(NOX);
|
||||
message += "\n";
|
||||
//TVOC
|
||||
message += "# HELP tvoc Total Volatile Organic Compounds, based on relative index 30\n";
|
||||
message += "# TYPE tvoc gauge\n";
|
||||
message += "tvoc";
|
||||
message += idString;
|
||||
message += String(TVOC);
|
||||
message += "\n";
|
||||
|
||||
//DONE NOX TVOC pm25 Co2 temp hum
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void inConf(){
|
||||
setConfig();
|
||||
currentState = digitalRead(D7);
|
||||
|
||||
if(lastState == LOW && currentState == HIGH) {
|
||||
pressedTime = millis();
|
||||
}
|
||||
|
||||
else if(lastState == HIGH && currentState == LOW) {
|
||||
releasedTime = millis();
|
||||
long pressDuration = releasedTime - pressedTime;
|
||||
if( pressDuration < 1000 ) {
|
||||
buttonConfig=buttonConfig+1;
|
||||
if (buttonConfig>7) buttonConfig=0;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastState == HIGH && currentState == HIGH){
|
||||
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", "Display Top");
|
||||
u8g2.setDisplayRotation(U8G2_R2);
|
||||
inF = false;
|
||||
inUSAQI = false;
|
||||
}
|
||||
if (buttonConfig == 1) {
|
||||
updateOLED2("Temp. in C", "PM in US AQI", "Display Top");
|
||||
u8g2.setDisplayRotation(U8G2_R2);
|
||||
inF = false;
|
||||
inUSAQI = true;
|
||||
}
|
||||
if (buttonConfig == 2) {
|
||||
updateOLED2("Temp. in F", "PM in ug/m3", "Display Top");
|
||||
u8g2.setDisplayRotation(U8G2_R2);
|
||||
inF = true;
|
||||
inUSAQI = false;
|
||||
}
|
||||
if (buttonConfig == 3) {
|
||||
updateOLED2("Temp. in F", "PM in US AQI", "Display Top");
|
||||
u8g2.setDisplayRotation(U8G2_R2);
|
||||
inF = true;
|
||||
inUSAQI = true;
|
||||
}
|
||||
if (buttonConfig == 4) {
|
||||
updateOLED2("Temp. in C", "PM in ug/m3", "Display Bottom");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = false;
|
||||
inUSAQI = false;
|
||||
}
|
||||
if (buttonConfig == 5) {
|
||||
updateOLED2("Temp. in C", "PM in US AQI", "Display Bottom");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = false;
|
||||
inUSAQI = true;
|
||||
}
|
||||
if (buttonConfig == 6) {
|
||||
updateOLED2("Temp. in F", "PM in ug/m3", "Display Bottom");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = true;
|
||||
inUSAQI = false;
|
||||
}
|
||||
if (buttonConfig == 7) {
|
||||
updateOLED2("Temp. in F", "PM in US AQI", "Display Bottom");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = true;
|
||||
inUSAQI = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// to do
|
||||
// if (buttonConfig == 8) {
|
||||
// updateOLED2("CO2", "Manual", "Calibration");
|
||||
// }
|
||||
}
|
||||
|
||||
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;
|
||||
TVOC = voc_algorithm.process(srawVoc);
|
||||
NOX = nox_algorithm.process(srawNox);
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||
+ (Co2 < 0 ? "" : ", \"rco2\":" + String(Co2))
|
||||
+ (pm25 < 0 ? "" : ", \"pm02\":" + String(pm25))
|
||||
+ (TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC))
|
||||
+ (NOX < 0 ? "" : ", \"nox_index\":" + String(NOX))
|
||||
+ ", \"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("90s to connect", "to Wifi Hotspot", HOTSPOT);
|
||||
wifiManager.setTimeout(90);
|
||||
|
||||
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;
|
||||
};
|
Loading…
Reference in New Issue