diff --git a/platformio.ini b/platformio.ini index 50d381c8..14e6ddbd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -38,8 +38,8 @@ build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/n ;upload_port = /dev/ttyUSB0 ;monitor_port = /dev/ttyUSB0 -;upload_port = /dev/cu.SLAB_USBtoUART -;monitor_port = /dev/cu.SLAB_USBtoUART +upload_port = /dev/cu.SLAB_USBtoUART +monitor_port = /dev/cu.SLAB_USBtoUART ; the default is esptool ; upload_protocol = esp-prog diff --git a/src/airtime.cpp b/src/airtime.cpp index 8f0f20f1..fb2b2251 100644 --- a/src/airtime.cpp +++ b/src/airtime.cpp @@ -3,8 +3,13 @@ #define hoursToLog 48 -// Here for convience and to avoid magic numbers. +// A reminder that there are 3600 seconds in an hour so I don't have +// to keep googling it. +// This can be changed to a smaller number to speed up testing. +// uint16_t secondsPerHour = 3600; +uint32_t lastMillis = 0; +uint32_t secSinceBoot = 0; // Don't read out of this directly. Use the helper functions. struct airtimeStruct { @@ -16,65 +21,63 @@ struct airtimeStruct { void logAirtime(reportTypes reportType, uint32_t airtime_ms) { - currentHourIndexReset(); if (reportType == TX_LOG) { - airtimes.hourTX[currentHourIndex()] = airtimes.hourTX[currentHourIndex()] + round(airtime_ms / 1000); + airtimes.hourTX[0] = airtimes.hourTX[0] + round(airtime_ms / 1000); } else if (reportType == RX_LOG) { - airtimes.hourRX[currentHourIndex()] = airtimes.hourRX[currentHourIndex()] + round(airtime_ms / 1000); + airtimes.hourRX[0] = airtimes.hourRX[0] + round(airtime_ms / 1000); } else if (reportType == RX_ALL_LOG) { - airtimes.hourRX_ALL[currentHourIndex()] = airtimes.hourRX_ALL[currentHourIndex()] + round(airtime_ms / 1000); + airtimes.hourRX_ALL[0] = airtimes.hourRX_ALL[0] + round(airtime_ms / 1000); } else { // Unknown report type - } } -// This will let us easily switch away from using millis at some point. -// todo: Don't use millis, instead maintain our own count of time since -// boot in seconds. -uint32_t secondsSinceBoot() +uint32_t getSecondsSinceBoot() { - return millis() / 1000; + return secSinceBoot; } uint8_t currentHourIndex() { // return ((secondsSinceBoot() - (secondsSinceBoot() / (hoursToLog * secondsPerHour))) / secondsPerHour); - return ((secondsSinceBoot() / secondsPerHour) % hoursToLog); + return ((getSecondsSinceBoot() / secondsPerHour) % hoursToLog); } -// currentHourIndexReset() should be called every time we receive a packet to log (either RX or TX) -// and every time we are asked to report on airtime usage. -void currentHourIndexReset() +void airtimeCalculator() { - if (airtimes.lastHourIndex != currentHourIndex()) { - airtimes.hourTX[currentHourIndex()] = 0; - airtimes.hourRX[currentHourIndex()] = 0; - airtimes.hourRX_ALL[currentHourIndex()] = 0; + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + secSinceBoot++; + // DEBUG_MSG("------- lastHourIndex %i currentHourIndex %i\n", airtimes.lastHourIndex, currentHourIndex()); + if (airtimes.lastHourIndex != currentHourIndex()) { + for (int i = hoursToLog - 2; i >= 0; --i) { + airtimes.hourTX[i + 1] = airtimes.hourTX[i]; + airtimes.hourRX[i + 1] = airtimes.hourRX[i]; + airtimes.hourRX_ALL[i + 1] = airtimes.hourRX_ALL[i]; + } + airtimes.hourTX[0] = 0; - airtimes.lastHourIndex = currentHourIndex(); + airtimes.lastHourIndex = currentHourIndex(); + } } } uint16_t *airtimeReport(reportTypes reportType) { - static uint16_t array[hoursToLog]; + // currentHourIndexReset(); - currentHourIndexReset(); - - for (int i = 0; i < hoursToLog; i++) { - if (reportType == TX_LOG) { - array[i] = airtimes.hourTX[(airtimes.lastHourIndex + i) % hoursToLog]; - } else if (reportType == RX_LOG) { - array[i] = airtimes.hourRX[(airtimes.lastHourIndex + i) % hoursToLog]; - } else if (reportType == RX_ALL_LOG) { - array[i] = airtimes.hourRX_ALL[(airtimes.lastHourIndex + i) % hoursToLog]; - } else { - // Unknown report type - return array; - } + if (reportType == TX_LOG) { + return airtimes.hourTX; + } else if (reportType == RX_LOG) { + return airtimes.hourRX; + } else if (reportType == RX_ALL_LOG) { + return airtimes.hourRX_ALL; } + return 0; +} - return array; +uint8_t getHoursToLog() +{ + return hoursToLog; } \ No newline at end of file diff --git a/src/airtime.h b/src/airtime.h index 5df916a5..0324cd56 100644 --- a/src/airtime.h +++ b/src/airtime.h @@ -8,9 +8,11 @@ enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG }; void logAirtime(reportTypes reportType, uint32_t airtime_ms); -void currentHourIndexReset(); -uint8_t currentHourIndex(); +void airtimeCalculator(); -uint32_t secondsSinceBoot(); +uint8_t currentHourIndex(); +uint8_t getHoursToLog(); + +uint32_t getSecondsSinceBoot(); uint16_t *airtimeReport(reportTypes reportType); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 3f028613..762cf5e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "NodeDB.h" #include "PowerFSM.h" #include "UBloxGPS.h" +#include "airtime.h" #include "configuration.h" #include "error.h" #include "power.h" @@ -558,4 +559,7 @@ void loop() // We want to sleep as long as possible here - because it saves power mainDelay.delay(delayMsec); // if (didWake) DEBUG_MSG("wake!\n"); + + // Handles cleanup for the airtime calculator. + airtimeCalculator(); } diff --git a/src/meshwifi/meshhttp.cpp b/src/meshwifi/meshhttp.cpp index b6953c32..e24f6717 100644 --- a/src/meshwifi/meshhttp.cpp +++ b/src/meshwifi/meshhttp.cpp @@ -1,10 +1,11 @@ #include "meshwifi/meshhttp.h" #include "NodeDB.h" +#include "PowerFSM.h" +#include "airtime.h" #include "configuration.h" #include "main.h" #include "meshhttpStatic.h" #include "meshwifi/meshwifi.h" -#include "PowerFSM.h" #include "sleep.h" #include #include @@ -64,6 +65,7 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res); void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res); void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res); void handleBlinkLED(HTTPRequest *req, HTTPResponse *res); +void handleReport(HTTPRequest *req, HTTPResponse *res); void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function next); void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function next); @@ -80,7 +82,7 @@ char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"} {".js", "text/javascript"}, {".png", "image/png"}, {".jpg", "image/jpg"}, {".gz", "application/gzip"}, {".gif", "image/gif"}, {".json", "application/json"}, - {".css", "text/css"}, {".ico","image/vnd.microsoft.icon"}, + {".css", "text/css"}, {".ico", "image/vnd.microsoft.icon"}, {".svg", "image/svg+xml"}, {"", ""}}; void handleWebResponse() @@ -248,6 +250,7 @@ void initWebServer() ResourceNode *nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload); ResourceNode *nodeJsonScanNetworks = new ResourceNode("/json/scanNetworks", "GET", &handleScanNetworks); ResourceNode *nodeJsonBlinkLED = new ResourceNode("/json/blink", "POST", &handleBlinkLED); + ResourceNode *nodeJsonReport = new ResourceNode("/json/report", "GET", &handleReport); ResourceNode *nodeJsonSpiffsBrowseStatic = new ResourceNode("/json/spiffs/browse/static/", "GET", &handleSpiffsBrowseStatic); ResourceNode *nodeJsonDelete = new ResourceNode("/json/spiffs/delete/static", "DELETE", &handleSpiffsDeleteStatic); @@ -267,6 +270,7 @@ void initWebServer() secureServer->registerNode(nodeJsonBlinkLED); secureServer->registerNode(nodeJsonSpiffsBrowseStatic); secureServer->registerNode(nodeJsonDelete); + secureServer->registerNode(nodeJsonReport); secureServer->setDefaultNode(node404); secureServer->addMiddleware(&middlewareSpeedUp240); @@ -287,6 +291,7 @@ void initWebServer() insecureServer->registerNode(nodeJsonBlinkLED); insecureServer->registerNode(nodeJsonSpiffsBrowseStatic); insecureServer->registerNode(nodeJsonDelete); + insecureServer->registerNode(nodeJsonReport); insecureServer->setDefaultNode(node404); insecureServer->addMiddleware(&middlewareSpeedUp160); @@ -458,26 +463,26 @@ void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res) void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res) { - ResourceParameters *params = req->getParams(); - std::string paramValDelete; + ResourceParameters *params = req->getParams(); + std::string paramValDelete; - res->setHeader("Content-Type", "application/json"); - if (params->getQueryParameter("delete", paramValDelete)) { - std::string pathDelete = "/" + paramValDelete; - if (SPIFFS.remove(pathDelete.c_str())) { - Serial.println(pathDelete.c_str()); - res->println("{"); - res->println("\"status\": \"ok\""); - res->println("}"); - return; - } else { - Serial.println(pathDelete.c_str()); - res->println("{"); - res->println("\"status\": \"Error\""); - res->println("}"); - return; + res->setHeader("Content-Type", "application/json"); + if (params->getQueryParameter("delete", paramValDelete)) { + std::string pathDelete = "/" + paramValDelete; + if (SPIFFS.remove(pathDelete.c_str())) { + Serial.println(pathDelete.c_str()); + res->println("{"); + res->println("\"status\": \"ok\""); + res->println("}"); + return; + } else { + Serial.println(pathDelete.c_str()); + res->println("{"); + res->println("\"status\": \"Error\""); + res->println("}"); + return; + } } - } } void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res) @@ -1041,6 +1046,85 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res) res->println("}"); } +void handleReport(HTTPRequest *req, HTTPResponse *res) +{ + + ResourceParameters *params = req->getParams(); + std::string content; + + if (!params->getQueryParameter("content", content)) { + content = "json"; + } + + if (content == "json") { + res->setHeader("Content-Type", "application/json"); + + } else { + res->setHeader("Content-Type", "text/html"); + res->println("
");
+    }
+
+    res->println("{");
+
+    res->println("\"data\": {");
+
+    res->println("\"airtime\": {");
+
+    uint16_t *logArray;
+
+    res->print("\"tx_log\": [");
+
+    logArray = airtimeReport(TX_LOG);
+    for (int i = 0; i < getHoursToLog(); i++) {
+        uint16_t tmp;
+        tmp = *(logArray + i);
+        res->printf("%d", tmp);
+        if (i != getHoursToLog() - 1) {
+            res->print(", ");
+        }
+    }
+
+    res->println("],");
+    res->print("\"rx_log\": [");
+
+    logArray = airtimeReport(RX_LOG);
+    for (int i = 0; i < getHoursToLog(); i++) {
+        uint16_t tmp;
+        tmp = *(logArray + i);
+        res->printf("%d", tmp);
+        if (i != getHoursToLog() - 1) {
+            res->print(", ");
+        }
+    }
+
+    res->println("],");
+    res->print("\"rx_all_log\": [");
+
+    logArray = airtimeReport(RX_ALL_LOG);
+    for (int i = 0; i < getHoursToLog(); i++) {
+        uint16_t tmp;
+        tmp = *(logArray + i);
+        res->printf("%d", tmp);
+        if (i != getHoursToLog() - 1) {
+            res->print(", ");
+        }
+    }
+
+    res->println("],");
+    res->printf("\"seconds_since_boot\": %u,\n", getSecondsSinceBoot());
+    res->printf("\"hours_to_log\": %u\n", getHoursToLog());
+
+    res->println("},");
+
+    res->println("\"something\": 123,");
+    res->println("\"something_else\": 123");
+
+    res->println("},");
+
+    res->println("\"status\": \"ok\"");
+    res->println("}");
+}
+
 void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
 {
     res->setHeader("Content-Type", "application/json");