From 1c30b20d2a468bc49804264f13edfe81855dbe35 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Thu, 29 Aug 2024 22:28:40 +0200 Subject: [PATCH] Add webserver including calibration API endpoint --- include/WebServer.h | 38 +++++++++++ platformio.ini | 1 + src/WebServer.cpp | 155 ++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 30 +++++---- 4 files changed, 212 insertions(+), 12 deletions(-) create mode 100644 include/WebServer.h create mode 100644 src/WebServer.cpp diff --git a/include/WebServer.h b/include/WebServer.h new file mode 100644 index 0000000..be68cd4 --- /dev/null +++ b/include/WebServer.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include + +class WebServer +{ + public: + bool start(Adafruit_SCD30 *scd30); + + static WebServer &instance(void) + { + static WebServer theInstance; + return theInstance; + } + + private: + httpsserver::HTTPServer *m_server; + + Adafruit_SCD30 *m_scd30; + + WebServer(void); + + static void serverTask(void *arg); + + static bool serveFile(String filename, httpsserver::HTTPResponse *res); + + // handlers + static void handleRoot(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res); + static void handleCalibrate(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res); + static void handleStatic(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res); +}; diff --git a/platformio.ini b/platformio.ini index 8d53347..dbf71cb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -22,6 +22,7 @@ lib_deps = SPI Wire NTPClient + esp32_https_server @ 1.0.0 #esp32_https_server diff --git a/src/WebServer.cpp b/src/WebServer.cpp new file mode 100644 index 0000000..5ffe3c4 --- /dev/null +++ b/src/WebServer.cpp @@ -0,0 +1,155 @@ +/* HTTP Server setup and handler functions */ + +#include +#include + +#include "WebServer.h" + +#include "Config.h" + +WebServer::WebServer(void) +{ + m_server = new httpsserver::HTTPServer(); +} + +bool WebServer::serveFile(String filename, httpsserver::HTTPResponse *res) +{ + uint8_t buf[1024]; + + bool exists = LittleFS.exists(filename); + + if(!exists) { + return false; + } + + File f = LittleFS.open(filename.c_str(), "r"); + + if(!f) { + return false; + } + + res->setHeader("Content-Length", httpsserver::intToString(f.size())); + + size_t nread = 1; + while(nread > 0) { + nread = f.readBytes(reinterpret_cast(buf), 1024); + + if(nread <= 0) { + break; + } + + res->write(buf, nread); + } + + f.close(); + + return true; +} + +void WebServer::handleRoot(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res) +{ + serveFile("/htdocs/index.html", res); + res->setHeader("Content-Type", "text/html"); +} + +static void error400(httpsserver::HTTPResponse *res, const std::string &reason) +{ + res->println("Error 400: Bad Request"); + res->println(reason.c_str()); + + res->setStatusCode(400); +} + +void WebServer::handleCalibrate(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res) +{ + res->setHeader("Content-Type", "text/plain"); + + httpsserver::ResourceParameters * params = req->getParams(); + + std::string ppmstr; + if(!params->getQueryParameter("ppm", ppmstr)) { + error400(res, "Parameter 'ppm' not set."); + return; + } + + int ppmvalue = String(ppmstr.c_str()).toInt(); + if(ppmvalue < 0 || ppmvalue > 65535) { + error400(res, "Parameter 'ppm' out of range [0 .. 65535]."); + return; + } + + res->setStatusCode(200); + res->print("Recalibrating SCD30 to "); + res->print(ppmvalue); + res->println(" ppm"); + + WebServer::instance().m_scd30->forceRecalibrationWithReference((uint16_t)ppmvalue); +} + +void WebServer::handleStatic(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res) +{ + std::string filename = "/htdocs" + req->getRequestString(); + + if(*(filename.end()-1) == '/') { + // looks like a directory name, so append 'index.html' + filename.append("index.html"); + } + + if(!serveFile(filename.c_str(), res)) { + req->discardRequestBody(); + + res->setStatusCode(404); + res->setHeader("Content-Type", "text/plain"); + + res->println("Error 404: Not found"); + } +} + +void WebServer::serverTask(void *arg) +{ + httpsserver::ResourceNode *nodeRoot = + new httpsserver::ResourceNode("/", "GET", WebServer::handleRoot); + httpsserver::ResourceNode *nodeAPICalibrate = + new httpsserver::ResourceNode("/api/calibrate", "GET", WebServer::handleCalibrate); + + // handle all remaining requests by trying to serve static files. If no file is found, 404 is generated. + httpsserver::ResourceNode *nodeStatic = + new httpsserver::ResourceNode("", "GET", WebServer::handleStatic); + + WebServer &server = WebServer::instance(); + + server.m_server->registerNode(nodeRoot); + server.m_server->registerNode(nodeAPICalibrate); + server.m_server->setDefaultNode(nodeStatic); + + Serial.println("[server] Starting HTTP Server..."); + server.m_server->start(); + if (server.m_server->isRunning()) + { + Serial.println("[server] Server ready."); + + while (true) + { + server.m_server->loop(); + delay(10); + } + } + + Serial.println("[server] Server died?! This should never happen."); +} + +bool WebServer::start(Adafruit_SCD30 *scd30) +{ + m_scd30 = scd30; + + xTaskCreatePinnedToCore( + WebServer::serverTask, /* Task function. */ + "HTTP Server Task", /* name of task. */ + 6144, /* Stack size of task */ + NULL, /* parameter of the task */ + 1, /* priority of the task */ + NULL, /* Task handle to keep track of created task */ + 1); + + return true; +} diff --git a/src/main.cpp b/src/main.cpp index 2788b50..5681dfb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,6 +13,7 @@ #include #include +#include "WebServer.h" #include "Config.h" #include @@ -215,6 +216,7 @@ void setup(void) epaper_draw_and_hibernate(draw_epaper_initial_callback, false); + WebServer::instance().start(&scd30); } @@ -341,6 +343,7 @@ void loop(void) { static bool first_scd30_readout = true; static uint32_t lastEPaperRefresh = 0; + static uint32_t lastUpload = 0; uint32_t now = millis(); wiFiMulti.run(); // maintain the WiFi connection @@ -372,18 +375,6 @@ void loop(void) Serial.println(" %"); Serial.println(""); - Serial.println("\nUploading..."); - upload_data(); - - if((now - lastEPaperRefresh) >= 60000) { - Serial.println("Rendering display..."); - lastEPaperRefresh = now; - epaper_draw_and_hibernate(draw_epaper_callback, false); - Serial.println("Display updated."); - - timeseries_prune(&ts_scd30_humidity, 3600); // keep the last hour - } - /* if(!calibrationDone && now >= 300000) { uint16_t calValue = scd30.getForcedCalibrationReference(); @@ -400,5 +391,20 @@ void loop(void) */ } + if(!first_scd30_readout && (now - lastUpload) >= 30000) { + lastUpload = now; + Serial.println("\nUploading..."); + upload_data(); + } + + if((now - lastEPaperRefresh) >= 60000) { + Serial.println("Rendering display..."); + lastEPaperRefresh = now; + epaper_draw_and_hibernate(draw_epaper_callback, false); + Serial.println("Display updated."); + + timeseries_prune(&ts_scd30_humidity, 3600); // keep the last hour + } + delay(1000); }