Compare commits
2 Commits
2ac358ba24
...
f648eea8d9
Author | SHA1 | Date | |
---|---|---|---|
f648eea8d9 | |||
359f3b4cd4 |
@ -16,7 +16,7 @@ DataLogger::~DataLogger()
|
||||
{
|
||||
}
|
||||
|
||||
void DataLogger::open()
|
||||
void DataLogger::open(const char* currentDateTime)
|
||||
{
|
||||
if(isOpen()) return;
|
||||
|
||||
@ -51,7 +51,10 @@ void DataLogger::open()
|
||||
file = SPIFFS.open(fileName, FILE_WRITE);
|
||||
if(!isOpen()) Serial.println("DataLogger: failed to open file");
|
||||
|
||||
if(!file.print("time,speed,battery voltage,battery output current,temperature,altitude\n")) Serial.println("DataLogger: failed to write to file");
|
||||
char metadata[64];
|
||||
sprintf(metadata, "start time: %s\n", currentDateTime == nullptr || currentDateTime[0] == 0 ? "NA" : currentDateTime);
|
||||
if(!file.print(metadata)) Serial.println("DataLogger: failed to write to file");
|
||||
if(!file.print("time,speed,battery voltage,battery output current,temperature,altitude,latitude,longitude\n")) Serial.println("DataLogger: failed to write to file");
|
||||
}
|
||||
|
||||
void DataLogger::close()
|
||||
@ -69,7 +72,16 @@ void DataLogger::log(unsigned long timeMilliseconds, const Entry& entry)
|
||||
if((lastEntry.isDifferent(entry) || currentTime >= lastLogTime + 20.0f) && currentTime >= lastLogTime + 0.2f)
|
||||
{
|
||||
char line[128];
|
||||
sprintf(line, "%.3f,%.3f,%.3f,%.3f,%.1f,%.1f\n", currentTime, entry.speed, entry.batteryVoltage, entry.batteryOutputCurrent, entry.temperature, entry.altitude);
|
||||
char coords[32];
|
||||
if(entry.latitude < -900.0f || !gpsCoordsEnabled)
|
||||
{
|
||||
sprintf(coords, "NA,NA");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(coords, "%.5f,%.5f", entry.latitude, entry.longitude);
|
||||
}
|
||||
sprintf(line, "%.3f,%.3f,%.3f,%.3f,%.1f,%.1f,%s\n", currentTime, entry.speed, entry.batteryVoltage, entry.batteryOutputCurrent, entry.temperature, entry.altitude, coords);
|
||||
file.print(line);
|
||||
|
||||
if(currentTime >= lastFlushTime + 10.0f)
|
||||
|
@ -11,6 +11,8 @@ public:
|
||||
float speed = 0.0f; // m/s
|
||||
float temperature = 0.0f; // in °C
|
||||
float altitude = 0.0f; // in m
|
||||
float latitude = -1000.0f;
|
||||
float longitude = -1000.0f;
|
||||
|
||||
bool isDifferent(const Entry& other)
|
||||
{
|
||||
@ -20,7 +22,9 @@ public:
|
||||
|| std::abs(batteryOutputCurrent - other.batteryOutputCurrent) > 0.1f * scale
|
||||
|| std::abs(speed - other.speed) > 0.1f
|
||||
|| std::abs(temperature - other.temperature) > 0.5f * scale
|
||||
|| std::abs(altitude - other.altitude) > 0.5f * scale;
|
||||
|| std::abs(altitude - other.altitude) > 0.5f * scale
|
||||
|| std::abs(latitude - other.latitude) > 0.0001f
|
||||
|| std::abs(longitude - other.longitude) > 0.0001f;
|
||||
}
|
||||
};
|
||||
|
||||
@ -30,13 +34,15 @@ public:
|
||||
|
||||
static DataLogger& get() { return mainLogger; }
|
||||
|
||||
void open();
|
||||
void open(const char* currentDateTime = nullptr);
|
||||
void close();
|
||||
void log(unsigned long timeMilliseconds, const Entry& entry);
|
||||
|
||||
bool isOpen();
|
||||
const char* currentLogFileName();
|
||||
|
||||
void enableGPSCoordinates(bool enable) { gpsCoordsEnabled = enable; }
|
||||
|
||||
private:
|
||||
static DataLogger mainLogger;
|
||||
|
||||
@ -47,4 +53,6 @@ private:
|
||||
float lastFlushTime = 0.0f;
|
||||
|
||||
File file; // @suppress("Abstract class cannot be instantiated")
|
||||
|
||||
bool gpsCoordsEnabled = false;
|
||||
};
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "lint-helpers.h"
|
||||
|
||||
#define DUMMY_DATA 0
|
||||
#define ENABLE_GPS_COORDINATES 1
|
||||
|
||||
AsyncWebServer server(80);
|
||||
|
||||
@ -41,6 +42,10 @@ uint16_t batteryVoltage = 0; // in mV
|
||||
uint16_t batteryOutputCurrent = 0; // in mV
|
||||
int16_t temperature = 0; // in tenth of °C
|
||||
int32_t altitude = 0; // in mm above sea level (can be negative if below sea level, or depending on atmospheric conditions)
|
||||
float latitude = -1000.0f; // in decimal degrees
|
||||
float longitude = -1000.0f; // in decimal degrees
|
||||
float gpsAltitude = -1000.0f; // in meters, above sea level
|
||||
char realtime[32] = {0}; // UTC date and time, in format YYYY-MM-DDTHH:mm:ss.sssZ
|
||||
|
||||
// current trip
|
||||
uint32_t tripDistance = 0; // in meters
|
||||
@ -193,6 +198,10 @@ void setup()
|
||||
|
||||
OTA.begin();
|
||||
|
||||
#if ENABLE_GPS_COORDINATES
|
||||
DataLogger::get().enableGPSCoordinates(true);
|
||||
#endif
|
||||
|
||||
Wire.begin(I2C_SDA, I2C_SCL);
|
||||
|
||||
pressureSensor.begin(Wire);
|
||||
@ -232,10 +241,54 @@ void setup()
|
||||
int usedSize = (int)(SPIFFS.usedBytes() / 1000);
|
||||
|
||||
char json[256];
|
||||
sprintf(json, "{\"v\":%d,\"c\":%d,\"s\":%d,\"td\":%d,\"ttt\":%d,\"tmt\":%d,\"tae\":%d,\"tme\":%d,\"temp\":%d,\"alt\":%d,\"log\":\"%s\",\"tot\":%d,\"used\":%d}", v, c, s, td, ttt, tmt, tae, tme, temp, alt, logFileName, totalSize, usedSize);
|
||||
sprintf(json, "{\"v\":%d,\"c\":%d,\"s\":%d,\"td\":%d,\"ttt\":%d,\"tmt\":%d,\"tae\":%d,\"tme\":%d,\"temp\":%d,\"alt\":%d,\"log\":\"%s\",\"tot\":%d,\"used\":%d,\"lat\":%.5f,\"lng\":%.5f,\"d\":\"%s\"}", v, c, s, td, ttt, tmt, tae, tme, temp, alt, logFileName, totalSize, usedSize, latitude, longitude, realtime);
|
||||
request->send(200, "text/json", json);
|
||||
});
|
||||
|
||||
server.on("/api/info", HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
//DebugLog.println("/api/info");
|
||||
|
||||
/*int params = request->params();
|
||||
for(int i=0;i<params;i++){
|
||||
AsyncWebParameter* p = request->getParam(i);
|
||||
DebugLog.print(p->name().c_str());
|
||||
DebugLog.print("=");
|
||||
DebugLog.println(p->value().c_str());
|
||||
}*/
|
||||
|
||||
AsyncWebParameter* latitudeParam = request->getParam("lat", true);
|
||||
AsyncWebParameter* longitudeParam = request->getParam("lng", true);
|
||||
AsyncWebParameter* altitudeParam = request->getParam("alt", true);
|
||||
if(latitudeParam != nullptr && longitudeParam != nullptr)
|
||||
{
|
||||
char *ending = nullptr;
|
||||
latitude = strtof(latitudeParam->value().c_str(), &ending);
|
||||
if (*ending != 0)
|
||||
latitude = -1000.0f;
|
||||
longitude = strtof(longitudeParam->value().c_str(), &ending);
|
||||
if (*ending != 0)
|
||||
longitude = -1000.0f;
|
||||
//DebugLog.print("lat="); DebugLog.print(latitude); DebugLog.print(" lng="); DebugLog.println(longitude);
|
||||
|
||||
if(altitudeParam != nullptr)
|
||||
{
|
||||
gpsAltitude = strtof(altitudeParam->value().c_str(), &ending);
|
||||
if (*ending != 0)
|
||||
gpsAltitude = -1000.0f;
|
||||
//DebugLog.print("alt="); DebugLog.println(gpsAltitude);
|
||||
}
|
||||
}
|
||||
|
||||
AsyncWebParameter* timeParam = request->getParam("time", true);
|
||||
if(timeParam != nullptr)
|
||||
{
|
||||
strcpy(realtime, timeParam->value().c_str());
|
||||
//DebugLog.print("time="); DebugLog.println(realtime);
|
||||
}
|
||||
|
||||
request->send(200);
|
||||
});
|
||||
|
||||
server.on("/api/log/list", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
String json;
|
||||
|
||||
@ -437,13 +490,16 @@ void loop()
|
||||
entry.temperature = (float)temperature / 10.0f;
|
||||
entry.altitude = (float)altitude / 1000.0f;
|
||||
|
||||
entry.latitude = latitude;
|
||||
entry.longitude = longitude;
|
||||
|
||||
if(entry.speed > 0.0f)
|
||||
{
|
||||
stoppedSince = -1;
|
||||
if(!DataLogger::get().isOpen())
|
||||
{
|
||||
DebugLog.println("Starting DataLogger");
|
||||
DataLogger::get().open();
|
||||
DataLogger::get().open(realtime);
|
||||
tripDistance = 0;
|
||||
tripMovingTime = 0;
|
||||
tripTotalTime = 0;
|
||||
@ -488,8 +544,16 @@ void loop()
|
||||
|
||||
uint32_t altitudeMillimeters = (uint32_t)(entry.altitude * 1000.0f + 0.5f);
|
||||
static uint32_t lastLoopAltitude = altitudeMillimeters;
|
||||
uint32_t altitudeChange = altitudeMillimeters - lastLoopAltitude;
|
||||
lastLoopAltitude = altitudeMillimeters;
|
||||
uint32_t clampedPositiveAltitudeChange = 0;
|
||||
if(altitudeMillimeters > lastLoopAltitude + 300)
|
||||
{
|
||||
clampedPositiveAltitudeChange = altitudeMillimeters - lastLoopAltitude;
|
||||
lastLoopAltitude = altitudeMillimeters;
|
||||
}
|
||||
else if(lastLoopAltitude > 300 && altitudeMillimeters < lastLoopAltitude - 300)
|
||||
{
|
||||
lastLoopAltitude = altitudeMillimeters;
|
||||
}
|
||||
|
||||
if(isOnTrip)
|
||||
{
|
||||
@ -497,8 +561,7 @@ void loop()
|
||||
if(isMoving) tripMovingTime += newSeconds;
|
||||
tripDistance += newMeters;
|
||||
|
||||
if(altitudeChange > 0)
|
||||
tripAscendingElevation += altitudeChange;
|
||||
tripAscendingElevation += clampedPositiveAltitudeChange;
|
||||
|
||||
static float remainingEnergy = 0.0f;
|
||||
float newEnergy = entry.batteryVoltage * entry.batteryOutputCurrent * ((float)dt / 1000.0f) + remainingEnergy;
|
||||
|
@ -10,7 +10,10 @@ export interface Status {
|
||||
tripAscendingElevation: number; // in meters
|
||||
tripMotorEnergy: number; // in Watt-hour
|
||||
temperature: number; // in Celcius degrees
|
||||
latitude?: number; // latitude (decimal degrees)
|
||||
longitude?: number; // longitude (decimal degrees)
|
||||
altitude: number; // in meters above sea level
|
||||
dateTime?: Date; // current date and time (UTC)
|
||||
};
|
||||
|
||||
interface ApiStatus {
|
||||
@ -24,11 +27,29 @@ interface ApiStatus {
|
||||
tme: number; // trip motor energy (Wh)
|
||||
temp: number; // temperature (tenth of °C)
|
||||
alt: number; // altitude (mm)
|
||||
lat: number; // latitude (in decimal degrees), or -1000 if unknown
|
||||
lng: number; // longitude (in decimal degrees), or -1000 if unknown
|
||||
d: string; // current UTC date and time, in format "YYYY-MM-DDTHH:mm:ss.sssZ", or empty string if unknown
|
||||
}
|
||||
|
||||
export interface Info {
|
||||
latitude: number; // latitude (decimal degrees)
|
||||
longitude: number; // longitude (decimal degrees)
|
||||
altitude: number; // altitude (from sea level, in meters)
|
||||
time: Date; // UTC date and time
|
||||
}
|
||||
|
||||
interface ApiInfo {
|
||||
lat?: number; // latitude (decimal degrees)
|
||||
lng?: number; // longitude (decimal degrees)
|
||||
alt?: number; // altitude (from sea level, in meters)
|
||||
time?: string; // UTC date and time, in format "YYYY-MM-DDTHH:mm:ss.sssZ"
|
||||
}
|
||||
|
||||
export class MonitorApi {
|
||||
private mockServer: boolean;
|
||||
private lastStatus: Status = null;
|
||||
private lastKnownPosition: GeolocationPosition = null;
|
||||
private static api: MonitorApi = null;
|
||||
|
||||
private lastFetchTime = 0;
|
||||
@ -52,6 +73,81 @@ export class MonitorApi {
|
||||
|
||||
getStatus() { return this.lastStatus; }
|
||||
|
||||
async autoUpdateInfo(): Promise<void> {
|
||||
if (navigator.geolocation) {
|
||||
let updatePosition = (position: GeolocationPosition) => {
|
||||
this.lastKnownPosition = position;
|
||||
};
|
||||
|
||||
const options = {
|
||||
enableHighAccuracy: true,
|
||||
timeout: 6000,
|
||||
maximumAge: 3000,
|
||||
};
|
||||
|
||||
//navigator.geolocation.getCurrentPosition(updatePosition, () => updatePosition(null), options);
|
||||
}
|
||||
|
||||
const defaultPosition : GeolocationPosition = {
|
||||
timestamp: 0,
|
||||
coords: {
|
||||
latitude: -1000,
|
||||
longitude: -1000,
|
||||
accuracy: -1,
|
||||
altitude: -1000,
|
||||
altitudeAccuracy: -1,
|
||||
heading: null,
|
||||
speed: null
|
||||
}
|
||||
};
|
||||
let position = this.lastKnownPosition == null ? defaultPosition : this.lastKnownPosition;
|
||||
|
||||
let info: Info = {
|
||||
latitude: position.coords.latitude,
|
||||
longitude: position.coords.longitude,
|
||||
altitude: position.coords.altitude,
|
||||
time: new Date()
|
||||
};
|
||||
await this.postInfo(info);
|
||||
}
|
||||
|
||||
async postInfo(info: Info): Promise<void> {
|
||||
let apiInfo: ApiInfo = {
|
||||
lat: info.latitude,
|
||||
lng: info.longitude,
|
||||
alt: info.altitude,
|
||||
time: info.time.toISOString()
|
||||
};
|
||||
|
||||
if(this.mockServer) {
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
} else {
|
||||
await new Promise<void>((resolve, error) => {
|
||||
let request = new XMLHttpRequest();
|
||||
request.onreadystatechange = () => {
|
||||
if(request.readyState == 4) {
|
||||
if(request.status == 200) {
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
error();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let urlEncodedData = "";
|
||||
for(let name in apiInfo) {
|
||||
if(urlEncodedData != "") urlEncodedData += "&";
|
||||
urlEncodedData += encodeURIComponent(name)+'='+encodeURIComponent((<any>apiInfo)[name]);
|
||||
}
|
||||
|
||||
request.open('POST', '/api/info', true);
|
||||
request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
request.send(urlEncodedData);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fetchStatus(): Promise<Status> {
|
||||
let apiStatus: ApiStatus;
|
||||
|
||||
@ -94,7 +190,10 @@ export class MonitorApi {
|
||||
tae: Math.round(this.mockedTripAscendingElevation*10.0),
|
||||
tme: Math.round(this.mockedTripMotorEnergy*10.0),
|
||||
temp: Math.round((Math.cos(t*0.2)+1.0)*0.5 * 400 - 100),
|
||||
alt: Math.round(altitude * 1000)
|
||||
lat: -1000,
|
||||
lng: -1000,
|
||||
alt: Math.round(altitude * 1000),
|
||||
d: ''
|
||||
};
|
||||
} else {
|
||||
apiStatus = await new Promise<ApiStatus>((resolve, error) => {
|
||||
@ -124,7 +223,10 @@ export class MonitorApi {
|
||||
tripAscendingElevation: apiStatus.tae / 10,
|
||||
tripMotorEnergy: apiStatus.tme / 10,
|
||||
temperature: apiStatus.temp / 10,
|
||||
altitude: apiStatus.alt / 1000
|
||||
latitude: apiStatus.lat <= -900 ? null : apiStatus.lat,
|
||||
longitude: apiStatus.lng <= -900 ? null : apiStatus.lng,
|
||||
altitude: apiStatus.alt / 1000,
|
||||
dateTime: apiStatus.d == '' ? null : new Date(apiStatus.d)
|
||||
};
|
||||
|
||||
return this.lastStatus;
|
||||
|
@ -49,6 +49,7 @@ export class DashboardPage extends Page {
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
await MonitorApi.get().autoUpdateInfo();
|
||||
let newStatus = await MonitorApi.get().fetchStatus();
|
||||
if(this.status == null)
|
||||
m.redraw();
|
||||
|
@ -18,6 +18,7 @@ export class RawDataPage extends Page {
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
await MonitorApi.get().autoUpdateInfo();
|
||||
this.status = await MonitorApi.get().fetchStatus();
|
||||
m.redraw();
|
||||
if(this.autoRefresh)
|
||||
@ -35,6 +36,12 @@ export class RawDataPage extends Page {
|
||||
<p>Vitesse : {(this.status.speed * 3.6).toFixed(1)}km/h</p>
|
||||
<p>Temperature : {this.status.temperature.toFixed(1)}°C</p>
|
||||
<p>Altitude : {this.status.altitude.toFixed(1)}m</p>
|
||||
{this.status.latitude ?
|
||||
<p>Lat {this.status.latitude.toFixed(5)}° Lng {this.status.latitude.toFixed(5)}°</p>
|
||||
: null}
|
||||
{this.status.dateTime ?
|
||||
<p>Time : {this.status.dateTime.toString()}</p>
|
||||
: null}
|
||||
</div>
|
||||
: <p>Chargement...</p>;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user