Browse Source

Using HTTPS protocol for ESP32 web server, and implemented POST /api/info handler

master
Youen 6 months ago
parent
commit
6c40ba4e1f
  1. 5
      ESP32/.cproject
  2. 9
      ESP32/src/Info.h
  3. 2
      ESP32/src/Status.h
  4. 88
      ESP32/src/WebServer.cpp
  5. 7
      ESP32/src/WebServer.h
  6. 29
      ESP32/src/utils.cpp
  7. 3
      ESP32/src/utils.h
  8. 10
      ESP32/src/vehicle-monitor.cpp
  9. 10
      WebApp/src/monitor-api.ts

5
ESP32/.cproject

@ -15,7 +15,7 @@
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="ESP32" buildProperties="" description="" id="0.910961921" name="Default" parent="org.eclipse.cdt.build.core.prefbase.cfg">
<configuration artifactName="ESP32" buildProperties="" description="" id="0.910961921" name="Default" optionalBuildProperties="" parent="org.eclipse.cdt.build.core.prefbase.cfg">
<folderInfo id="0.910961921." name="/" resourcePath="">
<toolChain id="org.eclipse.cdt.build.core.prefbase.toolchain.952979152" name="No ToolChain" resourceTypeBasedDiscovery="false" superClass="org.eclipse.cdt.build.core.prefbase.toolchain">
<targetPlatform binaryParser="org.eclipse.cdt.core.ELF" id="org.eclipse.cdt.build.core.prefbase.toolchain.952979152.52310970" name=""/>
@ -232,6 +232,9 @@
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/ESP32/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/ESP32/lib/AsyncTCP/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/ESP32/lib/DigitalPressureSensor/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${HOME}/.platformio/packages/toolchain-xtensa-esp32/xtensa-esp32-elf/include/c++/8.4.0&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/ESP32/.pio/libdeps/nodemcu-32s/esp32_https_server/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${HOME}/.platformio/packages/toolchain-xtensa-esp32/xtensa-esp32-elf/include&quot;"/>
</option>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="org.eclipse.cdt.build.core.settings.holder.symbols.1198905600" name="Symbols" superClass="org.eclipse.cdt.build.core.settings.holder.symbols" valueType="definedSymbols">
<listOptionValue builtIn="false" value="PLATFORMIO=50205"/>

9
ESP32/src/Info.h

@ -0,0 +1,9 @@
#pragma once
struct Info
{
char realtime[32] = {0}; // UTC date and time, in format YYYY-MM-DDTHH:mm:ss.sssZ
float latitude = -1000.0f; // in decimal degrees
float longitude = -1000.0f; // in decimal degrees
float gpsAltitude = -1000.0f; // in meters, above sea level
};

2
ESP32/src/Status.h

