Created
May 19, 2022 02:20
-
-
Save jwalanta/3f62724c687fca5d7d3b9e3206b236dc to your computer and use it in GitHub Desktop.
AirGradient code with Prometheus metrics
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Modified version of the AirGradient code at | |
https://github.com/airgradienthq/arduino/blob/master/examples/C02_PM_SHT_OLED_WIFI/C02_PM_SHT_OLED_WIFI.ino | |
## Changes from the original code ## | |
- Instead of sending the metrics to the server, this version starts a web server and exposes Prometheus | |
compatible metrics at /metrics | |
- Instead of cycling the AQI and CO2 metrics on the display every minute, show all of them with smaller font | |
-=-=-=-=-=-=-= | |
This is the code for the AirGradient DIY 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. | |
For build instructions please visit https://www.airgradient.com/diy/ | |
Compatible with the following sensors: | |
Plantower PMS5003 (Fine Particle Sensor) | |
SenseAir S8 (CO2 Sensor) | |
SHT30/31 (Temperature/Humidity Sensor) | |
Please install ESP8266 board manager (tested with version 3.0.0) | |
The codes needs the following libraries installed: | |
"WifiManager by tzapu, tablatronix" tested with Version 2.0.3-alpha | |
"ESP8266 and ESP32 OLED driver for SSD1306 displays by ThingPulse, Fabrice Weinberg" tested with Version 4.1.0 | |
If you have any questions please visit our forum at https://forum.airgradient.com/ | |
Configuration: | |
Please set in the code below which sensor you are using and if you want to connect it to WiFi. | |
You can also switch PM2.5 from ug/m3 to US AQI and Celcius to Fahrenheit | |
If you are a school or university contact us for a free trial on the AirGradient platform. | |
https://www.airgradient.com/schools/ | |
Kits with all required components are available at https://www.airgradient.com/diyshop/ | |
MIT License | |
*/ | |
#include <AirGradient.h> | |
#include <WiFiManager.h> | |
#include <ESP8266WiFi.h> | |
#include <ESP8266WebServer.h> | |
#include <Wire.h> | |
#include "SSD1306Wire.h" | |
AirGradient ag = AirGradient(); | |
SSD1306Wire display(0x3c, SDA, SCL); | |
// update frequency | |
int updateFreq = 30 * 1000; // 30 seconds | |
int lastUpdate = 0; | |
// current values | |
int currentPM, currentCO2; | |
TMP_RH currentSHT; | |
// web server | |
int port = 80; | |
ESP8266WebServer server(port); | |
// logging | |
String serialStr = ""; | |
void setup() { | |
Serial.begin(9600); | |
display.init(); | |
display.flipScreenVertically(); | |
display.setTextAlignment(TEXT_ALIGN_LEFT); | |
display.setFont(ArialMT_Plain_10); | |
display.drawString(32, 16, "AirGradient"); | |
display.drawString(32, 26, String(ESP.getChipId(), HEX)); | |
String macAddr = WiFi.macAddress().c_str(); | |
macAddr.replace(":",""); | |
display.drawString(32, 36, macAddr); | |
display.display(); | |
ag.PMS_Init(); | |
ag.CO2_Init(); | |
ag.TMP_RH_Init(0x44); | |
connectToWifi(); | |
display.drawString(32, 46, "WiFi OK!"); | |
display.display(); | |
delay(2000); | |
server.on("/", handleRoot); | |
server.on("/metrics", handleMetrics); | |
server.onNotFound(handleNotFound); | |
server.begin(); | |
} | |
void gatherData() { | |
currentPM = ag.getPM2_Raw(); | |
currentCO2 = ag.getCO2_Raw(); | |
currentSHT = ag.periodicFetchData(); | |
} | |
void displayData() { | |
display.clear(); | |
display.setTextAlignment(TEXT_ALIGN_LEFT); | |
// labels | |
display.setFont(ArialMT_Plain_10); | |
display.drawString(32, 36, "AQI"); | |
display.drawString(32, 52, "CO2"); | |
// values | |
String line = ""; | |
line = String(int(currentSHT.t * 9 / 5) + 32) + "F " + String(currentSHT.rh) + "%"; | |
display.setFont(ArialMT_Plain_16); | |
display.drawString(32, 16, line); | |
line = String(PM_TO_AQI_US(currentPM)); | |
display.setFont(ArialMT_Plain_16); | |
display.drawString(55, 32, line); | |
line = String(currentCO2); | |
display.setFont(ArialMT_Plain_16); | |
display.drawString(55, 48, line); | |
display.display(); | |
if (currentCO2 > 1000 || PM_TO_AQI_US(currentPM) > 200) { | |
for (int i = 0; i < 3; i++) { | |
delay(500); | |
display.invertDisplay(); | |
delay(500); | |
display.normalDisplay(); | |
} | |
} | |
} | |
String composeTextMetrics() { | |
String message = ""; | |
message += "Device ID: " + String(ESP.getChipId(), HEX) + "\n"; | |
message += "Mac Address: " + String(WiFi.macAddress().c_str()) + "\n"; | |
message += "Temperature: " + String((currentSHT.t * 9 / 5) + 32) + "F (" + String(currentSHT.t) + "C)\n"; | |
message += "Humidity: " + String(currentSHT.rh) + "%\n"; | |
message += "PM2.5: " + String(currentPM) + "\n"; | |
message += "AQI: " + String(PM_TO_AQI_US(currentPM)) + "\n"; | |
message += "CO2: " + String(currentCO2) + "\n"; | |
return message; | |
} | |
String composePrometheusMetrics() { | |
String message = ""; | |
String labels = "{id=\"" + String(ESP.getChipId(), HEX) + "\",mac=\"" + WiFi.macAddress().c_str() + "\"}"; | |
// wifi rssi | |
message += "# HELP wifi WiFi RSSI\n"; | |
message += "# TYPE wifi gauge\n"; | |
message += "wifi"; | |
message += labels; | |
message += String(WiFi.RSSI()); | |
message += "\n"; | |
// temp celcius | |
message += "# HELP tempc Temperature in Celcius\n"; | |
message += "# TYPE tempc gauge\n"; | |
message += "tempc"; | |
message += labels; | |
message += String(currentSHT.t); | |
message += "\n"; | |
// temp fahrenheit | |
message += "# HELP tempf Temperature in Fahrenheit\n"; | |
message += "# TYPE tempf gauge\n"; | |
message += "tempf"; | |
message += labels; | |
message += String((currentSHT.t * 9 / 5) + 32); | |
message += "\n"; | |
// humidity | |
message += "# HELP humidity Relative humidity in percentage\n"; | |
message += "# TYPE humidity gauge\n"; | |
message += "humidity"; | |
message += labels; | |
message += String(currentSHT.rh); | |
message += "\n"; | |
// pm2 | |
message += "# HELP pm25 Particulate Matter PM2.5 value\n"; | |
message += "# TYPE pm25 gauge\n"; | |
message += "pm25"; | |
message += labels; | |
message += String(currentPM); | |
message += "\n"; | |
// aqi | |
message += "# HELP aqi Air Quality Index (AQI) value\n"; | |
message += "# TYPE aqi gauge\n"; | |
message += "aqi"; | |
message += labels; | |
message += String(PM_TO_AQI_US(currentPM)); | |
message += "\n"; | |
// co2 | |
message += "# HELP co2 CO2 value in ppm\n"; | |
message += "# TYPE co2 gauge\n"; | |
message += "co2"; | |
message += labels; | |
message += String(currentCO2); | |
message += "\n"; | |
return message; | |
} | |
void handleNotFound() { | |
String message = "404 Not Found\n\n"; | |
server.send(404, "text/plain", message); | |
} | |
void handleRoot() { | |
server.send(200, "text/plain", composeTextMetrics()); | |
} | |
void handleMetrics() { | |
server.send(200, "text/plain", composePrometheusMetrics()); | |
} | |
void loop() { | |
if (lastUpdate == 0 || (millis() - lastUpdate) > updateFreq) { | |
// gather data every updateFreq and display | |
gatherData(); | |
// display data on display | |
displayData(); | |
// display data on serial | |
serialStr = String(millis()) + " Updating data"; | |
Serial.println(serialStr); | |
Serial.println(composeTextMetrics()); | |
lastUpdate = millis(); | |
} | |
server.handleClient(); | |
} | |
// Wifi Manager | |
void connectToWifi() { | |
WiFiManager wifiManager; | |
//WiFi.disconnect(); //to delete previous saved hotspot | |
String HOTSPOT = "AIRGRADIENT-" + String(ESP.getChipId(), HEX); | |
wifiManager.setTimeout(120); | |
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) { | |
Serial.println("failed to connect and hit timeout"); | |
delay(3000); | |
ESP.restart(); | |
delay(5000); | |
} | |
} | |
// 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; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment