commit 360a789f66c33a40f4941e77c5f8441fd4a03c0a Author: AlexLebens Date: Sun Jan 29 22:49:16 2023 -0700 Init diff --git a/AirGradient-DIY.ino b/AirGradient-DIY.ino new file mode 100644 index 0000000..184d920 --- /dev/null +++ b/AirGradient-DIY.ino @@ -0,0 +1,235 @@ +/** + * This sketch connects an AirGradient DIY sensor to a WiFi network, and runs a + * tiny HTTP server to serve air quality metrics to Prometheus. + */ + +#include +#include +#include +#include + +#include +#include "SSD1306Wire.h" + +AirGradient ag = AirGradient(); + +// Config ---------------------------------------------------------------------- + +// Optional. +const char* deviceId = "ag-dp-1"; + +// set to 'F' to switch display from Celcius to Fahrenheit +char temp_display = 'C'; + +// Hardware options for AirGradient DIY sensor. +const bool hasPM = true; +const bool hasCO2 = true; +const bool hasSHT = true; + +// WiFi and IP connection info. +const char* ssid = "Tengu"; +const char* password = "g1adis3n7"; +const int port = 9926; + +// Uncomment the line below to configure a static IP address. +// #define staticip +#ifdef staticip +IPAddress static_ip(192, 168, 0, 0); +IPAddress gateway(192, 168, 0, 0); +IPAddress subnet(255, 255, 255, 0); +#endif + +// The frequency of measurement updates. +const int updateFrequency = 5000; + +// For housekeeping. +long lastUpdate; +int counter = 0; + +// Config End ------------------------------------------------------------------ + +SSD1306Wire display(0x3c, SDA, SCL); +ESP8266WebServer server(port); + +void setup() { + Serial.begin(9600); + + // Init Display. + display.init(); + display.flipScreenVertically(); + showTextRectangle("Init", String(ESP.getChipId(),HEX),true); + + // Enable enabled sensors. + if (hasPM) ag.PMS_Init(); + if (hasCO2) ag.CO2_Init(); + if (hasSHT) ag.TMP_RH_Init(0x44); + + // Set static IP address if configured. + #ifdef staticip + WiFi.config(static_ip,gateway,subnet); + #endif + + // Set WiFi mode to client (without this it may try to act as an AP). + WiFi.mode(WIFI_STA); + + // Configure Hostname + if ((deviceId != NULL) && (deviceId[0] == '\0')) { + Serial.printf("No Device ID is Defined, Defaulting to board defaults"); + } + else { + wifi_station_set_hostname(deviceId); + WiFi.setHostname(deviceId); + } + + // Setup and wait for WiFi. + WiFi.begin(ssid, password); + Serial.println(""); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + showTextRectangle("Trying to", "connect...", true); + Serial.print("."); + } + + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + Serial.print("MAC address: "); + Serial.println(WiFi.macAddress()); + Serial.print("Hostname: "); + Serial.println(WiFi.hostname()); + server.on("/", HandleRoot); + server.on("/metrics", HandleRoot); + server.onNotFound(HandleNotFound); + + server.begin(); + Serial.println("HTTP server started at ip " + WiFi.localIP().toString() + ":" + String(port)); + showTextRectangle("Listening To", WiFi.localIP().toString() + ":" + String(port),true); +} + +void loop() { + long t = millis(); + + server.handleClient(); + updateScreen(t); +} + +String GenerateMetrics() { + String message = ""; + String idString = "{id=\"" + String(deviceId) + "\",mac=\"" + WiFi.macAddress().c_str() + "\"}"; + + if (hasPM) { + int stat = ag.getPM2_Raw(); + + message += "# HELP pm02 Particulate Matter PM2.5 value\n"; + message += "# TYPE pm02 gauge\n"; + message += "pm02"; + message += idString; + message += String(stat); + message += "\n"; + } + + if (hasCO2) { + int stat = ag.getCO2_Raw(); + + message += "# HELP rco2 CO2 value, in ppm\n"; + message += "# TYPE rco2 gauge\n"; + message += "rco2"; + message += idString; + message += String(stat); + message += "\n"; + } + + if (hasSHT) { + TMP_RH stat = ag.periodicFetchData(); + + message += "# HELP atmp Temperature, in degrees Celsius\n"; + message += "# TYPE atmp gauge\n"; + message += "atmp"; + message += idString; + message += String(stat.t); + message += "\n"; + + message += "# HELP rhum Relative humidity, in percent\n"; + message += "# TYPE rhum gauge\n"; + message += "rhum"; + message += idString; + message += String(stat.rh); + message += "\n"; + } + + return message; +} + +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); +} + +// DISPLAY +void showTextRectangle(String ln1, String ln2, boolean small) { + display.clear(); + display.setTextAlignment(TEXT_ALIGN_LEFT); + if (small) { + display.setFont(ArialMT_Plain_16); + } else { + display.setFont(ArialMT_Plain_24); + } + display.drawString(32, 16, ln1); + display.drawString(32, 36, ln2); + display.display(); +} + +void updateScreen(long now) { + if ((now - lastUpdate) > updateFrequency) { + // Take a measurement at a fixed interval. + switch (counter) { + case 0: + if (hasPM) { + int stat = ag.getPM2_Raw(); + showTextRectangle("PM2",String(stat),false); + } + break; + case 1: + if (hasCO2) { + int stat = ag.getCO2_Raw(); + showTextRectangle("CO2", String(stat), false); + } + break; + case 2: + if (hasSHT) { + TMP_RH stat = ag.periodicFetchData(); + if (temp_display == 'F' || temp_display == 'f') { + showTextRectangle("TMP", String((stat.t * 9 / 5) + 32, 1) + "F", false); + } else { + showTextRectangle("TMP", String(stat.t, 1) + "C", false); + } + } + break; + case 3: + if (hasSHT) { + TMP_RH stat = ag.periodicFetchData(); + showTextRectangle("HUM", String(stat.rh) + "%", false); + } + break; + } + counter++; + if (counter > 3) counter = 0; + lastUpdate = millis(); + } +} \ No newline at end of file