235 lines
5.9 KiB
C++
235 lines
5.9 KiB
C++
/**
|
|
* 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 <AirGradient.h>
|
|
#include <ESP8266WiFi.h>
|
|
#include <ESP8266WebServer.h>
|
|
#include <WiFiClient.h>
|
|
|
|
#include <Wire.h>
|
|
#include "SSD1306Wire.h"
|
|
|
|
AirGradient ag = AirGradient();
|
|
|
|
// Config ----------------------------------------------------------------------
|
|
|
|
// Optional.
|
|
const char* deviceId = "ag-br-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();
|
|
}
|
|
} |