Add sgp30 sensor
This commit is contained in:
parent
d70c4e8e30
commit
c34cd4d5cc
|
@ -0,0 +1,2 @@
|
||||||
|
.pio
|
||||||
|
.vscode/
|
|
@ -0,0 +1,24 @@
|
||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env]
|
||||||
|
platform = espressif8266
|
||||||
|
board = huzzah
|
||||||
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
Wire
|
||||||
|
SPI
|
||||||
|
ESP8266WiFi
|
||||||
|
adafruit/Adafruit SGP30 Sensor@^2.0.0
|
||||||
|
adafruit/Adafruit DHT Unified@^1.0.0
|
||||||
|
adafruit/DHT sensor library@^1.4.4
|
||||||
|
|
||||||
|
|
||||||
|
[env:huzzah]
|
|
@ -0,0 +1,15 @@
|
||||||
|
{ pkgs ? import <nixpkgs> { } }:
|
||||||
|
|
||||||
|
with pkgs;
|
||||||
|
|
||||||
|
mkShell rec {
|
||||||
|
nativeBuildInputs = [
|
||||||
|
platformio
|
||||||
|
python39
|
||||||
|
xdg-user-dirs
|
||||||
|
];
|
||||||
|
buildInputs = [
|
||||||
|
|
||||||
|
];
|
||||||
|
LD_LIBRARY_PATH = lib.makeLibraryPath buildInputs;
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
/config.h
|
config.h
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
//#include <Android.h>
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
#include <DHTesp.h>
|
#include <DHT.h>
|
||||||
|
#include <DHT_U.h>
|
||||||
|
#include <Adafruit_SGP30.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
@ -14,40 +17,77 @@ enum LogLevel {
|
||||||
};
|
};
|
||||||
|
|
||||||
void setup_dht_sensor();
|
void setup_dht_sensor();
|
||||||
|
void setup_sgp_sensor();
|
||||||
void setup_wifi();
|
void setup_wifi();
|
||||||
void setup_http_server();
|
void setup_http_server();
|
||||||
|
void handle_http_root();
|
||||||
|
void handle_http_metrics();
|
||||||
|
void handle_http_not_found();
|
||||||
|
void log_request();
|
||||||
|
void read_humidity_sensor();
|
||||||
|
void read_temperature_sensor();
|
||||||
|
void read_heat_index();
|
||||||
|
void read_sgp();
|
||||||
void handle_http_home_client();
|
void handle_http_home_client();
|
||||||
void handle_http_metrics_client();
|
void handle_http_metrics_client();
|
||||||
void read_sensors(boolean force=false);
|
void read_sensors(boolean force=false);
|
||||||
bool read_sensor(float (*function)(), float *value);
|
bool read_sensor(float (*function)(), float *value);
|
||||||
void log(char const *message, LogLevel level=LogLevel::INFO);
|
void log(char const *message, LogLevel level=LogLevel::INFO);
|
||||||
|
void get_http_method_name(char *name, size_t name_length, HTTPMethod method);
|
||||||
|
|
||||||
|
DHT dht_sensor(DHT_PIN, DHT_TYPE);
|
||||||
|
Adafruit_SGP30 sgp_sensor;
|
||||||
|
|
||||||
DHTesp dht_sensor;
|
|
||||||
ESP8266WebServer http_server(HTTP_SERVER_PORT);
|
ESP8266WebServer http_server(HTTP_SERVER_PORT);
|
||||||
|
|
||||||
|
char sgp30_serial[13];
|
||||||
float humidity, temperature, heat_index;
|
float humidity, temperature, heat_index;
|
||||||
|
uint32_t sgp30_counter = 0;
|
||||||
|
uint16_t tvoc, co2, h2, ethanol;
|
||||||
uint32_t previous_read_time = 0;
|
uint32_t previous_read_time = 0;
|
||||||
|
|
||||||
void setup(void) {
|
void setup(void) {
|
||||||
char message[128];
|
char message[128];
|
||||||
|
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
|
|
||||||
setup_dht_sensor();
|
setup_dht_sensor();
|
||||||
|
setup_sgp_sensor();
|
||||||
|
|
||||||
|
// Test all the sensors
|
||||||
|
read_sensors(true);
|
||||||
|
|
||||||
setup_wifi();
|
setup_wifi();
|
||||||
|
|
||||||
setup_http_server();
|
setup_http_server();
|
||||||
|
|
||||||
snprintf(message, 128, "Prometheus namespace: %s", PROM_NAMESPACE);
|
snprintf(message, 128, "Prometheus namespace: %s", PROM_NAMESPACE);
|
||||||
log(message);
|
log(message);
|
||||||
log("Setup done");
|
log("Setup done");
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_dht_sensor() {
|
void setup_dht_sensor() {
|
||||||
log("Setting up DHT sensor");
|
log("Setting up DHT sensor...");
|
||||||
dht_sensor.setup(DHT_PIN, DHTesp::DHT_TYPE);
|
dht_sensor.begin();
|
||||||
delay(dht_sensor.getMinimumSamplingPeriod());
|
delay(1000);
|
||||||
// Test read
|
|
||||||
read_sensors(true);
|
|
||||||
log("DHT sensor ready", LogLevel::DEBUG);
|
log("DHT sensor ready", LogLevel::DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup_sgp_sensor() {
|
||||||
|
log("Setting up SGP30 sensor...");
|
||||||
|
if (!sgp_sensor.begin()) {
|
||||||
|
log("SGP30 sensor failed to begin!", LogLevel::ERROR);
|
||||||
|
log("Failed to setup SGP30 sensor!", LogLevel::ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(sgp30_serial, 13, "%x%x%x", sgp_sensor.serialnumber[0], sgp_sensor.serialnumber[1], sgp_sensor.serialnumber[2]);
|
||||||
|
char message[32];
|
||||||
|
snprintf(message, 32, "SGP30 serial #%s", sgp30_serial);
|
||||||
|
|
||||||
|
sgp_sensor.softReset();
|
||||||
|
}
|
||||||
|
|
||||||
void setup_wifi() {
|
void setup_wifi() {
|
||||||
char message[128];
|
char message[128];
|
||||||
log("Setting up Wi-Fi");
|
log("Setting up Wi-Fi");
|
||||||
|
@ -107,6 +147,7 @@ void setup_wifi() {
|
||||||
snprintf(message, 128, "Secondary DNS server: %s", WiFi.dnsIP(1).toString().c_str());
|
snprintf(message, 128, "Secondary DNS server: %s", WiFi.dnsIP(1).toString().c_str());
|
||||||
log(message);
|
log(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_http_server() {
|
void setup_http_server() {
|
||||||
char message[128];
|
char message[128];
|
||||||
log("Setting up HTTP server");
|
log("Setting up HTTP server");
|
||||||
|
@ -139,12 +180,17 @@ void handle_http_root() {
|
||||||
|
|
||||||
void handle_http_metrics() {
|
void handle_http_metrics() {
|
||||||
log_request();
|
log_request();
|
||||||
static size_t const BUFSIZE = 1024;
|
static size_t const BUFSIZE = 2000;
|
||||||
static char const *response_template =
|
static char const *response_template =
|
||||||
"# HELP " PROM_NAMESPACE "_info Metadata about the device.\n"
|
"# HELP " PROM_NAMESPACE "_info Metadata about the device.\n"
|
||||||
"# TYPE " PROM_NAMESPACE "_info gauge\n"
|
"# TYPE " PROM_NAMESPACE "_info gauge\n"
|
||||||
"# UNIT " PROM_NAMESPACE "_info \n"
|
"# UNIT " PROM_NAMESPACE "_info \n"
|
||||||
PROM_NAMESPACE "_info{version=\"%s\",board=\"%s\",sensor=\"%s\"} 1\n"
|
PROM_NAMESPACE "_info{version=\"%s\",board=\"%s\"} 1\n"
|
||||||
|
"# HELP " PROM_NAMESPACE "_info Metadata about a sensor.\n"
|
||||||
|
"# TYPE " PROM_NAMESPACE "_info gauge\n"
|
||||||
|
"# UNIT " PROM_NAMESPACE "_info \n"
|
||||||
|
PROM_NAMESPACE "_sensor{sensor=\"%s\",serial=\"%s\"} 1\n"
|
||||||
|
PROM_NAMESPACE "_sensor{sensor=\"%s\",serial=\"%s\"} 1\n"
|
||||||
"# HELP " PROM_NAMESPACE "_air_humidity_percent Air humidity.\n"
|
"# HELP " PROM_NAMESPACE "_air_humidity_percent Air humidity.\n"
|
||||||
"# TYPE " PROM_NAMESPACE "_air_humidity_percent gauge\n"
|
"# TYPE " PROM_NAMESPACE "_air_humidity_percent gauge\n"
|
||||||
"# UNIT " PROM_NAMESPACE "_air_humidity_percent %%\n"
|
"# UNIT " PROM_NAMESPACE "_air_humidity_percent %%\n"
|
||||||
|
@ -156,16 +202,32 @@ void handle_http_metrics() {
|
||||||
"# HELP " PROM_NAMESPACE "_air_heat_index_celsius Apparent air temperature, based on temperature and humidity.\n"
|
"# HELP " PROM_NAMESPACE "_air_heat_index_celsius Apparent air temperature, based on temperature and humidity.\n"
|
||||||
"# TYPE " PROM_NAMESPACE "_air_heat_index_celsius gauge\n"
|
"# TYPE " PROM_NAMESPACE "_air_heat_index_celsius gauge\n"
|
||||||
"# UNIT " PROM_NAMESPACE "_air_heat_index_celsius \u00B0C\n"
|
"# UNIT " PROM_NAMESPACE "_air_heat_index_celsius \u00B0C\n"
|
||||||
PROM_NAMESPACE "_air_heat_index_celsius %f\n";
|
PROM_NAMESPACE "_air_heat_index_celsius %f\n"
|
||||||
|
"# HELP " PROM_NAMESPACE "_air_quality_eco2 Equivalent calculated carbon-dioxide (eCO2).\n"
|
||||||
|
"# TYPE " PROM_NAMESPACE "_air_quality_eco2 gauge\n"
|
||||||
|
"# UNIT " PROM_NAMESPACE "_air_quality_eco2 ppm\n"
|
||||||
|
PROM_NAMESPACE "_air_quality_eco2 %d\n"
|
||||||
|
"# HELP " PROM_NAMESPACE "_air_quality_tvoc Total Volatile Organic Compound (TVOC).\n"
|
||||||
|
"# TYPE " PROM_NAMESPACE "_air_quality_tvoc gauge\n"
|
||||||
|
"# UNIT " PROM_NAMESPACE "_air_quality_tvoc ppb\\t\n"
|
||||||
|
PROM_NAMESPACE "_air_quality_tvoc %d\n"
|
||||||
|
"# HELP " PROM_NAMESPACE "_air_quality_h2 Hydrogen.\n"
|
||||||
|
"# TYPE " PROM_NAMESPACE "_air_quality_h2 gauge\n"
|
||||||
|
"# UNIT " PROM_NAMESPACE "_air_quality_h2 ppm\n"
|
||||||
|
PROM_NAMESPACE "_air_quality_h2 %d\n"
|
||||||
|
"# HELP " PROM_NAMESPACE "_air_quality_ethanol Ethanol.\n"
|
||||||
|
"# TYPE " PROM_NAMESPACE "_air_quality_ethanol gauge\n"
|
||||||
|
"# UNIT " PROM_NAMESPACE "_air_quality_ethanol ppm\n"
|
||||||
|
PROM_NAMESPACE "_air_quality_ethanol %d\n";
|
||||||
|
|
||||||
read_sensors();
|
read_sensors();
|
||||||
if (isnan(humidity) || isnan(temperature) || isnan(heat_index)) {
|
if (isnan(humidity) || isnan(temperature) || isnan(heat_index) || isnan(tvoc) || isnan(co2) || isnan(h2) || isnan(ethanol)) {
|
||||||
http_server.send(500, "text/plain; charset=utf-8", "Sensor error.");
|
http_server.send(500, "text/plain; charset=utf-8", "Sensor error.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char response[BUFSIZE];
|
char response[BUFSIZE];
|
||||||
snprintf(response, BUFSIZE, response_template, VERSION, BOARD_NAME, DHT_NAME, humidity, temperature, heat_index);
|
snprintf(response, BUFSIZE, response_template, VERSION, BOARD_NAME, DHT_NAME, "n/a", "SGP30", sgp30_serial, humidity, temperature, heat_index, tvoc, co2, h2, ethanol);
|
||||||
http_server.send(200, "text/plain; charset=utf-8", response);
|
http_server.send(200, "text/plain; charset=utf-8", response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,8 +237,9 @@ void handle_http_not_found() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_sensors(boolean force) {
|
void read_sensors(boolean force) {
|
||||||
uint32_t min_interval = max(dht_sensor.getMinimumSamplingPeriod(), READ_INTERVAL);
|
uint32_t min_interval = max(READ_INTERVAL, 1000);
|
||||||
uint32_t current_time = millis();
|
uint32_t current_time = millis();
|
||||||
|
|
||||||
if (!force && current_time - previous_read_time < min_interval) {
|
if (!force && current_time - previous_read_time < min_interval) {
|
||||||
log("Sensors were recently read, will not read again yet.", LogLevel::DEBUG);
|
log("Sensors were recently read, will not read again yet.", LogLevel::DEBUG);
|
||||||
return;
|
return;
|
||||||
|
@ -186,14 +249,17 @@ void read_sensors(boolean force) {
|
||||||
read_humidity_sensor();
|
read_humidity_sensor();
|
||||||
read_temperature_sensor();
|
read_temperature_sensor();
|
||||||
read_heat_index();
|
read_heat_index();
|
||||||
|
read_sgp();
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_humidity_sensor() {
|
void read_humidity_sensor() {
|
||||||
log("Reading humidity sensor ...", LogLevel::DEBUG);
|
log("Reading humidity sensor ...", LogLevel::DEBUG);
|
||||||
bool result = read_sensor([] {
|
|
||||||
return dht_sensor.getHumidity();
|
read_sensor([] {
|
||||||
|
return dht_sensor.readHumidity();
|
||||||
}, &humidity);
|
}, &humidity);
|
||||||
if (result) {
|
|
||||||
|
if (humidity) {
|
||||||
humidity += HUMIDITY_CORRECTION_OFFSET;
|
humidity += HUMIDITY_CORRECTION_OFFSET;
|
||||||
} else {
|
} else {
|
||||||
log("Failed to read humidity sensor.", LogLevel::ERROR);
|
log("Failed to read humidity sensor.", LogLevel::ERROR);
|
||||||
|
@ -202,10 +268,12 @@ void read_humidity_sensor() {
|
||||||
|
|
||||||
void read_temperature_sensor() {
|
void read_temperature_sensor() {
|
||||||
log("Reading temperature sensor ...", LogLevel::DEBUG);
|
log("Reading temperature sensor ...", LogLevel::DEBUG);
|
||||||
bool result = read_sensor([] {
|
|
||||||
return dht_sensor.getTemperature();
|
read_sensor([] {
|
||||||
|
return dht_sensor.readTemperature();
|
||||||
}, &temperature);
|
}, &temperature);
|
||||||
if (result) {
|
|
||||||
|
if (temperature) {
|
||||||
temperature += TEMPERATURE_CORRECTION_OFFSET;
|
temperature += TEMPERATURE_CORRECTION_OFFSET;
|
||||||
} else {
|
} else {
|
||||||
log("Failed to read temperature sensor.", LogLevel::ERROR);
|
log("Failed to read temperature sensor.", LogLevel::ERROR);
|
||||||
|
@ -214,12 +282,50 @@ void read_temperature_sensor() {
|
||||||
|
|
||||||
void read_heat_index() {
|
void read_heat_index() {
|
||||||
if (!isnan(humidity) && !isnan(temperature)) {
|
if (!isnan(humidity) && !isnan(temperature)) {
|
||||||
heat_index = dht_sensor.computeHeatIndex(temperature, humidity, false);
|
heat_index = dht_sensor.computeHeatIndex(temperature, humidity, false); // compute in celsius
|
||||||
} else {
|
} else {
|
||||||
heat_index = NAN;
|
heat_index = NAN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getAbsoluteHumidity(float t, float h) {
|
||||||
|
float absoluteHumidity = (2.167 * 6.112) * h ;
|
||||||
|
absoluteHumidity *= exp((17.62 * t)/(243.12 + t));
|
||||||
|
absoluteHumidity /= (273.15 + t);
|
||||||
|
|
||||||
|
return absoluteHumidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_sgp() {
|
||||||
|
log("Reading air quality (SGP30) sensor ...", LogLevel::DEBUG);
|
||||||
|
|
||||||
|
sgp_sensor.setHumidity(getAbsoluteHumidity(temperature, humidity));
|
||||||
|
|
||||||
|
if (sgp_sensor.IAQmeasure() && sgp_sensor.IAQmeasureRaw()) {
|
||||||
|
tvoc = sgp_sensor.TVOC;
|
||||||
|
co2 = sgp_sensor.eCO2;
|
||||||
|
h2 = sgp_sensor.rawH2;
|
||||||
|
ethanol = sgp_sensor.rawEthanol;
|
||||||
|
|
||||||
|
sgp30_counter++;
|
||||||
|
if (sgp30_counter == 30) {
|
||||||
|
sgp30_counter = 0;
|
||||||
|
|
||||||
|
uint16_t tvoc_base, eco2_base;
|
||||||
|
if (!sgp_sensor.getIAQBaseline(&eco2_base, &tvoc_base)) {
|
||||||
|
log("Failed to get baseline readings", LogLevel::ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char message[128];
|
||||||
|
snprintf(message, 128, "Baseline values: eCO2: 0x%s & TVOC: 0x%s", String(eco2_base, HEX).c_str(), String(tvoc_base, HEX).c_str());
|
||||||
|
log(message, LogLevel::DEBUG);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log("Failed to read air quality sensor.", LogLevel::ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool read_sensor(float (*function)(), float *value) {
|
bool read_sensor(float (*function)(), float *value) {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
for (int i = 0; i < READ_TRY_COUNT; i++) {
|
for (int i = 0; i < READ_TRY_COUNT; i++) {
|
Loading…
Reference in New Issue