Finally success…
TVOC is reporting data now. Still have to keep the stupid serial monitor on to get data from the CO sensor though…
# HELP TVOC
# TYPE TVOC gauge
tvoc{id="testboard",mac="A4:E5:7C:B3:XX:XX"}2
# HELP eCO2
# TYPE eCO2 gauge
eCO2{id="testboard",mac="A4:E5:7C:B3:XX:XX"}400
# HELP H2
# TYPE H2 gauge
H2{id="testboard",mac="A4:E5:7C:B3:XX:XX"}0
# HELP Ethanol
# TYPE Ethanol gauge
Ethanol{id="testboard",mac="A4:E5:7C:B3:XX:XX"}0
# HELP pm02 Particulate Matter PM2.5 value
# TYPE pm02 gauge
pm02{id="testboard",mac="A4:E5:7C:B3:XX:XX"}3
# HELP rco2 CO2 value, in ppm
# TYPE rco2 gauge
rco2{id="testboard",mac="A4:E5:7C:B3:XX:XX"}615
# HELP atmp Temperature, in degrees Fahrenheit
# TYPE atmp gauge
atmp{id="testboard",mac="A4:E5:7C:B3:XX:XX"}75.92
# HELP rhum Relative humidity, in percent
# TYPE rhum gauge
rhum{id="testboard",mac="A4:E5:7C:B3:XX:XX"}41
The sketch:
/**
* 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 "Adafruit_SGP30.h"
#include <Wire.h>
#include "SSD1306Wire.h"
AirGradient ag = AirGradient();
Adafruit_SGP30 SGP;
// Config ----------------------------------------------------------------------
// Optional.
const char* deviceId = "testboard";
// Hardware options for AirGradient DIY sensor.
const bool hasPM = true;
const bool hasCO2 = true;
const bool hasSHT = true;
const bool hasTVOC = true;
// WiFi and IP connection info.
const char* ssid = "XXXXXXX";
const char* password = "XXXXXXX";
const int port = 9925;
// 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(115200);
// Init Display.
display.init();
display.flipScreenVertically();
showTextRectangle("Init", String(ESP.getChipId(),HEX),true);
// Enable enabled sensors.
if (hasTVOC) SGP.begin();
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);
if (hasTVOC) {
Serial.print("Found SGP30 serial #");
Serial.print(SGP.serialnumber[0], HEX);
Serial.print(SGP.serialnumber[1], HEX);
Serial.println(SGP.serialnumber[2], HEX);
}
}
void loop() {
long t = millis();
server.handleClient();
updateScreen(t);
SGP.IAQmeasure();
}
String GenerateMetrics() {
String message = "";
String idString = "{id=\"" + String(deviceId) + "\",mac=\"" + WiFi.macAddress().c_str() + "\"}";
if (hasTVOC) {
int stat = SGP.TVOC;
message += "# HELP TVOC\n";
message += "# TYPE TVOC gauge\n";
message += "tvoc";
message += idString;
message += String(stat);
message += "\n";
}
if (hasTVOC) {
int stat = SGP.eCO2;
message += "# HELP eCO2\n";
message += "# TYPE eCO2 gauge\n";
message += "eCO2";
message += idString;
message += String(stat);
message += "\n";
}
if (hasTVOC) {
int stat = SGP.rawH2;
message += "# HELP H2\n";
message += "# TYPE H2 gauge\n";
message += "H2";
message += idString;
message += String(stat);
message += "\n";
}
if (hasTVOC) {
int stat = SGP.rawEthanol;
message += "# HELP Ethanol\n";
message += "# TYPE Ethanol gauge\n";
message += "Ethanol";
message += idString;
message += String(stat);
message += "\n";
}
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 Fahrenheit\n";
message += "# TYPE atmp gauge\n";
message += "atmp";
message += idString;
message += String((stat.t * 1.8) + 32);
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();
showTextRectangle("TMP", String((stat.t * 1.8) + 32, 1) + "F", false);
}
break;
case 3:
if (hasSHT) {
TMP_RH stat = ag.periodicFetchData();
showTextRectangle("HUM", String(stat.rh) + "%", false);
}
break;
case 4:
if (hasTVOC) {
int stat = SGP.TVOC;
showTextRectangle("TVOC", String(stat), false);
//Serial.print("TVOC "); Serial.print(SGP.TVOC); Serial.println(" ppb\t");
}
break;
case 5:
if (hasTVOC) {
int stat = SGP.eCO2;
showTextRectangle("eCO2", String(stat), false);
//Serial.print("eCO2 "); Serial.print(SGP.eCO2); Serial.println(" ppm");
}
break;
case 6:
if (hasTVOC) {
int stat = SGP.rawH2;
showTextRectangle("H2", String(stat), false);
//Serial.print("rawH2 "); Serial.print(SGP.rawH2); Serial.println(" ppm");
}
break;
case 7:
if (hasTVOC) {
int stat = SGP.rawEthanol;
showTextRectangle("Ethanol", String(stat), false);
//Serial.print("Ethanol "); Serial.print(SGP.rawEthanol); Serial.println(" ppm");
}
break;
}
counter++;
if (counter > 7) counter = 0;
lastUpdate = millis();
}
}