259 lines
7.0 KiB
C++
259 lines
7.0 KiB
C++
/***************************************************
|
||
|
||
Wemos D1 mini or NodeMCU 1.0
|
||
VCC - 3.3V
|
||
GND - G
|
||
SCL - D1 -- GPIO 5
|
||
SDA - D2 -- GPIO 4
|
||
WAK - D3 -- GPIO 0
|
||
|
||
ESP32
|
||
VCC - 3.3V
|
||
GND - G
|
||
SCL - 19
|
||
SDA - 18
|
||
WAK - 23
|
||
|
||
****************************************************/
|
||
|
||
#include "ESPAsyncWebServer.h"
|
||
#include <WiFi.h>
|
||
#include <Wire.h>
|
||
#include "ClosedCube_HDC1080.h" // HDC1080 library - https://github.com/closedcube/ClosedCube_HDC1080_Arduino // 14.04.2019
|
||
#include "ccs811.h" // CCS811 library - https://github.com/maarten-pennings/CCS811 // 13.03.2020
|
||
#include <time.h>
|
||
//#include "src/ESPinfluxdb.h" // https://github.com/hwwong/ESP_influxdb // 14.04.2019
|
||
#include "http_static.h" // HTTP pages and JSON request templates
|
||
// ********************** Config **********************
|
||
|
||
// DeepSleep time – send data every 60 seconds
|
||
const int sleepTimeS = 60;
|
||
|
||
//Global sensor objects
|
||
#define CCS811_WAK 23
|
||
|
||
CCS811 ccs811(CCS811_WAK);
|
||
ClosedCube_HDC1080 hdc1080;
|
||
|
||
// WiFi Config
|
||
#define WiFi_SSID "Ischtar"
|
||
#define WiFi_Password "highfive"
|
||
|
||
// NTP conf
|
||
const char* ntpServer = "pool.ntp.org";
|
||
const long gmtOffset_sec = 0;
|
||
const int daylightOffset_sec = 3600;
|
||
|
||
// Create AsyncWebServer object on port 80
|
||
AsyncWebServer server(80);
|
||
|
||
// Globals for CCS811
|
||
uint16_t eco2, etvoc, errstat, raw;
|
||
// Globals for timestamp
|
||
struct tm timeinfo;
|
||
|
||
// loop cycle
|
||
#define workCycle 60 //seconds
|
||
// ******************** Config End ********************
|
||
|
||
String readHDC1080Temperature() {
|
||
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
|
||
float t = hdc1080.readTemperature();
|
||
if (isnan(t)) {
|
||
Serial.println("Failed to read from HDC1080 sensor!");
|
||
return "--";
|
||
}
|
||
else {
|
||
return String(t);
|
||
}
|
||
}
|
||
|
||
String readHDC1080Humidity() {
|
||
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
|
||
float h = hdc1080.readHumidity();
|
||
if (isnan(h)) {
|
||
Serial.println("Failed to read from HDC1080 sensor!");
|
||
return "--";
|
||
}
|
||
else {
|
||
return String(h);
|
||
}
|
||
}
|
||
|
||
String processCCS811Error(err_t errstat) {
|
||
if ( errstat == CCS811_ERRSTAT_OK_NODATA ) {
|
||
Serial.println("CCS811: waiting for (new) data");
|
||
return "loading";
|
||
} else if ( errstat & CCS811_ERRSTAT_I2CFAIL ) {
|
||
Serial.println("CCS811: I2C error");
|
||
return "i2c error";
|
||
} else {
|
||
Serial.print("CCS811: errstat="); Serial.print(errstat, HEX);
|
||
Serial.print("="); Serial.println( ccs811.errstat_str(errstat) );
|
||
return "<span color='red' title='" + String(ccs811.errstat_str(errstat)) + "'>CCS811 sensor error</span>";
|
||
}
|
||
}
|
||
|
||
|
||
|
||
String readCCS811TVOC() {
|
||
return String(etvoc);
|
||
}
|
||
String readCCS811ECO2() {
|
||
return String(eco2);
|
||
}
|
||
|
||
String formatISO8601() {
|
||
char timestamp[20]; // Buffer for timestamp
|
||
|
||
// Format the time into ISO 8601 format
|
||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%SZ", &timeinfo);
|
||
|
||
// Convert the formatted timestamp to a String object
|
||
return String(timestamp);
|
||
}
|
||
|
||
|
||
// Replaces placeholder in HTML template with real values
|
||
// SSR if you will
|
||
String processor(const String& var){
|
||
if(var == "TEMPERATURE"){
|
||
return readHDC1080Temperature();
|
||
}
|
||
else if(var == "HUMIDITY"){
|
||
return readHDC1080Humidity();
|
||
}
|
||
else if(var == "TVOC"){
|
||
return readCCS811TVOC();
|
||
}
|
||
else if(var == "ECO2"){
|
||
return readCCS811ECO2();
|
||
} else if(var == "TIMESTAMP"){
|
||
return formatISO8601();
|
||
}
|
||
return String();
|
||
}
|
||
|
||
void connectToWiFi() {
|
||
WiFi.mode(WIFI_STA);
|
||
WiFi.begin(WiFi_SSID, WiFi_Password);
|
||
|
||
Serial.println();
|
||
Serial.print("Connecting to WiFi: ");
|
||
Serial.print(WiFi_SSID);
|
||
|
||
int attempts = 0;
|
||
while (WiFi.status() != WL_CONNECTED && attempts < 10) {
|
||
delay(500);
|
||
Serial.print(".");
|
||
attempts++;
|
||
}
|
||
|
||
if (WiFi.status() == WL_CONNECTED) {
|
||
Serial.println("\nWiFi connected");
|
||
Serial.print("IP address: http://");
|
||
Serial.println(WiFi.localIP());
|
||
} else {
|
||
Serial.println("\nFailed to connect to WiFi");
|
||
// Handle connection failure, e.g., retry or reset the ESP32
|
||
}
|
||
}
|
||
|
||
void setup()
|
||
{
|
||
Serial.begin(115200);
|
||
delay(10);
|
||
Serial.println("");
|
||
|
||
connectToWiFi();
|
||
delay(10);
|
||
Serial.println("");
|
||
// Config NTP
|
||
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||
|
||
// hdc1080 info
|
||
hdc1080.begin(0x40);
|
||
Serial.print("Manufacturer ID=0x");
|
||
Serial.println(hdc1080.readManufacturerId(), HEX); // 0x5449 ID of Texas Instruments
|
||
Serial.print("Device ID=0x");
|
||
Serial.println(hdc1080.readDeviceId(), HEX); // 0x1050 ID of the device
|
||
|
||
// i2c
|
||
Wire.begin();
|
||
|
||
Serial.println("CCS811 test");
|
||
// Enable CCS811
|
||
bool ok = ccs811.begin();
|
||
if ( !ok ) Serial.println("setup: CCS811 begin FAILED");
|
||
|
||
// Print CCS811 versions
|
||
Serial.print("setup: hardware version: "); Serial.println(ccs811.hardware_version(), HEX);
|
||
Serial.print("setup: bootloader version: "); Serial.println(ccs811.bootloader_version(), HEX);
|
||
Serial.print("setup: application version: "); Serial.println(ccs811.application_version(), HEX);
|
||
|
||
// Start measuring
|
||
ok = ccs811.start(CCS811_MODE_1SEC);
|
||
if ( !ok ) Serial.println("init: CCS811 start FAILED");
|
||
|
||
// Pages and JSONs
|
||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
||
request->send_P(200, "text/html", http_static::index_html, processor);
|
||
});
|
||
server.on("/api/sensors.json", HTTP_GET, [](AsyncWebServerRequest *request){
|
||
request->send_P(200, "text/html", http_static::sensor_things_resp, processor);
|
||
});
|
||
|
||
// lightweight named endpoints
|
||
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
|
||
request->send_P(200, "text/plain", readHDC1080Temperature().c_str());
|
||
});
|
||
server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
|
||
request->send_P(200, "text/plain", readHDC1080Humidity().c_str());
|
||
});
|
||
server.on("/tvoc", HTTP_GET, [](AsyncWebServerRequest *request){
|
||
request->send_P(200, "text/plain", readCCS811TVOC().c_str());
|
||
});
|
||
server.on("/eco2", HTTP_GET, [](AsyncWebServerRequest *request){
|
||
request->send_P(200, "text/plain", readCCS811ECO2().c_str());
|
||
});
|
||
|
||
// Start server
|
||
server.begin();
|
||
}
|
||
|
||
void loop()
|
||
{
|
||
if (WiFi.status() != WL_CONNECTED) {
|
||
Serial.println("WiFi connection lost. Reconnecting...");
|
||
connectToWiFi();
|
||
}
|
||
|
||
if(!getLocalTime(&timeinfo)){
|
||
Serial.println("Failed to obtain time");
|
||
return;
|
||
}
|
||
Serial.print(&timeinfo, "[%M-%d-%Y--%H:%M:%S]: ");
|
||
|
||
Serial.print("H="); Serial.print(readHDC1080Temperature()); Serial.print(" °C ");
|
||
Serial.print("T="); Serial.print(readHDC1080Temperature()); Serial.print(" % ");
|
||
// Read CCS811
|
||
ccs811.read(&eco2,&etvoc,&errstat,&raw);
|
||
|
||
// Process CCS811
|
||
if( errstat==CCS811_ERRSTAT_OK ) {
|
||
Serial.print("eco2="); Serial.print(eco2); Serial.print(" ppm ");
|
||
Serial.print("etvoc="); Serial.print(etvoc); Serial.print(" ppb ");
|
||
} else if( errstat==CCS811_ERRSTAT_OK_NODATA ) {
|
||
Serial.print("waiting for (new) data");
|
||
} else if( errstat & CCS811_ERRSTAT_I2CFAIL ) {
|
||
Serial.print("I2C error");
|
||
} else {
|
||
Serial.print( "error: " );
|
||
Serial.print( ccs811.errstat_str(errstat) );
|
||
}
|
||
Serial.println();
|
||
|
||
// Wait
|
||
delay(workCycle*1000);
|
||
}
|