diff --git a/src/airtime.cpp b/src/airtime.cpp index 683b8f2c0..3f744d4ac 100644 --- a/src/airtime.cpp +++ b/src/airtime.cpp @@ -1,117 +1,135 @@ -#include "configuration.h" #include "airtime.h" #include "NodeDB.h" +#include "configuration.h" -#define periodsToLog 24 AirTime *airTime; -uint32_t secondsPerPeriod = 3600; -uint32_t lastMillis = 0; -uint32_t secSinceBoot = 0; - -// AirTime at; - // Don't read out of this directly. Use the helper functions. -struct airtimeStruct { - uint32_t periodTX[periodsToLog]; // AirTime transmitted - uint32_t periodRX[periodsToLog]; // AirTime received and repeated (Only valid mesh packets) - uint32_t periodRX_ALL[periodsToLog]; // AirTime received regardless of valid mesh packet. Could include noise. - uint8_t lastPeriodIndex; -} airtimes; + void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms) { + + // TODO: Is the airtimes array still necessary? It's now in myNodeInfo anyway + if (reportType == TX_LOG) { DEBUG_MSG("AirTime - Packet transmitted : %ums\n", airtime_ms); - airtimes.periodTX[0] = airtimes.periodTX[0] + airtime_ms; + this->airtimes.periodTX[0] = this->airtimes.periodTX[0] + airtime_ms; myNodeInfo.air_period_tx[0] = myNodeInfo.air_period_tx[0] + airtime_ms; } else if (reportType == RX_LOG) { DEBUG_MSG("AirTime - Packet received : %ums\n", airtime_ms); - airtimes.periodRX[0] = airtimes.periodRX[0] + airtime_ms; + this->airtimes.periodRX[0] = this->airtimes.periodRX[0] + airtime_ms; myNodeInfo.air_period_rx[0] = myNodeInfo.air_period_rx[0] + airtime_ms; } else if (reportType == RX_ALL_LOG) { DEBUG_MSG("AirTime - Packet received (noise?) : %ums\n", airtime_ms); - airtimes.periodRX_ALL[0] = airtimes.periodRX_ALL[0] + airtime_ms; - } else { - DEBUG_MSG("AirTime - Unknown report time. This should never happen!!\n"); + this->airtimes.periodRX_ALL[0] = this->airtimes.periodRX_ALL[0] + airtime_ms; } + + uint8_t channelUtilPeriod = (getSecondsSinceBoot() / 10) % CHANNEL_UTILIZATION_PERIODS; + this->channelUtilization[channelUtilPeriod] = channelUtilization[channelUtilPeriod] + airtime_ms; } -uint8_t currentPeriodIndex() +uint8_t AirTime::currentPeriodIndex() { - return ((getSecondsSinceBoot() / secondsPerPeriod) % periodsToLog); + return ((getSecondsSinceBoot() / SECONDS_PER_PERIOD) % PERIODS_TO_LOG); } -void airtimeRotatePeriod() +void AirTime::airtimeRotatePeriod() { - if (airtimes.lastPeriodIndex != currentPeriodIndex()) { + if (this->airtimes.lastPeriodIndex != currentPeriodIndex()) { DEBUG_MSG("Rotating airtimes to a new period = %u\n", currentPeriodIndex()); - for (int i = periodsToLog - 2; i >= 0; --i) { - airtimes.periodTX[i + 1] = airtimes.periodTX[i]; - airtimes.periodRX[i + 1] = airtimes.periodRX[i]; - airtimes.periodRX_ALL[i + 1] = airtimes.periodRX_ALL[i]; + for (int i = PERIODS_TO_LOG - 2; i >= 0; --i) { + this->airtimes.periodTX[i + 1] = this->airtimes.periodTX[i]; + this->airtimes.periodRX[i + 1] = this->airtimes.periodRX[i]; + this->airtimes.periodRX_ALL[i + 1] = this->airtimes.periodRX_ALL[i]; myNodeInfo.air_period_tx[i + 1] = myNodeInfo.air_period_tx[i]; myNodeInfo.air_period_rx[i + 1] = myNodeInfo.air_period_rx[i]; } - airtimes.periodTX[0] = 0; - airtimes.periodRX[0] = 0; - airtimes.periodRX_ALL[0] = 0; + + this->airtimes.periodTX[0] = 0; + this->airtimes.periodRX[0] = 0; + this->airtimes.periodRX_ALL[0] = 0; myNodeInfo.air_period_tx[0] = 0; myNodeInfo.air_period_rx[0] = 0; - - airtimes.lastPeriodIndex = currentPeriodIndex(); + this->airtimes.lastPeriodIndex = currentPeriodIndex(); } } -uint32_t *airtimeReport(reportTypes reportType) +uint32_t *AirTime::airtimeReport(reportTypes reportType) { if (reportType == TX_LOG) { - return airtimes.periodTX; + return this->airtimes.periodTX; } else if (reportType == RX_LOG) { - return airtimes.periodRX; + return this->airtimes.periodRX; } else if (reportType == RX_ALL_LOG) { - return airtimes.periodRX_ALL; + return this->airtimes.periodRX_ALL; } return 0; } -uint8_t getPeriodsToLog() +uint8_t AirTime::getPeriodsToLog() { - return periodsToLog; + return PERIODS_TO_LOG; } -uint32_t getSecondsPerPeriod() +uint32_t AirTime::getSecondsPerPeriod() { - return secondsPerPeriod; + return SECONDS_PER_PERIOD; } -uint32_t getSecondsSinceBoot() +uint32_t AirTime::getSecondsSinceBoot() { - return secSinceBoot; + return this->secSinceBoot; +} + +float AirTime::channelUtilizationPercent() +{ + uint32_t sum = 0; + for (uint32_t i = 0; i < CHANNEL_UTILIZATION_PERIODS; i++) { + sum += this->channelUtilization[i]; + // DEBUG_MSG("ChanUtilArray %u %u\n", i, this->channelUtilization[i]); + } + + return (float(sum) / float(CHANNEL_UTILIZATION_PERIODS * 10 * 1000)) * 100; } AirTime::AirTime() : concurrency::OSThread("AirTime") {} int32_t AirTime::runOnce() { - //DEBUG_MSG("AirTime::runOnce()\n"); - - airtimeRotatePeriod(); secSinceBoot++; - /* - This actually doesn't need to be run once per second but we currently use it for the - secSinceBoot counter. + uint8_t utilPeriod = (getSecondsSinceBoot() / 10) % CHANNEL_UTILIZATION_PERIODS; + + if (firstTime) { + airtimeRotatePeriod(); + + for (uint32_t i = 0; i < CHANNEL_UTILIZATION_PERIODS; i++) { + this->channelUtilization[i] = 0; + } + + firstTime = false; + lastUtilPeriod = utilPeriod; + + } else { + + // Reset the channelUtilization window when we roll over + if (lastUtilPeriod != utilPeriod) { + lastUtilPeriod = utilPeriod; + + this->channelUtilization[utilPeriod] = 0; + } + + // Update channel_utilization every second. + myNodeInfo.channel_utilization = airTime->channelUtilizationPercent(); + } - If we have a better counter of how long the device has been online (and not millis()) - then we can change this to something less frequent. Maybe once ever 5 seconds? - */ return (1000 * 1); } \ No newline at end of file diff --git a/src/airtime.h b/src/airtime.h index 134bad47d..bf78ac3a9 100644 --- a/src/airtime.h +++ b/src/airtime.h @@ -23,21 +23,18 @@ RX_ALL_LOG - RX_LOG = Other lora radios on our frequency channel. */ + +#define CHANNEL_UTILIZATION_PERIODS 6 +#define SECONDS_PER_PERIOD 3600 +#define PERIODS_TO_LOG 24 + + enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG }; void logAirtime(reportTypes reportType, uint32_t airtime_ms); -void airtimeRotatePeriod(); - -uint8_t currentPeriodIndex(); -uint8_t getPeriodsToLog(); - -uint32_t getSecondsSinceBoot(); - uint32_t *airtimeReport(reportTypes reportType); -uint32_t getSecondsPerPeriod(); - class AirTime : private concurrency::OSThread { @@ -45,6 +42,27 @@ class AirTime : private concurrency::OSThread AirTime(); void logAirtime(reportTypes reportType, uint32_t airtime_ms); + float channelUtilizationPercent(); + uint32_t channelUtilization[CHANNEL_UTILIZATION_PERIODS]; + + uint8_t currentPeriodIndex(); + void airtimeRotatePeriod(); + uint8_t getPeriodsToLog(); + uint32_t getSecondsPerPeriod(); + uint32_t getSecondsSinceBoot(); + uint32_t *airtimeReport(reportTypes reportType); + + private: + bool firstTime = true; + uint8_t lastUtilPeriod = 0; + uint32_t secSinceBoot = 0; + + struct airtimeStruct { + uint32_t periodTX[PERIODS_TO_LOG]; // AirTime transmitted + uint32_t periodRX[PERIODS_TO_LOG]; // AirTime received and repeated (Only valid mesh packets) + uint32_t periodRX_ALL[PERIODS_TO_LOG]; // AirTime received regardless of valid mesh packet. Could include noise. + uint8_t lastPeriodIndex; + } airtimes; protected: virtual int32_t runOnce() override; diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 502c60b02..cdf5ab1a5 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1398,11 +1398,11 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat display->drawString(x, y + FONT_HEIGHT_SMALL * 1, uptime); -#ifndef NO_ESP32 - // Show CPU Frequency. - display->drawString(x + SCREEN_WIDTH - display->getStringWidth("CPU " + String(getCpuFrequencyMhz()) + "MHz"), - y + FONT_HEIGHT_SMALL * 1, "CPU " + String(getCpuFrequencyMhz()) + "MHz"); -#endif + // Display Channel Utilization + char chUtil[13]; + sprintf(chUtil, "ChUtil %2.0f%%", airTime->channelUtilizationPercent()); + display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil), + y + FONT_HEIGHT_SMALL * 1, chUtil); // Line 3 if (radioConfig.preferences.gps_format != GpsCoordinateFormat_GpsFormatDMS) // if DMS then don't draw altitude diff --git a/src/mesh/generated/admin.pb.h b/src/mesh/generated/admin.pb.h index 08c647aea..ebc6fc9a0 100644 --- a/src/mesh/generated/admin.pb.h +++ b/src/mesh/generated/admin.pb.h @@ -86,7 +86,7 @@ extern const pb_msgdesc_t AdminMessage_msg; #define AdminMessage_fields &AdminMessage_msg /* Maximum encoded size of messages (where known) */ -#define AdminMessage_size 461 +#define AdminMessage_size 529 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/deviceonly.pb.h b/src/mesh/generated/deviceonly.pb.h index 2d4e53f0c..284aa68f7 100644 --- a/src/mesh/generated/deviceonly.pb.h +++ b/src/mesh/generated/deviceonly.pb.h @@ -125,7 +125,7 @@ extern const pb_msgdesc_t ChannelFile_msg; /* Maximum encoded size of messages (where known) */ #define LegacyRadioConfig_size 4 #define LegacyRadioConfig_LegacyPreferences_size 2 -#define DeviceState_size 9943 +#define DeviceState_size 9949 #define ChannelFile_size 832 #ifdef __cplusplus diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index 8f4a7ffbc..7419ac023 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -164,6 +164,7 @@ typedef struct _MyNodeInfo { pb_size_t air_period_rx_count; uint32_t air_period_rx[24]; bool has_wifi; + float channel_utilization; } MyNodeInfo; typedef struct _Position { @@ -332,7 +333,7 @@ extern "C" { #define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0} #define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN} #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0} -#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0} +#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, 0} #define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN} #define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}} #define ToRadio_init_default {0, {MeshPacket_init_default}} @@ -344,7 +345,7 @@ extern "C" { #define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0} #define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN} #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0} -#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0} +#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, 0} #define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN} #define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}} #define ToRadio_init_zero {0, {MeshPacket_init_zero}} @@ -378,6 +379,7 @@ extern "C" { #define MyNodeInfo_air_period_tx_tag 16 #define MyNodeInfo_air_period_rx_tag 17 #define MyNodeInfo_has_wifi_tag 18 +#define MyNodeInfo_channel_utilization_tag 19 #define Position_latitude_i_tag 1 #define Position_longitude_i_tag 2 #define Position_altitude_tag 3 @@ -559,7 +561,8 @@ X(a, STATIC, SINGULAR, UINT32, min_app_version, 14) \ X(a, STATIC, SINGULAR, UINT32, max_channels, 15) \ X(a, STATIC, REPEATED, UINT32, air_period_tx, 16) \ X(a, STATIC, REPEATED, UINT32, air_period_rx, 17) \ -X(a, STATIC, SINGULAR, BOOL, has_wifi, 18) +X(a, STATIC, SINGULAR, BOOL, has_wifi, 18) \ +X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 19) #define MyNodeInfo_CALLBACK NULL #define MyNodeInfo_DEFAULT NULL @@ -637,9 +640,9 @@ extern const pb_msgdesc_t ToRadio_PeerInfo_msg; #define Data_size 260 #define MeshPacket_size 311 #define NodeInfo_size 270 -#define MyNodeInfo_size 445 +#define MyNodeInfo_size 451 #define LogRecord_size 81 -#define FromRadio_size 454 +#define FromRadio_size 460 #define ToRadio_size 314 #define ToRadio_PeerInfo_size 8 diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 4174c0a23..c1fced6de 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -502,12 +502,12 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) res->print("\"tx_log\": ["); - logArray = airtimeReport(TX_LOG); - for (int i = 0; i < getPeriodsToLog(); i++) { + logArray = airTime->airtimeReport(TX_LOG); + for (int i = 0; i < airTime->getPeriodsToLog(); i++) { uint32_t tmp; tmp = *(logArray + i); res->printf("%d", tmp); - if (i != getPeriodsToLog() - 1) { + if (i != airTime->getPeriodsToLog() - 1) { res->print(", "); } } @@ -515,12 +515,12 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) res->println("],"); res->print("\"rx_log\": ["); - logArray = airtimeReport(RX_LOG); - for (int i = 0; i < getPeriodsToLog(); i++) { + logArray = airTime->airtimeReport(RX_LOG); + for (int i = 0; i < airTime->getPeriodsToLog(); i++) { uint32_t tmp; tmp = *(logArray + i); res->printf("%d", tmp); - if (i != getPeriodsToLog() - 1) { + if (i != airTime->getPeriodsToLog() - 1) { res->print(", "); } } @@ -528,20 +528,20 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) res->println("],"); res->print("\"rx_all_log\": ["); - logArray = airtimeReport(RX_ALL_LOG); - for (int i = 0; i < getPeriodsToLog(); i++) { + logArray = airTime->airtimeReport(RX_ALL_LOG); + for (int i = 0; i < airTime->getPeriodsToLog(); i++) { uint32_t tmp; tmp = *(logArray + i); res->printf("%d", tmp); - if (i != getPeriodsToLog() - 1) { + if (i != airTime->getPeriodsToLog() - 1) { res->print(", "); } } res->println("],"); - res->printf("\"seconds_since_boot\": %u,\n", getSecondsSinceBoot()); - res->printf("\"seconds_per_period\": %u,\n", getSecondsPerPeriod()); - res->printf("\"periods_to_log\": %u\n", getPeriodsToLog()); + res->printf("\"seconds_since_boot\": %u,\n", airTime->getSecondsSinceBoot()); + res->printf("\"seconds_per_period\": %u,\n", airTime->getSecondsPerPeriod()); + res->printf("\"periods_to_log\": %u\n", airTime->getPeriodsToLog()); res->println("},");