Add webserver including calibration API endpoint

This commit is contained in:
Thomas Kolb 2024-08-29 22:28:40 +02:00
parent b417554444
commit 1c30b20d2a
4 changed files with 212 additions and 12 deletions

38
include/WebServer.h Normal file
View file

@ -0,0 +1,38 @@
#pragma once
#include <Adafruit_SCD30.h>
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>
#include <HTTPServer.hpp>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
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);
};

View file

@ -22,6 +22,7 @@ lib_deps =
SPI
Wire
NTPClient
esp32_https_server @ 1.0.0
#esp32_https_server

155
src/WebServer.cpp Normal file
View file

@ -0,0 +1,155 @@
/* HTTP Server setup and handler functions */
#include <LittleFS.h>
#include <mbedtls/sha256.h>
#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<char*>(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;
}

View file

@ -13,6 +13,7 @@
#include <Fonts/FreeSans12pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include "WebServer.h"
#include "Config.h"
#include <epaper.h>
@ -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);
}