Compare commits

...

2 Commits

  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. 2
      README.md
  10. 10
      WebApp/src/monitor-api.ts

5
ESP32/.cproject

@ -15,7 +15,7 @@
</extensions> </extensions>
</storageModule> </storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0"> <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=""> <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"> <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=""/> <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/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/ESP32/lib/AsyncTCP/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;${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>
<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"> <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"/> <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 #pragma once
#include <cstdint> #include <stdint.h>
struct Status struct Status
{ {

88
ESP32/src/WebServer.cpp

@ -2,16 +2,23 @@
#include "DebugLog.h" #include "DebugLog.h"
#include "DataLogger.h" #include "DataLogger.h"
#include "Info.h"
#include "utils.h"
#include <Arduino.h> #include <Arduino.h>
#include <SPIFFS.h> #include <SPIFFS.h>
#include <string> #include <string>
#include <cstring>
#include "HTTPServer.hpp" #include "HTTPSServer.hpp"
#include "SSLCert.hpp" #include "SSLCert.hpp"
#include "HTTPRequest.hpp" #include "HTTPRequest.hpp"
#include "HTTPResponse.hpp" #include "HTTPResponse.hpp"
// generated certificate data
#include "cert.h"
#include "private_key.h"
/** Check if we have multiple cores */ /** Check if we have multiple cores */
#if CONFIG_FREERTOS_UNICORE #if CONFIG_FREERTOS_UNICORE
#define WEBSERVER_RUNNING_CORE 0 #define WEBSERVER_RUNNING_CORE 0
@ -25,7 +32,13 @@ detail::WebServer WebServer;
namespace detail 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() WebServer::WebServer()
{ {
@ -121,7 +134,76 @@ namespace detail
void WebServer::HandlePostInfo_(httpsserver::HTTPRequest * request, httpsserver::HTTPResponse * response) 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(); 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) void WebServer::HandleDefault_(HTTPRequest * request, HTTPResponse * response)
@ -165,7 +247,7 @@ namespace detail
void WebServer::SendContent_(Stream& stream, httpsserver::HTTPResponse * response) void WebServer::SendContent_(Stream& stream, httpsserver::HTTPResponse * response)
{ {
uint8_t buffer[512]; uint8_t buffer[128];
while(stream.available()) while(stream.available())
{ {
size_t numBytes = stream.readBytes(buffer, sizeof(buffer)); size_t numBytes = stream.readBytes(buffer, sizeof(buffer));

7
ESP32/src/WebServer.h

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

29
ESP32/src/utils.cpp

@ -33,4 +33,33 @@ namespace utils
return (biggestValue - from) + to + 1; 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 namespace utils
{ {
unsigned long elapsed(unsigned long from, unsigned long to); 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 "DataLogger.h"
#include "WebServer.h" #include "WebServer.h"
#include "Status.h" #include "Status.h"
#include "Info.h"
#include "utils.h" #include "utils.h"
#include <WiFi.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() void setup()
{ {
pinMode(debugLedPin, OUTPUT); pinMode(debugLedPin, OUTPUT);
@ -198,6 +207,7 @@ void setup()
//currentSensor2.begin(0x48, &Wire); //currentSensor2.begin(0x48, &Wire);
WebServer.setInfoReceivedCallback(onInfoReceived);
WebServer.begin(); WebServer.begin();
/*server.on("/api/debug/log", HTTP_GET, [](AsyncWebServerRequest *request){ /*server.on("/api/debug/log", HTTP_GET, [](AsyncWebServerRequest *request){

2
README.md

@ -46,4 +46,4 @@ You will also need to upload some files to the ESP32 file system (SPIFFS). These
# Using Eclipse as IDE # Using Eclipse as IDE
The repository contains a `.metadata` folder, `.project` files, and a `.cproject` file for the CDT plugin. It's not perfect but brings some autocompletion and error linting. On Windows, you will need to add a `HOME` environment variable pointing to `C:\Users\your_user_name` for include files to be correctly found by CDT. The repository contains `.project` files, and a `.cproject` file. To use these, install [Eclipse](https://www.eclipse.org/downloads/) and the CDT plugin, then create a new workspace in the root folder of the project, and finally import projects from the `ESP32` and `WebApp` folders into your workspace. This setup is not perfect, can probably be improved, but should bring some autocompletion and error linting. Eclipse should prompt you to install more plugins when opening some files, depending on the file extension. On Windows, you will also need to add a `HOME` environment variable pointing to `C:\Users\your_user_name` for include files to be correctly found by CDT.

10
WebApp/src/monitor-api.ts

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

Loading…
Cancel
Save