@ -1,6 +1,6 @@
#pragma once
#include <cstdint>
#include <stdint.h>
struct Status
{

88
ESP32/src/WebServer.cpp

@ -2,16 +2,23 @@
#include "DebugLog.h"
#include "DataLogger.h"
#include "Info.h"
#include "utils.h"
#include <Arduino.h>
#include <SPIFFS.h>
#include <string>
#include <cstring>
#include "HTTPServer.hpp"
#include "HTTPSServer.hpp"
#include "SSLCert.hpp"
#include "HTTPRequest.hpp"
#include "HTTPResponse.hpp"
// generated certificate data
#include "cert.h"
#include "private_key.h"
/** Check if we have multiple cores */
#if CONFIG_FREERTOS_UNICORE
#define WEBSERVER_RUNNING_CORE 0
@ -25,7 +32,13 @@ detail::WebServer WebServer;
namespace detail
{
HTTPServer httpServer;
// Create an SSL certificate object from the files included above
SSLCert cert = SSLCert(
crt_DER, crt_DER_len,
private_key_DER, private_key_DER_len
);
HTTPSServer httpServer = HTTPSServer(&cert);
WebServer::WebServer()
{
@ -121,7 +134,76 @@ namespace detail
void WebServer::HandlePostInfo_(httpsserver::HTTPRequest * request, httpsserver::HTTPResponse * response)
{
char body[128]; // in current implementation, we expect the whole body to fit here
size_t bodySize = request->readChars(body, sizeof(body) - 1);
body[bodySize] = 0;
request->discardRequestBody();
//::DebugLog.println(body.c_str());
Info receivedInfo;
char* nextParam = body;
while (true)
{
char* sepPos = strstr(nextParam, "&");
if(sepPos == nullptr) sepPos = &body[bodySize];
if(nextParam == sepPos)
break; // should happen only if body is an empty string or ends with an "&" character
*sepPos = 0; // split the string in-place, this overrides the separating character which is fine
//::DebugLog.println(nextParam);
char* eqPos = strstr(nextParam, "=");
if(eqPos != nullptr && eqPos != nextParam && *(eqPos+1) != 0)
{
*eqPos = 0; // split the string in-place, overriding the equal sign
char* paramName = nextParam;
char* paramValue = eqPos + 1;
utils::replaceString(paramValue, "%3A", ":");
//::DebugLog.print(paramName); ::DebugLog.print(" = "); ::DebugLog.println(paramValue);
if(strcmp(paramName, "lat") == 0)
{
char *ending = nullptr;
receivedInfo.latitude = strtof(paramValue, &ending);
if (*ending != 0)
receivedInfo.latitude = -1000.0f;
}
else if(strcmp(paramName, "lng") == 0)
{
char *ending = nullptr;
receivedInfo.longitude = strtof(paramValue, &ending);
if (*ending != 0)
receivedInfo.longitude = -1000.0f;
}
else if(strcmp(paramName, "alt") == 0)
{
char *ending = nullptr;
receivedInfo.gpsAltitude = strtof(paramValue, &ending);
if (*ending != 0)
receivedInfo.gpsAltitude = -1000.0f;
}
else if(strcmp(paramName, "time") == 0)
{
strncpy(receivedInfo.realtime, paramValue, sizeof(receivedInfo.realtime));
}
}
if(sepPos == &body[bodySize])
break;
nextParam = sepPos + 1;
}
if(::WebServer.infoReceived_ != nullptr)
{
::WebServer.infoReceived_(receivedInfo);
}
}
void WebServer::HandleDefault_(HTTPRequest * request, HTTPResponse * response)
@ -165,7 +247,7 @@ namespace detail
void WebServer::SendContent_(Stream& stream, httpsserver::HTTPResponse * response)
{
uint8_t buffer[512];
uint8_t buffer[128];
while(stream.available())
{
size_t numBytes = stream.readBytes(buffer, sizeof(buffer));

7
ESP32/src/WebServer.h

@ -3,6 +3,7 @@
#include "Status.h"
class Stream;
struct Info;
namespace httpsserver
{
@ -14,6 +15,9 @@ namespace detail
{
class WebServer
{
public:
typedef void (*InfoReceived)(const Info& info);
public:
WebServer();
~WebServer();
@ -22,6 +26,8 @@ namespace detail
void setStatus(const Status& status);
void setInfoReceivedCallback(InfoReceived infoReceived) { infoReceived_ = infoReceived; }
private:
static void ServerTask_(void* params);
static void HandleIndex_(httpsserver::HTTPRequest * request, httpsserver::HTTPResponse * response);
@ -33,6 +39,7 @@ namespace detail
private:
Status status_;
InfoReceived infoReceived_ = nullptr;
};
}

29
ESP32/src/utils.cpp

@ -33,4 +33,33 @@ namespace utils
return (biggestValue - from) + to + 1;
}
}
void replaceString(char* str, char* searchStr, char* replacementStr)
{
size_t searchStrLen = strlen(searchStr);
size_t replacementStrLen = strlen(replacementStr);
char* newPos = str;
char* nextSearchPos = str;
while(true)
{
char* p = strstr(nextSearchPos, searchStr);
if(p == nullptr) p = nextSearchPos + strlen(nextSearchPos);
size_t l = p - nextSearchPos;
if(newPos != nextSearchPos)
{
memcpy(newPos, nextSearchPos, l);
}
newPos += l;
if(*p == 0)
break;
memcpy(newPos, replacementStr, replacementStrLen);
newPos += replacementStrLen;
nextSearchPos = p + searchStrLen;
}
}
}

3
ESP32/src/utils.h

@ -1,4 +1,7 @@
namespace utils
{
unsigned long elapsed(unsigned long from, unsigned long to);
//! Replace all occurences of searchStr by remplacementStr, in-place within input str. This function can only be used if replacementStr is shorter or the same size as searchStr.
void replaceString(char* str, char* searchStr, char* replacementStr);
}

10
ESP32/src/vehicle-monitor.cpp

@ -6,6 +6,7 @@
#include "DataLogger.h"
#include "WebServer.h"
#include "Status.h"
#include "Info.h"
#include "utils.h"
#include <WiFi.h>
@ -151,6 +152,14 @@ void connectWifi()
}
}
void onInfoReceived(const Info& info)
{
if(info.latitude > -900.0f) status.latitude = info.latitude;
if(info.longitude > -900.0f) status.longitude = info.longitude;
if(info.gpsAltitude > -900.0f) status.gpsAltitude = info.gpsAltitude;
if(info.realtime[0] != 0) strncpy(status.realtime, info.realtime, sizeof(status.realtime));
}
void setup()
{
pinMode(debugLedPin, OUTPUT);
@ -198,6 +207,7 @@ void setup()
//currentSensor2.begin(0x48, &Wire);
WebServer.setInfoReceivedCallback(onInfoReceived);
WebServer.begin();
/*server.on("/api/debug/log", HTTP_GET, [](AsyncWebServerRequest *request){

10
WebApp/src/monitor-api.ts

@ -50,6 +50,7 @@ export class MonitorApi {
private mockServer: boolean;
private lastStatus: Status = null;
private lastKnownPosition: GeolocationPosition = null;
private watchingPosition: boolean = false;
private static api: MonitorApi = null;
private lastFetchTime = 0;
@ -74,18 +75,19 @@ export class MonitorApi {
getStatus() { return this.lastStatus; }
async autoUpdateInfo(): Promise<void> {
if (navigator.geolocation) {
if (!this.watchingPosition && navigator.geolocation) {
let updatePosition = (position: GeolocationPosition) => {
this.lastKnownPosition = position;
};
const options = {
enableHighAccuracy: true,
timeout: 6000,
maximumAge: 3000,
timeout: 10000,
maximumAge: 5000,
};
navigator.geolocation.getCurrentPosition(updatePosition, () => updatePosition(null), options);
navigator.geolocation.watchPosition(updatePosition, () => updatePosition(null), options);
this.watchingPosition = true;
}
const defaultPosition : GeolocationPosition = {

Loading…
Cancel
Save