From 7396d0f2419b916b65dbc9664dc402ae53ec5590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 21 Dec 2022 13:36:38 +0100 Subject: [PATCH] Cherry Picking Stuff from develop... --- bin/regen-protos.bat | 2 +- bin/regen-protos.sh | 6 +- src/Power.cpp | 3 + src/airtime.cpp | 14 +++ src/airtime.h | 2 + src/graphics/Screen.cpp | 89 +++++++++--------- src/graphics/images.h | 14 ++- src/mesh/NodeDB.cpp | 1 + src/mesh/Router.cpp | 13 +++ src/mesh/http/WiFiAPClient.cpp | 134 +++++++++++++++++----------- src/mesh/http/WiFiAPClient.h | 4 + src/modules/AdminModule.cpp | 5 +- src/modules/CannedMessageModule.cpp | 4 + src/modules/esp32/AudioModule.cpp | 48 +++++----- src/power.h | 3 + 15 files changed, 220 insertions(+), 122 deletions(-) diff --git a/bin/regen-protos.bat b/bin/regen-protos.bat index 12ee7d7ab..5c576c5b2 100644 --- a/bin/regen-protos.bat +++ b/bin/regen-protos.bat @@ -1 +1 @@ -cd protobufs && ..\nanopb-0.4.6\generator-bin\protoc.exe --nanopb_out=-v:..\src\mesh\generated -I=..\protobufs *.proto +cd protobufs && ..\nanopb-0.4.7\generator-bin\protoc.exe --nanopb_out=-v:..\src\mesh\generated -I=..\protobufs *.proto diff --git a/bin/regen-protos.sh b/bin/regen-protos.sh index 2734c213b..133d587a3 100755 --- a/bin/regen-protos.sh +++ b/bin/regen-protos.sh @@ -2,13 +2,13 @@ set -e -echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.6 to be located in the" +echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.7 to be located in the" echo "firmware root directory if the following step fails, you should download the correct" -echo "prebuilt binaries for your computer into nanopb-0.4.6" +echo "prebuilt binaries for your computer into nanopb-0.4.7" # the nanopb tool seems to require that the .options file be in the current directory! cd protobufs -../nanopb-0.4.6/generator-bin/protoc --nanopb_out=-v:../src/mesh/generated -I=../protobufs *.proto +../nanopb-0.4.7/generator-bin/protoc --nanopb_out=-v:../src/mesh/generated -I=../protobufs *.proto #echo "Regenerating protobuf documentation - if you see an error message" #echo "you can ignore it unless doing a new protobuf release to github." diff --git a/src/Power.cpp b/src/Power.cpp index d5fe312e9..d2c5b01a0 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -182,6 +182,9 @@ Power::Power() : OSThread("Power") { statusHandler = {}; low_voltage_counter = 0; +#ifdef DEBUG_HEAP + lastheap = ESP.getFreeHeap(); +#endif } bool Power::analogInit() diff --git a/src/airtime.cpp b/src/airtime.cpp index fed4ef8aa..1c2fb3233 100644 --- a/src/airtime.cpp +++ b/src/airtime.cpp @@ -117,6 +117,20 @@ float AirTime::utilizationTXPercent() return (float(sum) / float(MS_IN_HOUR)) * 100; } +// Get the amount of minutes we have to be silent before we can send again +uint8_t AirTime::getSilentMinutes(float txPercent, float dutyCycle) +{ + float newTxPercent = txPercent; + for (int8_t i = MINUTES_IN_HOUR-1; i >= 0; --i) { + newTxPercent -= ((float)this->utilizationTX[i] / (MS_IN_MINUTE * MINUTES_IN_HOUR / 100)); + if (newTxPercent < dutyCycle) + return MINUTES_IN_HOUR-1-i; + } + + return MINUTES_IN_HOUR; +} + + AirTime::AirTime() : concurrency::OSThread("AirTime"),airtimes({}) { } diff --git a/src/airtime.h b/src/airtime.h index f6b9bdcb5..3f38f39f8 100644 --- a/src/airtime.h +++ b/src/airtime.h @@ -29,6 +29,7 @@ #define PERIODS_TO_LOG 8 #define MINUTES_IN_HOUR 60 #define SECONDS_IN_MINUTE 60 +#define MS_IN_MINUTE (SECONDS_IN_MINUTE * 1000) #define MS_IN_HOUR (MINUTES_IN_HOUR * SECONDS_IN_MINUTE * 1000) @@ -57,6 +58,7 @@ class AirTime : private concurrency::OSThread uint32_t getSecondsPerPeriod(); uint32_t getSecondsSinceBoot(); uint32_t *airtimeReport(reportTypes reportType); + uint8_t getSilentMinutes(float txPercent, float dutyCycle); private: bool firstTime = true; diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 3ea4463ea..30fc396a9 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -35,6 +35,7 @@ along with this program. If not, see . #include "mesh/Channels.h" #include "mesh/generated/deviceonly.pb.h" #include "modules/TextMessageModule.h" +#include "modules/esp32/StoreForwardModule.h" #include "sleep.h" #include "target_specific.h" #include "utils.h" @@ -95,17 +96,17 @@ static uint16_t displayWidth, displayHeight; #if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) // The screen is bigger so use bigger fonts -#define FONT_SMALL ArialMT_Plain_16 -#define FONT_MEDIUM ArialMT_Plain_24 -#define FONT_LARGE ArialMT_Plain_24 +#define FONT_SMALL ArialMT_Plain_16 // Height: 19 +#define FONT_MEDIUM ArialMT_Plain_24 // Height: 28 +#define FONT_LARGE ArialMT_Plain_24 // Height: 28 #else #ifdef OLED_RU #define FONT_SMALL ArialMT_Plain_10_RU #else -#define FONT_SMALL ArialMT_Plain_10 +#define FONT_SMALL ArialMT_Plain_10 // Height: 13 #endif -#define FONT_MEDIUM ArialMT_Plain_16 -#define FONT_LARGE ArialMT_Plain_24 +#define FONT_MEDIUM ArialMT_Plain_16 // Height: 19 +#define FONT_LARGE ArialMT_Plain_24 // Height: 28 #endif #define fontHeight(font) ((font)[1] + 1) // height is position 1 @@ -465,7 +466,11 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *no { char usersString[20]; sprintf(usersString, "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal()); +#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) + display->drawFastImage(x, y + 3, 8, 8, imgUser); +#else display->drawFastImage(x, y, 8, 8, imgUser); +#endif display->drawString(x + 10, y - 2, usersString); display->drawString(x + 11, y - 2, usersString); } @@ -513,19 +518,20 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus } //Draw status when gps is disabled by PMU -static void drawGPSpowerstat(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps){ -String displayLine = ""; -displayLine = "GPS disabled"; -int16_t xPos = display->getStringWidth(displayLine); - #ifdef HAS_PMU +static void drawGPSpowerstat(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps) +{ +#ifdef HAS_PMU + String displayLine = "GPS disabled"; + int16_t xPos = display->getStringWidth(displayLine); + if (!config.position.gps_enabled){ - display->drawString(x + xPos, y, displayLine); - #ifdef GPS_POWER_TOGGLE - display->drawString(x + xPos, y - 2 + FONT_HEIGHT_SMALL, " by button"); - #endif + display->drawString(x + xPos, y, displayLine); +#ifdef GPS_POWER_TOGGLE + display->drawString(x + xPos, y - 2 + FONT_HEIGHT_SMALL, " by button"); +#endif //display->drawString(x + xPos, y + 2, displayLine); } - #endif +#endif } static void drawGPSAltitude(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps) @@ -860,29 +866,6 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ drawColumns(display, x, y, fields); } -#if 0 -void _screen_header() -{ - if (!disp) - return; - - // Message count - //snprintf(buffer, sizeof(buffer), "#%03d", ttn_get_count() % 1000); - //display->setTextAlignment(TEXT_ALIGN_LEFT); - //display->drawString(0, 2, buffer); - - // Datetime - display->setTextAlignment(TEXT_ALIGN_CENTER); - display->drawString(display->getWidth()/2, 2, gps.getTimeStr()); - - // Satellite count - display->setTextAlignment(TEXT_ALIGN_RIGHT); - char buffer[10]; - display->drawString(display->getWidth() - SATELLITE_IMAGE_WIDTH - 4, 2, itoa(gps.satellites.value(), buffer, 10)); - display->drawXbm(display->getWidth() - SATELLITE_IMAGE_WIDTH, 0, SATELLITE_IMAGE_WIDTH, SATELLITE_IMAGE_HEIGHT, SATELLITE_IMAGE); -} -#endif - // #ifdef RAK4630 // Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl), // dispdev_oled(address, sda, scl), ui(&dispdev) @@ -1414,8 +1397,32 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 display->setColor(WHITE); // Draw the channel name display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr); - // Draw our hardware ID to assist with bluetooth pairing - display->drawFastImage(x + SCREEN_WIDTH - (10) - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8, imgInfo); + // Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo + if (moduleConfig.store_forward.enabled) { + if (millis() - storeForwardModule->lastHeartbeat > (storeForwardModule->heartbeatInterval * 1200)) { //no heartbeat, overlap a bit +#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) + display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8, imgQuestionL1); + display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8, imgQuestionL2); +#else + display->drawFastImage(x + SCREEN_WIDTH - 10 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8, imgQuestion); +#endif + } else { +#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) + display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 16, 8, imgSFL1); + display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 16, 8, imgSFL2); +#else + display->drawFastImage(x + SCREEN_WIDTH - 13 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 11, 8, imgSF); +#endif + } + } else { +#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) + display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8, imgInfoL1); + display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8, imgInfoL2); +#else + display->drawFastImage(x + SCREEN_WIDTH - 10 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8, imgInfo); +#endif + } + display->drawString(x + SCREEN_WIDTH - display->getStringWidth(ourId), y + FONT_HEIGHT_SMALL, ourId); // Draw any log messages diff --git a/src/graphics/images.h b/src/graphics/images.h index 4680b9475..9bf66a3a3 100644 --- a/src/graphics/images.h +++ b/src/graphics/images.h @@ -12,6 +12,18 @@ const uint8_t imgPower[] PROGMEM = { 0x40, 0x40, 0x40, 0x58, 0x48, 0x08, const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C }; const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF }; const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF }; -const uint8_t imgInfo[] PROGMEM = { 0xFF, 0x81, 0x81, 0xB5, 0xB5, 0x81, 0x81, 0xFF }; + +#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) +const uint8_t imgQuestionL1[] PROGMEM = { 0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff }; +const uint8_t imgQuestionL2[] PROGMEM = { 0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f }; +const uint8_t imgInfoL1[] PROGMEM = { 0xff, 0x01, 0x01, 0x01, 0x1e, 0x7f, 0x1e, 0x01, 0x01, 0x01, 0x01, 0xff }; +const uint8_t imgInfoL2[] PROGMEM = { 0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f }; +const uint8_t imgSFL1[] PROGMEM = { 0xb6, 0x8f, 0x19, 0x11, 0x31, 0xe3, 0xc2, 0x01, 0x01, 0xf9, 0xf9, 0x89, 0x89, 0x89, 0x09, 0xeb}; +const uint8_t imgSFL2[] PROGMEM = { 0x0e, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x00, 0x0f, 0x0f, 0x00, 0x08, 0x08, 0x08, 0x0f}; +#else +const uint8_t imgInfo[] PROGMEM = { 0xff, 0x81, 0x00, 0xfb, 0xfb, 0x00, 0x81, 0xff }; +const uint8_t imgQuestion[] PROGMEM = { 0xbf, 0x41, 0xc0, 0x8b, 0xdb, 0x70, 0xa1, 0xdf }; +const uint8_t imgSF[] PROGMEM = { 0xd2, 0xb7, 0xad, 0xbb, 0x92, 0x01, 0xfd, 0xfd, 0x15, 0x85, 0xf5}; +#endif #include "img/icon.xbm" diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 2faff2aae..1e40b8d92 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -162,6 +162,7 @@ void NodeDB::installDefaultConfig() config.has_network = true; config.has_bluetooth = true; config.lora.tx_enabled = true; // FIXME: maybe false in the future, and setting region to enable it. (unset region forces it off) + config.lora.override_duty_cycle = false; config.lora.region = Config_LoRaConfig_RegionCode_UNSET; config.lora.modem_preset = Config_LoRaConfig_ModemPreset_LONG_FAST; config.lora.hop_limit = HOP_RELIABLE; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 6b6a03a3a..5ce26f49a 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -2,6 +2,7 @@ #include "Channels.h" #include "CryptoEngine.h" #include "NodeDB.h" +#include "MeshRadio.h" #include "RTC.h" #include "configuration.h" #include "main.h" @@ -187,6 +188,18 @@ ErrorCode Router::send(MeshPacket *p) { assert(p->to != nodeDB.getNodeNum()); // should have already been handled by sendLocal + // Abort sending if we are violating the duty cycle + if (!config.lora.override_duty_cycle && myRegion->dutyCycle != 100) { + float hourlyTxPercent = airTime->utilizationTXPercent(); + if (hourlyTxPercent > myRegion->dutyCycle) { + uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle); + DEBUG_MSG("WARNING: Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes); + Routing_Error err = Routing_Error_DUTY_CYCLE_LIMIT; + abortSendAndNak(err, p); + return err; + } + } + // PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0; // assert(!nakId); // I don't think we ever send 0hop naks over the wire (other than to the phone), test that assumption with // assert diff --git a/src/mesh/http/WiFiAPClient.cpp b/src/mesh/http/WiFiAPClient.cpp index bb4542468..731197e60 100644 --- a/src/mesh/http/WiFiAPClient.cpp +++ b/src/mesh/http/WiFiAPClient.cpp @@ -1,7 +1,7 @@ -#include "mesh/http/WiFiAPClient.h" #include "NodeDB.h" #include "RTC.h" #include "concurrency/Periodic.h" +#include "mesh/http/WiFiAPClient.h" #include "configuration.h" #include "main.h" #include "mesh/http/WebServer.h" @@ -37,9 +37,9 @@ bool APStartupComplete = 0; unsigned long lastrun_ntp = 0; -static bool needReconnect = true; // If we create our reconnector, run it once at the beginning +bool needReconnect = true; // If we create our reconnector, run it once at the beginning -static Periodic *wifiReconnect; +Periodic *wifiReconnect; static int32_t reconnectWiFi() { @@ -56,29 +56,15 @@ static int32_t reconnectWiFi() // Make sure we clear old connection credentials WiFi.disconnect(false, true); - DEBUG_MSG("... Reconnecting to WiFi access point %s\n",wifiName); - - int n = WiFi.scanNetworks(); - - if (n > 0) { - for (int i = 0; i < n; ++i) { - DEBUG_MSG("Found WiFi network %s, signal strength %d\n", WiFi.SSID(i).c_str(), WiFi.RSSI(i)); - yield(); - } - WiFi.mode(WIFI_MODE_STA); - WiFi.begin(wifiName, wifiPsw); - } else { - DEBUG_MSG("No networks found during site survey. Rebooting MCU...\n"); - screen->startRebootScreen(); - rebootAtMsec = millis() + 5000; - } - + DEBUG_MSG("Reconnecting to WiFi access point %s\n",wifiName); + WiFi.mode(WIFI_MODE_STA); + WiFi.begin(wifiName, wifiPsw); } #ifndef DISABLE_NTP if (WiFi.isConnected() && (((millis() - lastrun_ntp) > 43200000) || (lastrun_ntp == 0))) { // every 12 hours - DEBUG_MSG("Updating NTP time\n"); + DEBUG_MSG("Updating NTP time from %s\n",config.network.ntp_server); if (timeClient.update()) { DEBUG_MSG("NTP Request Success - Setting RTCQualityNTP if needed\n"); @@ -129,7 +115,7 @@ static void onNetworkConnected() { if (!APStartupComplete) { // Start web server - DEBUG_MSG("... Starting network services\n"); + DEBUG_MSG("Starting network services\n"); // start mdns if (!MDNS.begin("Meshtastic")) { @@ -168,6 +154,8 @@ bool initWifi() createSSLCert(); + esp_wifi_set_storage(WIFI_STORAGE_RAM); // Disable flash storage for WiFi credentials + if (!*wifiPsw) // Treat empty password as no password wifiPsw = NULL; @@ -194,7 +182,7 @@ bool initWifi() WiFi.onEvent( [](WiFiEvent_t event, WiFiEventInfo_t info) { - Serial.print("\nWiFi lost connection. Reason: "); + Serial.print("WiFi lost connection. Reason: "); Serial.println(info.wifi_sta_disconnected.reason); /* @@ -221,91 +209,137 @@ bool initWifi() // Called by the Espressif SDK to static void WiFiEvent(WiFiEvent_t event) { - DEBUG_MSG("************ [WiFi-event] event: %d ************\n", event); + DEBUG_MSG("WiFi-Event %d: ", event); switch (event) { - case SYSTEM_EVENT_WIFI_READY: + case ARDUINO_EVENT_WIFI_READY: DEBUG_MSG("WiFi interface ready\n"); break; - case SYSTEM_EVENT_SCAN_DONE: + case ARDUINO_EVENT_WIFI_SCAN_DONE: DEBUG_MSG("Completed scan for access points\n"); break; - case SYSTEM_EVENT_STA_START: + case ARDUINO_EVENT_WIFI_STA_START: DEBUG_MSG("WiFi station started\n"); break; - case SYSTEM_EVENT_STA_STOP: + case ARDUINO_EVENT_WIFI_STA_STOP: DEBUG_MSG("WiFi station stopped\n"); break; - case SYSTEM_EVENT_STA_CONNECTED: + case ARDUINO_EVENT_WIFI_STA_CONNECTED: DEBUG_MSG("Connected to access point\n"); break; - case SYSTEM_EVENT_STA_DISCONNECTED: + case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: DEBUG_MSG("Disconnected from WiFi access point\n"); WiFi.disconnect(false, true); needReconnect = true; wifiReconnect->setIntervalFromNow(1000); break; - case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: + case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: DEBUG_MSG("Authentication mode of access point has changed\n"); break; - case SYSTEM_EVENT_STA_GOT_IP: + case ARDUINO_EVENT_WIFI_STA_GOT_IP: DEBUG_MSG("Obtained IP address: "); Serial.println(WiFi.localIP()); onNetworkConnected(); break; - case SYSTEM_EVENT_STA_LOST_IP: + case ARDUINO_EVENT_WIFI_STA_GOT_IP6: + DEBUG_MSG("Obtained IP6 address: "); + Serial.println(WiFi.localIPv6()); + break; + case ARDUINO_EVENT_WIFI_STA_LOST_IP: DEBUG_MSG("Lost IP address and IP address is reset to 0\n"); WiFi.disconnect(false, true); needReconnect = true; wifiReconnect->setIntervalFromNow(1000); break; - case SYSTEM_EVENT_STA_WPS_ER_SUCCESS: + case ARDUINO_EVENT_WPS_ER_SUCCESS: DEBUG_MSG("WiFi Protected Setup (WPS): succeeded in enrollee mode\n"); break; - case SYSTEM_EVENT_STA_WPS_ER_FAILED: + case ARDUINO_EVENT_WPS_ER_FAILED: DEBUG_MSG("WiFi Protected Setup (WPS): failed in enrollee mode\n"); break; - case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT: + case ARDUINO_EVENT_WPS_ER_TIMEOUT: DEBUG_MSG("WiFi Protected Setup (WPS): timeout in enrollee mode\n"); break; - case SYSTEM_EVENT_STA_WPS_ER_PIN: + case ARDUINO_EVENT_WPS_ER_PIN: DEBUG_MSG("WiFi Protected Setup (WPS): pin code in enrollee mode\n"); break; - case SYSTEM_EVENT_AP_START: + case ARDUINO_EVENT_WPS_ER_PBC_OVERLAP: + DEBUG_MSG("WiFi Protected Setup (WPS): push button overlap in enrollee mode\n"); + break; + case ARDUINO_EVENT_WIFI_AP_START: DEBUG_MSG("WiFi access point started\n"); break; - case SYSTEM_EVENT_AP_STOP: + case ARDUINO_EVENT_WIFI_AP_STOP: DEBUG_MSG("WiFi access point stopped\n"); break; - case SYSTEM_EVENT_AP_STACONNECTED: + case ARDUINO_EVENT_WIFI_AP_STACONNECTED: DEBUG_MSG("Client connected\n"); break; - case SYSTEM_EVENT_AP_STADISCONNECTED: + case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED: DEBUG_MSG("Client disconnected\n"); break; - case SYSTEM_EVENT_AP_STAIPASSIGNED: + case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED: DEBUG_MSG("Assigned IP address to client\n"); break; - case SYSTEM_EVENT_AP_PROBEREQRECVED: + case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED: DEBUG_MSG("Received probe request\n"); break; - case SYSTEM_EVENT_GOT_IP6: + case ARDUINO_EVENT_WIFI_AP_GOT_IP6: DEBUG_MSG("IPv6 is preferred\n"); break; - case SYSTEM_EVENT_ETH_START: + case ARDUINO_EVENT_WIFI_FTM_REPORT: + DEBUG_MSG("Fast Transition Management report\n"); + break; + case ARDUINO_EVENT_ETH_START: DEBUG_MSG("Ethernet started\n"); break; - case SYSTEM_EVENT_ETH_STOP: + case ARDUINO_EVENT_ETH_STOP: DEBUG_MSG("Ethernet stopped\n"); break; - case SYSTEM_EVENT_ETH_CONNECTED: + case ARDUINO_EVENT_ETH_CONNECTED: DEBUG_MSG("Ethernet connected\n"); break; - case SYSTEM_EVENT_ETH_DISCONNECTED: + case ARDUINO_EVENT_ETH_DISCONNECTED: DEBUG_MSG("Ethernet disconnected\n"); break; - case SYSTEM_EVENT_ETH_GOT_IP: - DEBUG_MSG("Obtained IP address (SYSTEM_EVENT_ETH_GOT_IP)\n"); + case ARDUINO_EVENT_ETH_GOT_IP: + DEBUG_MSG("Obtained IP address (ARDUINO_EVENT_ETH_GOT_IP)\n"); + break; + case ARDUINO_EVENT_ETH_GOT_IP6: + DEBUG_MSG("Obtained IP6 address (ARDUINO_EVENT_ETH_GOT_IP6)\n"); + break; + case ARDUINO_EVENT_SC_SCAN_DONE: + DEBUG_MSG("SmartConfig: Scan done\n"); + break; + case ARDUINO_EVENT_SC_FOUND_CHANNEL: + DEBUG_MSG("SmartConfig: Found channel\n"); + break; + case ARDUINO_EVENT_SC_GOT_SSID_PSWD: + DEBUG_MSG("SmartConfig: Got SSID and password\n"); + break; + case ARDUINO_EVENT_SC_SEND_ACK_DONE: + DEBUG_MSG("SmartConfig: Send ACK done\n"); + break; + case ARDUINO_EVENT_PROV_INIT: + DEBUG_MSG("Provisioning: Init\n"); + break; + case ARDUINO_EVENT_PROV_DEINIT: + DEBUG_MSG("Provisioning: Stopped\n"); + break; + case ARDUINO_EVENT_PROV_START: + DEBUG_MSG("Provisioning: Started\n"); + break; + case ARDUINO_EVENT_PROV_END: + DEBUG_MSG("Provisioning: End\n"); + break; + case ARDUINO_EVENT_PROV_CRED_RECV: + DEBUG_MSG("Provisioning: Credentials received\n"); + break; + case ARDUINO_EVENT_PROV_CRED_FAIL: + DEBUG_MSG("Provisioning: Credentials failed\n"); + break; + case ARDUINO_EVENT_PROV_CRED_SUCCESS: + DEBUG_MSG("Provisioning: Credentials success\n"); break; default: break; diff --git a/src/mesh/http/WiFiAPClient.h b/src/mesh/http/WiFiAPClient.h index 9729c24b5..a11330ad0 100644 --- a/src/mesh/http/WiFiAPClient.h +++ b/src/mesh/http/WiFiAPClient.h @@ -1,6 +1,7 @@ #pragma once #include "configuration.h" +#include "concurrency/Periodic.h" #include #include @@ -8,6 +9,9 @@ #include #endif +extern bool needReconnect; +extern concurrency::Periodic *wifiReconnect; + /// @return true if wifi is now in use bool initWifi(); diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 2639e0594..0b617adea 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -195,6 +195,7 @@ bool AdminModule::handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *r) void AdminModule::handleSetOwner(const User &o) { int changed = 0; + bool licensed_changed = false; if (*o.long_name) { changed |= strcmp(owner.long_name, o.long_name); @@ -210,12 +211,14 @@ void AdminModule::handleSetOwner(const User &o) } if (owner.is_licensed != o.is_licensed) { changed = 1; + licensed_changed = true; owner.is_licensed = o.is_licensed; + config.lora.override_duty_cycle = owner.is_licensed; // override duty cycle for licensed operators } if (changed) { // If nothing really changed, don't broadcast on the network or write to flash service.reloadOwner(!hasOpenEditTransaction); - saveChanges(SEGMENT_DEVICESTATE); + licensed_changed ? saveChanges(SEGMENT_CONFIG | SEGMENT_DEVICESTATE) : saveChanges(SEGMENT_DEVICESTATE); } } diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index e7b34b9a7..d148768ad 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -451,11 +451,15 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st if (this->destSelect) { display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); display->setColor(BLACK); + display->drawStringf(1 + x, 0 + y, buffer, "To: %s", cannedMessageModule->getNodeName(this->dest)); } display->drawStringf(0 + x, 0 + y, buffer, "To: %s", cannedMessageModule->getNodeName(this->dest)); // used chars right aligned sprintf(buffer, "%d left", Constants_DATA_PAYLOAD_LEN - this->freetext.length()); display->drawString(x + display->getWidth() - display->getStringWidth(buffer), y + 0, buffer); + if (this->destSelect) { + display->drawString(x + display->getWidth() - display->getStringWidth(buffer) - 1, y + 0, buffer); + } display->setColor(WHITE); display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), cannedMessageModule->drawWithCursor(cannedMessageModule->freetext, cannedMessageModule->cursor)); } else { diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 17e5cf4fa..e7132fcc4 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -71,27 +71,22 @@ void run_codec2(void* parameter) // 4 bytes of header in each frame hex c0 de c2 plus the bitrate memcpy(audioModule->tx_encode_frame,&audioModule->tx_header,sizeof(audioModule->tx_header)); + DEBUG_MSG("Starting codec2 task\n"); + while (true) { uint32_t tcount = ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(10000)); if (tcount != 0) { if (audioModule->radio_state == RadioState::tx) { - - // Apply the TX filter for (int i = 0; i < audioModule->adc_buffer_size; i++) audioModule->speech[i] = (int16_t)hp_filter.Update((float)audioModule->speech[i]); - // Encode the audio codec2_encode(audioModule->codec2, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index, audioModule->speech); - - //increment the pointer where the encoded frame must be saved audioModule->tx_encode_frame_index += audioModule->encode_codec_size; - //If it this is reached we have a ready trasnmission frame if (audioModule->tx_encode_frame_index == (audioModule->encode_frame_size + sizeof(audioModule->tx_header))) { - //Transmit it - DEBUG_MSG("♪♫♪ Sending %d codec2 bytes\n", audioModule->encode_frame_size); + DEBUG_MSG("Sending %d codec2 bytes\n", audioModule->encode_frame_size); audioModule->sendPayload(); audioModule->tx_encode_frame_index = sizeof(audioModule->tx_header); } @@ -99,10 +94,8 @@ void run_codec2(void* parameter) if (audioModule->radio_state == RadioState::rx) { size_t bytesOut = 0; if (memcmp(audioModule->rx_encode_frame, &audioModule->tx_header, sizeof(audioModule->tx_header)) == 0) { - // Make a cycle to get each codec2 frame from the received frame for (int i = 4; i < audioModule->rx_encode_frame_index; i += audioModule->encode_codec_size) { - //Decode the codec2 frame codec2_decode(audioModule->codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i); i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500)); } @@ -112,10 +105,8 @@ void run_codec2(void* parameter) codec2_set_lpc_post_filter(tmp_codec2, 1, 0, 0.8, 0.2); int tmp_encode_codec_size = (codec2_bits_per_frame(tmp_codec2) + 7) / 8; int tmp_adc_buffer_size = codec2_samples_per_frame(tmp_codec2); - // Make a cycle to get each codec2 frame from the received frame for (int i = 4; i < audioModule->rx_encode_frame_index; i += tmp_encode_codec_size) { - //Decode the codec2 frame codec2_decode(tmp_codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i); i2s_write(I2S_PORT, &audioModule->output_buffer, tmp_adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500)); } @@ -128,8 +119,15 @@ void run_codec2(void* parameter) AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") { + // moduleConfig.audio.codec2_enabled = true; + // moduleConfig.audio.i2s_ws = 13; + // moduleConfig.audio.i2s_sd = 15; + // moduleConfig.audio.i2s_din = 22; + // moduleConfig.audio.i2s_sck = 14; + // moduleConfig.audio.ptt_pin = 39; + if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { - DEBUG_MSG("♪♫♪ Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); + DEBUG_MSG("Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); codec2 = codec2_create((moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); memcpy(tx_header.magic,c2_magic,sizeof(c2_magic)); tx_header.mode = (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1; @@ -141,7 +139,7 @@ AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), DEBUG_MSG(" using %d frames of %d bytes for a total payload length of %d bytes\n", encode_frame_num, encode_codec_size, encode_frame_size); xTaskCreate(&run_codec2, "codec2_task", 30000, NULL, 5, &codec2HandlerTask); } else { - DEBUG_MSG("♪♫♪ Codec2 disabled (AudioModule %d, Region %s, permitted %d)\n", moduleConfig.audio.codec2_enabled, myRegion->name, myRegion->audioPermitted); + DEBUG_MSG("Codec2 disabled (AudioModule %d, Region %s, permitted %d)\n", moduleConfig.audio.codec2_enabled, myRegion->name, myRegion->audioPermitted); } } @@ -175,7 +173,7 @@ int32_t AudioModule::runOnce() esp_err_t res; if (firstTime) { // Set up I2S Processor configuration. This will produce 16bit samples at 8 kHz instead of 12 from the ADC - DEBUG_MSG("♪♫♪ Initializing I2S SD: %d DIN: %d WS: %d SCK:%d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck); + DEBUG_MSG("Initializing I2S SD: %d DIN: %d WS: %d SCK: %d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck); i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | (moduleConfig.audio.i2s_sd ? I2S_MODE_RX : 0) | (moduleConfig.audio.i2s_din ? I2S_MODE_TX : 0)), .sample_rate = 8000, @@ -191,7 +189,7 @@ int32_t AudioModule::runOnce() }; res = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to install I2S driver: %d\n", res); + DEBUG_MSG("Failed to install I2S driver: %d\n", res); const i2s_pin_config_t pin_config = { .bck_io_num = moduleConfig.audio.i2s_sck, @@ -201,16 +199,16 @@ int32_t AudioModule::runOnce() }; res = i2s_set_pin(I2S_PORT, &pin_config); if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to set I2S pin config: %d\n", res); + DEBUG_MSG("Failed to set I2S pin config: %d\n", res); res = i2s_start(I2S_PORT); if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to start I2S: %d\n", res); + DEBUG_MSG("Failed to start I2S: %d\n", res); radio_state = RadioState::rx; // Configure PTT input - DEBUG_MSG("♪♫♪ Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); + DEBUG_MSG("Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); pinMode(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN, INPUT); firstTime = false; @@ -219,19 +217,19 @@ int32_t AudioModule::runOnce() // Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive. if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) { if (radio_state == RadioState::rx) { - DEBUG_MSG("♪♫♪ PTT pressed, switching to TX\n"); + DEBUG_MSG("PTT pressed, switching to TX\n"); radio_state = RadioState::tx; e.frameChanged = true; this->notifyObservers(&e); } } else { if (radio_state == RadioState::tx) { + DEBUG_MSG("PTT released, switching to RX\n"); if (tx_encode_frame_index > sizeof(tx_header)) { // Send the incomplete frame - DEBUG_MSG("♪♫♪ Sending %d codec2 bytes (incomplete)\n", tx_encode_frame_index); + DEBUG_MSG("Sending %d codec2 bytes (incomplete)\n", tx_encode_frame_index); sendPayload(); } - DEBUG_MSG("♪♫♪ PTT released, switching to RX\n"); tx_encode_frame_index = sizeof(tx_header); radio_state = RadioState::rx; e.frameChanged = true; @@ -260,7 +258,7 @@ int32_t AudioModule::runOnce() } return 100; } else { - DEBUG_MSG("♪♫♪ Audio Module Disabled\n"); + DEBUG_MSG("Audio Module Disabled\n"); return INT32_MAX; } @@ -268,7 +266,7 @@ int32_t AudioModule::runOnce() MeshPacket *AudioModule::allocReply() { - auto reply = allocDataPacket(); // Allocate a packet for sending + auto reply = allocDataPacket(); return reply; } @@ -286,7 +284,7 @@ void AudioModule::sendPayload(NodeNum dest, bool wantReplies) p->to = dest; p->decoded.want_response = wantReplies; - p->want_ack = false; // Audio is shoot&forget. TODO: Is this really suppressing retransmissions? + p->want_ack = false; // Audio is shoot&forget. No need to wait for ACKs. p->priority = MeshPacket_Priority_MAX; // Audio is important, because realtime p->decoded.payload.size = tx_encode_frame_index; diff --git a/src/power.h b/src/power.h index 64043eb86..b370e0248 100644 --- a/src/power.h +++ b/src/power.h @@ -40,6 +40,9 @@ class Power : private concurrency::OSThread private: uint8_t low_voltage_counter; +#ifdef DEBUG_HEAP + uint32_t lastheap; +#endif }; extern Power *power;