diff --git a/.github/workflows/update_protobufs.yml b/.github/workflows/update_protobufs.yml
index 3b32c578c..6944d827e 100644
--- a/.github/workflows/update_protobufs.yml
+++ b/.github/workflows/update_protobufs.yml
@@ -17,9 +17,9 @@ jobs:
- name: Download nanopb
run: |
- wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.6-linux-x86.tar.gz
- tar xvzf nanopb-0.4.6-linux-x86.tar.gz
- mv nanopb-0.4.6-linux-x86 nanopb-0.4.6
+ wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.7-linux-x86.tar.gz
+ tar xvzf nanopb-0.4.7-linux-x86.tar.gz
+ mv nanopb-0.4.7-linux-x86 nanopb-0.4.7
- name: Re-generate protocol buffers
run: |
diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini
index 730b78942..0011cc39f 100644
--- a/arch/esp32/esp32.ini
+++ b/arch/esp32/esp32.ini
@@ -26,6 +26,7 @@ build_flags =
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
+ -DDEBUG_HEAP
lib_deps =
${arduino_base.lib_deps}
@@ -33,7 +34,8 @@ lib_deps =
${environmental_base.lib_deps}
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
h2zero/NimBLE-Arduino@^1.4.0
- https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
+ https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
+ caveman99/ESP32 Codec2@^1.0.1
lib_ignore =
segger_rtt
diff --git a/arch/esp32/esp32s2.ini b/arch/esp32/esp32s2.ini
new file mode 100644
index 000000000..ca4f576d6
--- /dev/null
+++ b/arch/esp32/esp32s2.ini
@@ -0,0 +1,47 @@
+[esp32s2_base]
+extends = arduino_base
+platform = platformio/espressif32@^5.2.0
+build_src_filter =
+ ${arduino_base.build_src_filter} - - - - -
+upload_speed = 961200
+monitor_speed = 115200
+debug_init_break = tbreak setup
+monitor_filters = esp32_exception_decoder
+board_build.filesystem = littlefs
+
+# Remove -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL for low level BLE logging.
+# See library directory for BLE logging possible values: .pio/libdeps/tbeam/NimBLE-Arduino/src/log_common/log_common.h
+# This overrides the BLE logging default of LOG_LEVEL_INFO (1) from: .pio/libdeps/tbeam/NimBLE-Arduino/src/esp_nimble_cfg.h
+build_flags =
+ ${arduino_base.build_flags}
+ -Wall
+ -Wextra
+ -Isrc/platform/esp32
+ -std=c++11
+ -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG
+ -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
+ -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL
+ -DAXP_DEBUG_PORT=Serial
+ -DCONFIG_BT_NIMBLE_ENABLED
+ -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
+ -DCONFIG_BT_NIMBLE_MAX_CCCDS=20
+ -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
+ -DHAS_BLUETOOTH=0
+ -DDEBUG_HEAP
+
+lib_deps =
+ ${arduino_base.lib_deps}
+ ${networking_base.lib_deps}
+ ${environmental_base.lib_deps}
+ https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
+ https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
+ caveman99/ESP32 Codec2@^1.0.1
+
+lib_ignore =
+ segger_rtt
+ ESP32 BLE Arduino
+
+; customize the partition table
+; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
+board_build.partitions = partition-table.csv
+
diff --git a/arch/esp32/esp32s3.ini b/arch/esp32/esp32s3.ini
index 0c2d7d8f1..b276ceff9 100644
--- a/arch/esp32/esp32s3.ini
+++ b/arch/esp32/esp32s3.ini
@@ -23,9 +23,10 @@ build_flags =
-DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL
-DAXP_DEBUG_PORT=Serial
-DCONFIG_BT_NIMBLE_ENABLED
- -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
+ -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
- -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
+ -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
+ -DDEBUG_HEAP
lib_deps =
${arduino_base.lib_deps}
@@ -34,6 +35,7 @@ lib_deps =
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
h2zero/NimBLE-Arduino@^1.4.0
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
+ caveman99/ESP32 Codec2@^1.0.1
lib_ignore =
segger_rtt
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/ButtonThread.h b/src/ButtonThread.h
index 088642099..0e9830f3f 100644
--- a/src/ButtonThread.h
+++ b/src/ButtonThread.h
@@ -51,6 +51,7 @@ class ButtonThread : public concurrency::OSThread
pinMode(BUTTON_PIN, INPUT_PULLUP_SENSE);
#endif
userButton.attachClick(userButtonPressed);
+ userButton.setClickTicks(300);
userButton.attachDuringLongPress(userButtonPressedLong);
userButton.attachDoubleClick(userButtonDoublePressed);
userButton.attachMultiClick(userButtonMultiPressed);
@@ -159,9 +160,21 @@ class ButtonThread : public concurrency::OSThread
static void userButtonDoublePressed()
{
-#if defined(USE_EINK) && defined(PIN_EINK_EN)
+ #if defined(USE_EINK) && defined(PIN_EINK_EN)
digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW);
-#endif
+ #endif
+ #if defined(GPS_POWER_TOGGLE)
+ if(config.position.gps_enabled)
+ {
+ DEBUG_MSG("Flag set to false for gps power\n");
+ }
+ else
+ {
+ DEBUG_MSG("Flag set to true to restore power\n");
+ }
+ config.position.gps_enabled = !(config.position.gps_enabled);
+ doGPSpowersave(config.position.gps_enabled);
+ #endif
}
static void userButtonMultiPressed()
diff --git a/src/GPSStatus.h b/src/GPSStatus.h
index 35a0b11f2..ef97c59b7 100644
--- a/src/GPSStatus.h
+++ b/src/GPSStatus.h
@@ -20,16 +20,19 @@ class GPSStatus : public Status
bool hasLock = false; // default to false, until we complete our first read
bool isConnected = false; // Do we have a GPS we are talking to
+ bool isPowerSaving = false; //Are we in power saving state
+
Position p = Position_init_default;
public:
GPSStatus() { statusType = STATUS_TYPE_GPS; }
// preferred method
- GPSStatus(bool hasLock, bool isConnected, const Position &pos) : Status()
+ GPSStatus(bool hasLock, bool isConnected, bool isPowerSaving, const Position &pos) : Status()
{
this->hasLock = hasLock;
this->isConnected = isConnected;
+ this->isPowerSaving = isPowerSaving;
// all-in-one struct copy
this->p = pos;
@@ -44,6 +47,8 @@ class GPSStatus : public Status
bool getIsConnected() const { return isConnected; }
+ bool getIsPowerSaving() const { return isPowerSaving;}
+
int32_t getLatitude() const
{
if (config.position.fixed_position) {
@@ -94,7 +99,7 @@ class GPSStatus : public Status
#ifdef GPS_EXTRAVERBOSE
DEBUG_MSG("GPSStatus.match() new pos@%x to old pos@%x\n", newStatus->p.pos_timestamp, p.pos_timestamp);
#endif
- return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected ||
+ return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected || newStatus->isPowerSaving !=isPowerSaving ||
newStatus->p.latitude_i != p.latitude_i || newStatus->p.longitude_i != p.longitude_i ||
newStatus->p.altitude != p.altitude || newStatus->p.altitude_hae != p.altitude_hae ||
newStatus->p.PDOP != p.PDOP || newStatus->p.ground_track != p.ground_track ||
diff --git a/src/Power.cpp b/src/Power.cpp
index ec86adc94..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()
@@ -283,6 +286,12 @@ void Power::readPowerStatus()
DEBUG_MSG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(),
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
newStatus.notifyObservers(&powerStatus2);
+#ifdef DEBUG_HEAP
+ if (lastheap != ESP.getFreeHeap()){
+ DEBUG_MSG("Heap status: %d/%d bytes free (%d), running %d threads\n", ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getFreeHeap() - lastheap , concurrency::mainController.size(false));
+ lastheap = ESP.getFreeHeap();
+ }
+#endif
// If we have a battery at all and it is less than 10% full, force deep sleep if we have more than 3 low readings in a row
// Supect fluctuating voltage on the RAK4631 to force it to deep sleep even if battery is at 85% after only a few days
diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp
index 647167242..e60056ccd 100644
--- a/src/PowerFSM.cpp
+++ b/src/PowerFSM.cpp
@@ -326,10 +326,10 @@ void PowerFSM_setup()
powerFSM.add_timed_transition(&stateON, &stateDARK, getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL, "Screen-on timeout");
+#ifdef ARCH_ESP32
// On most boards we use light-sleep to be our main state, but on NRF52 we just stay in DARK
State *lowPowerState = &stateLS;
-#ifdef ARCH_ESP32
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
// See: https://github.com/meshtastic/firmware/issues/1071
@@ -340,9 +340,6 @@ void PowerFSM_setup()
if (config.power.sds_secs != UINT32_MAX)
powerFSM.add_timed_transition(lowPowerState, &stateSDS, getConfiguredOrDefaultMs(config.power.sds_secs), NULL, "mesh timeout");
-
-#elif defined (ARCH_NRF52)
- lowPowerState = &stateDARK;
#endif
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/gps/GPS.cpp b/src/gps/GPS.cpp
index 54dc59cbb..7e860cdd0 100644
--- a/src/gps/GPS.cpp
+++ b/src/gps/GPS.cpp
@@ -270,21 +270,30 @@ bool GPS::setup()
pinMode(PIN_GPS_EN, OUTPUT);
#endif
+#ifdef HAS_PMU
+if(config.position.gps_enabled){
+ setGPSPower(true);
+}
+#endif
+
#ifdef PIN_GPS_RESET
digitalWrite(PIN_GPS_RESET, 1); // assert for 10ms
pinMode(PIN_GPS_RESET, OUTPUT);
delay(10);
digitalWrite(PIN_GPS_RESET, 0);
#endif
-
setAwake(true); // Wake GPS power before doing any init
bool ok = setupGPS();
if (ok) {
notifySleepObserver.observe(¬ifySleep);
notifyDeepSleepObserver.observe(¬ifyDeepSleep);
+ notifyGPSSleepObserver.observe(¬ifyGPSSleep);
+ }
+ if (config.position.gps_enabled==false) {
+ setAwake(false);
+ doGPSpowersave(false);
}
-
return ok;
}
@@ -293,6 +302,7 @@ GPS::~GPS()
// we really should unregister our sleep observer
notifySleepObserver.unobserve(¬ifySleep);
notifyDeepSleepObserver.unobserve(¬ifyDeepSleep);
+ notifyGPSSleepObserver.observe(¬ifyGPSSleep);
}
bool GPS::hasLock()
@@ -405,7 +415,7 @@ void GPS::publishUpdate()
DEBUG_MSG("publishing pos@%x:2, hasVal=%d, GPSlock=%d\n", p.timestamp, hasValidLocation, hasLock());
// Notify any status instances that are observing us
- const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasValidLocation, isConnected(), p);
+ const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasValidLocation, isConnected(), isPowerSaving(), p);
newStatus.notifyObservers(&status);
}
}
@@ -416,7 +426,7 @@ int32_t GPS::runOnce()
// if we have received valid NMEA claim we are connected
setConnected();
} else {
- if(gnssModel == GNSS_MODEL_UBLOX){
+ if((config.position.gps_enabled == 1) && (gnssModel == GNSS_MODEL_UBLOX)){
// reset the GPS on next bootup
if(devicestate.did_gps_reset && (millis() > 60000) && !hasFlow()) {
DEBUG_MSG("GPS is not communicating, trying factory reset on next bootup.\n");
@@ -518,6 +528,7 @@ int GPS::prepareDeepSleep(void *unused)
DEBUG_MSG("GPS deep sleep!\n");
// For deep sleep we also want abandon any lock attempts (because we want minimum power)
+ getSleepTime();
setAwake(false);
return 0;
@@ -653,6 +664,11 @@ GPS *createGps()
return new_gps;
}
}
+ else{
+ GPS *new_gps = new NMEAGPS();
+ new_gps->setup();
+ return new_gps;
+ }
return nullptr;
#endif
}
diff --git a/src/gps/GPS.h b/src/gps/GPS.h
index 8f04b6793..5d24268d7 100644
--- a/src/gps/GPS.h
+++ b/src/gps/GPS.h
@@ -49,6 +49,7 @@ class GPS : private concurrency::OSThread
CallbackObserver notifySleepObserver = CallbackObserver(this, &GPS::prepareSleep);
CallbackObserver notifyDeepSleepObserver = CallbackObserver(this, &GPS::prepareDeepSleep);
+ CallbackObserver notifyGPSSleepObserver = CallbackObserver(this, &GPS::prepareDeepSleep);
public:
/** If !NULL we will use this serial port to construct our GPS */
@@ -77,6 +78,8 @@ class GPS : private concurrency::OSThread
/// Return true if we are connected to a GPS
bool isConnected() const { return hasGPS; }
+ bool isPowerSaving() const { return !config.position.gps_enabled;}
+
/**
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
* called after the CPU wakes from light-sleep state
diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index eac78dd64..8d3429454 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 "sleep.h"
#include "target_specific.h"
#include "utils.h"
@@ -42,6 +43,7 @@ along with this program. If not, see .
#ifdef ARCH_ESP32
#include "esp_task_wdt.h"
#include "mesh/http/WiFiAPClient.h"
+#include "modules/esp32/StoreForwardModule.h"
#endif
#ifdef OLED_RU
@@ -95,17 +97,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
@@ -329,11 +331,8 @@ static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, i
display->drawString(64 + x, y, "Updating");
display->setFont(FONT_SMALL);
- if ((millis() / 1000) % 2) {
- display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . .");
- } else {
- display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . ");
- }
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->drawStringMaxWidth(0 + x, 2 + y + FONT_HEIGHT_SMALL *2, x + display->getWidth(), "Please be patient and do not power off.");
}
/// Draw the last text message we received
@@ -364,6 +363,9 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
{
displayedNodeNum = 0; // Not currently showing a node pane
+ // the max length of this buffer is much longer than we can possibly print
+ static char tempBuf[237];
+
MeshPacket &mp = devicestate.rx_text_message;
NodeInfo *node = nodeDB.getNode(getFrom(&mp));
// DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from,
@@ -373,16 +375,14 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
// with the third parameter you can define the width after which words will
// be wrapped. Currently only spaces and "-" are allowed for wrapping
display->setTextAlignment(TEXT_ALIGN_LEFT);
- display->setFont(FONT_MEDIUM);
- String sender = (node && node->has_user) ? node->user.short_name : "???";
- display->drawString(0 + x, 0 + y, sender);
display->setFont(FONT_SMALL);
-
- // the max length of this buffer is much longer than we can possibly print
- static char tempBuf[96];
- snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.payload.bytes);
-
- display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf);
+ display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
+ display->setColor(BLACK);
+ display->drawStringf(0 + x, 0 + y, tempBuf, "From: %s", (node && node->has_user) ? node->user.short_name : "???");
+ display->drawStringf(1 + x, 0 + y, tempBuf, "From: %s", (node && node->has_user) ? node->user.short_name : "???");
+ display->setColor(WHITE);
+ snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.payload.bytes);
+ display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf);
}
/// Draw a series of fields in a column, wrapping to multiple colums if needed
@@ -395,6 +395,10 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char *
int xo = x, yo = y;
while (*f) {
display->drawString(xo, yo, *f);
+ if (display->getColor() == BLACK)
+ display->drawString(xo + 1, yo, *f);
+
+ display->setColor(WHITE);
yo += FONT_HEIGHT_SMALL;
if (yo > SCREEN_HEIGHT - FONT_HEIGHT_SMALL) {
xo += SCREEN_WIDTH / 2;
@@ -463,8 +467,13 @@ 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);
}
// Draw GPS status summary
@@ -473,15 +482,18 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus
if (config.position.fixed_position) {
// GPS coordinates are currently fixed
display->drawString(x - 1, y - 2, "Fixed GPS");
+ display->drawString(x, y - 2, "Fixed GPS");
return;
}
if (!gps->getIsConnected()) {
display->drawString(x, y - 2, "No GPS");
+ display->drawString(x + 1, y - 2, "No GPS");
return;
}
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
if (!gps->getHasLock()) {
display->drawString(x + 8, y - 2, "No sats");
+ display->drawString(x + 9, y - 2, "No sats");
return;
} else {
char satsString[3];
@@ -506,6 +518,23 @@ 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)
+{
+#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 + 2, displayLine);
+ }
+#endif
+}
+
static void drawGPSAltitude(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
{
String displayLine = "";
@@ -669,16 +698,16 @@ static uint16_t getCompassDiam(OLEDDisplay *display)
{
uint16_t diam = 0;
// get the smaller of the 2 dimensions and subtract 20
- if(display->getWidth() > display->getHeight()) {
- diam = display->getHeight();
+ if(display->getWidth() > (display->getHeight() - FONT_HEIGHT_SMALL)) {
+ diam = display->getHeight() - FONT_HEIGHT_SMALL;
// if 2/3 of the other size would be smaller, use that
if (diam > (display->getWidth() * 2 / 3)) {
diam = display->getWidth() * 2 / 3;
}
} else {
diam = display->getWidth();
- if (diam > (display->getHeight() * 2 / 3)) {
- diam = display->getHeight() * 2 / 3;
+ if (diam > ((display->getHeight() - FONT_HEIGHT_SMALL) * 2 / 3)) {
+ diam = (display->getHeight() - FONT_HEIGHT_SMALL) * 2 / 3;
}
}
@@ -758,6 +787,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
+
const char *username = node->has_user ? node->user.long_name : "Unknown Name";
static char signalStr[20];
@@ -788,7 +819,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
// coordinates for the center of the compass/circle
- int16_t compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5, compassY = y + SCREEN_HEIGHT / 2;
+ int16_t compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5, compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - FONT_HEIGHT_SMALL) / 2;
bool hasNodeHeading = false;
if (ourNode && hasPosition(ourNode)) {
@@ -831,33 +862,11 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
display->drawCircle(compassX, compassY, getCompassDiam(display) / 2);
+ display->setColor(BLACK);
// Must be after distStr is populated
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)
@@ -1357,6 +1366,9 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
+ display->setColor(BLACK);
+
char channelStr[20];
{
concurrency::LockGuard guard(&lock);
@@ -1366,18 +1378,54 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// Display power status
if (powerStatus->getHasBattery())
- drawBattery(display, x, y + 2, imgBattery, powerStatus);
+ drawBattery(display, x + 1, y + 3, imgBattery, powerStatus);
else if (powerStatus->knowsUSB())
- display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
+ display->drawFastImage(x + 1, y + 3, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
// Display nodes status
- drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
+ drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 3, nodeStatus);
// Display GPS status
- drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
+ if (!config.position.gps_enabled){
+ int16_t yPos = y + 2;
+#ifdef GPS_POWER_TOGGLE
+ yPos = (y + 10 + FONT_HEIGHT_SMALL);
+#endif
+ drawGPSpowerstat(display, x, yPos, gpsStatus);
+ } else {
+ drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
+ drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 3, gpsStatus);
+ }
+ 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 0
+ 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
+ }
+#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
@@ -1404,15 +1452,24 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
+ display->setColor(BLACK);
+
if (WiFi.status() != WL_CONNECTED) {
display->drawString(x, y, String("WiFi: Not Connected"));
+ display->drawString(x + 1, y, String("WiFi: Not Connected"));
} else {
display->drawString(x, y, String("WiFi: Connected"));
+ display->drawString(x + 1, y, String("WiFi: Connected"));
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())), y,
"RSSI " + String(WiFi.RSSI()));
+ display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())) - 1, y,
+ "RSSI " + String(WiFi.RSSI()));
}
+ display->setColor(WHITE);
+
/*
- WL_CONNECTED: assigned when connected to a WiFi network;
- WL_NO_SSID_AVAIL: assigned when no SSID are available;
@@ -1521,6 +1578,9 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
+ display->setColor(BLACK);
+
char batStr[20];
if (powerStatus->getHasBattery()) {
int batV = powerStatus->getBatteryVoltageMv() / 1000;
@@ -1531,9 +1591,11 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
// Line 1
display->drawString(x, y, batStr);
+ display->drawString(x + 1, y, batStr);
} else {
// Line 1
display->drawString(x, y, String("USB"));
+ display->drawString(x + 1, y, String("USB"));
}
auto mode = "";
@@ -1566,6 +1628,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
}
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
+ display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode);
// Line 2
uint32_t currentMillis = millis();
@@ -1578,6 +1641,8 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
// minutes %= 60;
// hours %= 24;
+ display->setColor(WHITE);
+
// Show uptime as days, hours, minutes OR seconds
String uptime;
if (days >= 2)
@@ -1613,7 +1678,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
char chUtil[13];
sprintf(chUtil, "ChUtil %2.0f%%", airTime->channelUtilizationPercent());
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil), y + FONT_HEIGHT_SMALL * 1, chUtil);
-
+if (config.position.gps_enabled) {
// Line 3
if (config.display.gps_format !=
Config_DisplayConfig_GpsCoordinateFormat_DMS) // if DMS then don't draw altitude
@@ -1621,7 +1686,9 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
// Line 4
drawGPScoordinates(display, x, y + FONT_HEIGHT_SMALL * 3, gpsStatus);
-
+} else {
+ drawGPSpowerstat(display, x - (SCREEN_WIDTH / 4), y + FONT_HEIGHT_SMALL * 2, gpsStatus);
+}
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
if (heartbeat)
diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h
index 23828b3ee..3988fa1a8 100644
--- a/src/graphics/Screen.h
+++ b/src/graphics/Screen.h
@@ -21,6 +21,7 @@ class Screen
void startBluetoothPinScreen(uint32_t pin) {}
void stopBluetoothPinScreen() {}
void startRebootScreen() {}
+ void startFirmwareUpdateScreen() {}
};
}
diff --git a/src/graphics/images.h b/src/graphics/images.h
index 41b390eaf..9bf66a3a3 100644
--- a/src/graphics/images.h
+++ b/src/graphics/images.h
@@ -12,36 +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 };
-#include "img/icon.xbm"
-
-// We now programmatically draw our compass
-#if 0
-const
-#include "img/compass.xbm"
+#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
-#if 0
-const uint8_t activeSymbol[] PROGMEM = {
- B00000000,
- B00000000,
- B00011000,
- B00100100,
- B01000010,
- B01000010,
- B00100100,
- B00011000
-};
-
-const uint8_t inactiveSymbol[] PROGMEM = {
- B00000000,
- B00000000,
- B00000000,
- B00000000,
- B00011000,
- B00011000,
- B00000000,
- B00000000
-};
-#endif
\ No newline at end of file
+#include "img/icon.xbm"
diff --git a/src/graphics/img/compass.xbm b/src/graphics/img/compass.xbm
deleted file mode 100644
index 115a7a1d4..000000000
--- a/src/graphics/img/compass.xbm
+++ /dev/null
@@ -1,28 +0,0 @@
-#define compass_width 48
-#define compass_height 48
-static char compass_bits[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00,
- 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00,
- 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x01, 0x00,
- 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0x00,
- 0x00, 0xF8, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0xFC, 0x07, 0xE0, 0x3F, 0x00,
- 0x00, 0xFE, 0x01, 0x80, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00,
- 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xFC, 0x01,
- 0x80, 0x1F, 0x00, 0x00, 0xF8, 0x01, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x01,
- 0xC0, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03,
- 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03,
- 0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F, 0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F,
- 0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F, 0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F,
- 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03,
- 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, 0xC0, 0x0F, 0x00, 0x00, 0xF0, 0x03,
- 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x01, 0x80, 0x1F, 0x00, 0x00, 0xF8, 0x01,
- 0x80, 0x3F, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00,
- 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x01, 0x80, 0x7F, 0x00,
- 0x00, 0xFC, 0x07, 0xE0, 0x3F, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x1F, 0x00,
- 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0x00,
- 0x00, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00,
- 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00,
- 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- };
diff --git a/src/graphics/img/pin.xbm b/src/graphics/img/pin.xbm
deleted file mode 100644
index e8c8f8930..000000000
--- a/src/graphics/img/pin.xbm
+++ /dev/null
@@ -1,6 +0,0 @@
-#define pin_width 13
-#define pin_height 13
-static char pin_bits[] = {
- 0x00, 0x00, 0xF0, 0x01, 0xF8, 0x03, 0xFC, 0x07, 0xBC, 0x07, 0xBC, 0x07,
- 0xFC, 0x07, 0xF8, 0x03, 0xF8, 0x03, 0xF0, 0x01, 0xE0, 0x00, 0xE0, 0x00,
- 0x00, 0x00, };
diff --git a/src/main.cpp b/src/main.cpp
index 98f22ecf1..c0fcaf96e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -8,8 +8,6 @@
#include "configuration.h"
#include "error.h"
#include "power.h"
-// #include "rom/rtc.h"
-//#include "DSRRouter.h"
#include "ReliableRouter.h"
// #include "debug.h"
#include "FSCommon.h"
@@ -217,7 +215,6 @@ void setup()
fsInit();
- // router = new DSRRouter();
router = new ReliableRouter();
#ifdef I2C_SDA1
@@ -387,7 +384,7 @@ void setup()
}
#endif
-#if defined(USE_SX1280) && !defined(ARCH_PORTDUINO)
+#if defined(USE_SX1280)
if (!rIf) {
rIf = new SX1280Interface(SX128X_CS, SX128X_DIO1, SX128X_RESET, SX128X_BUSY, SPI);
if (!rIf->init()) {
@@ -452,6 +449,30 @@ void setup()
}
#endif
+// check if the radio chip matches the selected region
+
+if((config.lora.region == Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())){
+ DEBUG_MSG("Warning: Radio chip does not support 2.4GHz LoRa. Reverting to unset.\n");
+ config.lora.region = Config_LoRaConfig_RegionCode_UNSET;
+ nodeDB.saveToDisk(SEGMENT_CONFIG);
+ if(!rIf->reconfigure()) {
+ DEBUG_MSG("Reconfigure failed, rebooting\n");
+ screen->startRebootScreen();
+ rebootAtMsec = millis() + 5000;
+ }
+}
+
+if((config.lora.region != Config_LoRaConfig_RegionCode_LORA_24) && (rIf->wideLora())){
+ DEBUG_MSG("Warning: Radio chip only supports 2.4GHz LoRa. Adjusting Region.\n");
+ config.lora.region = Config_LoRaConfig_RegionCode_LORA_24;
+ nodeDB.saveToDisk(SEGMENT_CONFIG);
+ if(!rIf->reconfigure()) {
+ DEBUG_MSG("Reconfigure failed, rebooting\n");
+ screen->startRebootScreen();
+ rebootAtMsec = millis() + 5000;
+ }
+}
+
#if HAS_WIFI || HAS_ETHERNET
mqttInit();
#endif
diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp
index 818bacf45..925735903 100644
--- a/src/mesh/FloodingRouter.cpp
+++ b/src/mesh/FloodingRouter.cpp
@@ -41,6 +41,11 @@ void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c)
tosend->hop_limit--; // bump down the hop count
+ // If it is a traceRoute request, update the route that it went via me
+ if (p->which_payload_variant == MeshPacket_decoded_tag && traceRouteModule->wantPacket(p)) {
+ traceRouteModule->updateRoute(tosend);
+ }
+
printPacket("Rebroadcasting received floodmsg to neighbors", p);
// Note: we are careful to resend using the original senders node id
// We are careful not to call our hooked version of send() - because we don't want to check this again
diff --git a/src/mesh/FloodingRouter.h b/src/mesh/FloodingRouter.h
index 387b4576b..7e6271fc0 100644
--- a/src/mesh/FloodingRouter.h
+++ b/src/mesh/FloodingRouter.h
@@ -2,6 +2,7 @@
#include "PacketHistory.h"
#include "Router.h"
+#include "modules/TraceRouteModule.h"
/**
* This is a mixin that extends Router with the ability to do Naive Flooding (in the standard mesh protocol sense)
diff --git a/src/mesh/InterfacesTemplates.cpp b/src/mesh/InterfacesTemplates.cpp
index 9602525b5..0d2246428 100644
--- a/src/mesh/InterfacesTemplates.cpp
+++ b/src/mesh/InterfacesTemplates.cpp
@@ -7,7 +7,4 @@
template class SX126xInterface;
template class SX126xInterface;
template class SX126xInterface;
-
-#if defined(RADIOLIB_GODMODE)
template class SX128xInterface;
-#endif
\ No newline at end of file
diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 6b2d5bfb3..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;
@@ -214,9 +215,9 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.has_external_notification = true;
moduleConfig.has_canned_message = true;
- strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(default_mqtt_address));
- strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(default_mqtt_username));
- strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(default_mqtt_password));
+ strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(moduleConfig.mqtt.address));
+ strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(moduleConfig.mqtt.username));
+ strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(moduleConfig.mqtt.password));
initModuleConfigIntervals();
}
diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h
index e9f725c89..f50c0ae77 100644
--- a/src/mesh/RadioInterface.h
+++ b/src/mesh/RadioInterface.h
@@ -1,6 +1,5 @@
#pragma once
-#include "../concurrency/NotifiedWorkerThread.h"
#include "MemoryPool.h"
#include "MeshTypes.h"
#include "Observer.h"
@@ -97,6 +96,8 @@ class RadioInterface
*/
virtual bool canSleep() { return true; }
+ virtual bool wideLora() { return false; }
+
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
virtual bool sleep() { return true; }
diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h
index f368cf83e..16495c2f4 100644
--- a/src/mesh/RadioLibInterface.h
+++ b/src/mesh/RadioLibInterface.h
@@ -1,10 +1,9 @@
#pragma once
-#include "../concurrency/OSThread.h"
+#include "concurrency/NotifiedWorkerThread.h"
#include "RadioInterface.h"
#include "MeshPacketQueue.h"
-#define RADIOLIB_EXCLUDE_HTTP
#include
// ESP32 has special rules about ISR code
diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp
index 2807178b8..7933a7920 100644
--- a/src/mesh/ReliableRouter.cpp
+++ b/src/mesh/ReliableRouter.cpp
@@ -90,8 +90,7 @@ void ReliableRouter::sniffReceived(const MeshPacket *p, const Routing *c)
{
NodeNum ourNode = getNodeNum();
- if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability
- // - not DSR routing)
+ if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability)
if (p->want_ack) {
if (MeshModule::currentReply)
DEBUG_MSG("Some other module has replied to this message, no need for a 2nd ack\n");
@@ -200,8 +199,7 @@ int32_t ReliableRouter::doRetransmissions()
DEBUG_MSG("Reliable send failed, returning a nak for fr=0x%x,to=0x%x,id=0x%x\n", p.packet->from, p.packet->to,
p.packet->id);
sendAckNak(Routing_Error_MAX_RETRANSMIT, getFrom(p.packet), p.packet->id, p.packet->channel);
- // Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived - which
- // allows the DSR version to still be able to look at the PendingPacket
+ // Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived
stopRetransmission(it->first);
stillValid = false; // just deleted it
} else {
diff --git a/src/mesh/ReliableRouter.h b/src/mesh/ReliableRouter.h
index 3f89dcbd6..ff304cdd7 100644
--- a/src/mesh/ReliableRouter.h
+++ b/src/mesh/ReliableRouter.h
@@ -38,12 +38,6 @@ struct PendingPacket {
/** Starts at NUM_RETRANSMISSIONS -1(normally 3) and counts down. Once zero it will be removed from the list */
uint8_t numRetransmissions = 0;
- /** True if we have started trying to find a route - for DSR usage
- * While trying to find a route we don't actually send the data packet. We just leave it here pending until
- * we have a route or we've failed to find one.
- */
- bool wantRoute = false;
-
PendingPacket() {}
explicit PendingPacket(MeshPacket *p);
};
diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp
index e0746bdd9..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"
@@ -21,7 +22,6 @@ extern "C" {
* DONE: Implement basic interface and use it elsewhere in app
* Add naive flooding mixin (& drop duplicate rx broadcasts), add tools for sending broadcasts with incrementing sequence #s
* Add an optional adjacent node only 'send with ack' mixin. If we timeout waiting for the ack, call handleAckTimeout(packet)
- * Add DSR mixin
*
**/
@@ -188,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/SX1280Interface.cpp b/src/mesh/SX1280Interface.cpp
index 97a3febe3..7fc6b45e1 100644
--- a/src/mesh/SX1280Interface.cpp
+++ b/src/mesh/SX1280Interface.cpp
@@ -2,12 +2,8 @@
#include "SX1280Interface.h"
#include "error.h"
-#if defined(RADIOLIB_GODMODE)
-
SX1280Interface::SX1280Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
SPIClass &spi)
: SX128xInterface(cs, irq, rst, busy, spi)
{
}
-
-#endif
\ No newline at end of file
diff --git a/src/mesh/SX1280Interface.h b/src/mesh/SX1280Interface.h
index a9661501a..190ca3cf4 100644
--- a/src/mesh/SX1280Interface.h
+++ b/src/mesh/SX1280Interface.h
@@ -6,12 +6,9 @@
* Our adapter for SX1280 radios
*/
-#if defined(RADIOLIB_GODMODE)
class SX1280Interface : public SX128xInterface
{
public:
SX1280Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi);
};
-
-#endif
\ No newline at end of file
diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp
index 36eb0bb94..0a51d618a 100644
--- a/src/mesh/SX128xInterface.cpp
+++ b/src/mesh/SX128xInterface.cpp
@@ -2,8 +2,6 @@
#include "SX128xInterface.h"
#include "error.h"
-#if defined(RADIOLIB_GODMODE)
-
// Particular boards might define a different max power based on what their hardware can do
#ifndef SX128X_MAX_POWER
#define SX128X_MAX_POWER 13
@@ -27,11 +25,11 @@ bool SX128xInterface::init()
pinMode(SX128X_POWER_EN, OUTPUT);
#endif
-#ifdef SX128X_RXEN // set not rx or tx mode
+#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode
digitalWrite(SX128X_RXEN, LOW); // Set low before becoming an output
pinMode(SX128X_RXEN, OUTPUT);
#endif
-#ifdef SX128X_TXEN
+#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC)
digitalWrite(SX128X_TXEN, LOW);
pinMode(SX128X_TXEN, OUTPUT);
#endif
@@ -46,6 +44,8 @@ bool SX128xInterface::init()
limitPower();
+ preambleLength = 12; // 12 is the default for this chip, 32 does not RX at all
+
int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength);
// \todo Display actual typename of the adapter, not just `SX128x`
DEBUG_MSG("SX128x init result %d\n", res);
@@ -54,12 +54,6 @@ bool SX128xInterface::init()
DEBUG_MSG("Bandwidth set to %f\n", bw);
DEBUG_MSG("Power output set to %d\n", power);
-#ifdef SX128X_TXEN
- // lora.begin sets Dio2 as RF switch control, which is not true if we are manually controlling RX and TX
- if (res == RADIOLIB_ERR_NONE)
- res = lora.setDio2AsRfSwitch(true);
-#endif
-
if (res == RADIOLIB_ERR_NONE)
res = lora.setCRC(2);
@@ -122,18 +116,28 @@ void INTERRUPT_ATTR SX128xInterface::disableInterrupt()
lora.clearDio1Action();
}
+template
+bool SX128xInterface::wideLora()
+{
+ return true;
+}
+
template
void SX128xInterface::setStandby()
{
checkNotification(); // handle any pending interrupts before we force standby
int err = lora.standby();
+
+ if (err != RADIOLIB_ERR_NONE)
+ DEBUG_MSG("SX128x standby failed with error %d\n", err);
+
assert(err == RADIOLIB_ERR_NONE);
-#ifdef SX128X_RXEN // we have RXEN/TXEN control - turn off RX and TX power
+#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn off RX and TX power
digitalWrite(SX128X_RXEN, LOW);
#endif
-#ifdef SX128X_TXEN
+#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC)
digitalWrite(SX128X_TXEN, LOW);
#endif
@@ -158,10 +162,10 @@ void SX128xInterface::addReceiveMetadata(MeshPacket *mp)
template
void SX128xInterface::configHardwareForSend()
{
-#ifdef SX128X_TXEN // we have RXEN/TXEN control - turn on TX power / off RX power
+#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn on TX power / off RX power
digitalWrite(SX128X_TXEN, HIGH);
#endif
-#ifdef SX128X_RXEN
+#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC)
digitalWrite(SX128X_RXEN, LOW);
#endif
@@ -180,10 +184,10 @@ void SX128xInterface::startReceive()
setStandby();
-#ifdef SX128X_RXEN // we have RXEN/TXEN control - turn on RX power / off TX power
+#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn on RX power / off TX power
digitalWrite(SX128X_RXEN, HIGH);
#endif
-#ifdef SX128X_TXEN
+#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC)
digitalWrite(SX128X_TXEN, LOW);
#endif
@@ -219,11 +223,13 @@ bool SX128xInterface::isChannelActive()
template
bool SX128xInterface::isActivelyReceiving()
{
- // return isChannelActive();
-
+#ifdef RADIOLIB_GODMODE
uint16_t irq = lora.getIrqStatus();
bool hasPreamble = (irq & RADIOLIB_SX128X_IRQ_HEADER_VALID);
return hasPreamble;
+#else
+ return isChannelActive();
+#endif
}
template
@@ -248,5 +254,3 @@ bool SX128xInterface::sleep()
return true;
}
-
-#endif
\ No newline at end of file
diff --git a/src/mesh/SX128xInterface.h b/src/mesh/SX128xInterface.h
index 9994ce850..5a6ed95f9 100644
--- a/src/mesh/SX128xInterface.h
+++ b/src/mesh/SX128xInterface.h
@@ -1,7 +1,5 @@
#pragma once
-#if defined(RADIOLIB_GODMODE)
-
#include "RadioLibInterface.h"
/**
@@ -19,6 +17,8 @@ class SX128xInterface : public RadioLibInterface
/// \return true if initialisation succeeded.
virtual bool init() override;
+ virtual bool wideLora() override;
+
/// Apply any radio provisioning changes
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
@@ -27,9 +27,11 @@ class SX128xInterface : public RadioLibInterface
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
virtual bool sleep() override;
- protected:
+#ifdef RADIOLIB_GODMODE
+ bool isIRQPending() override { return lora.getIrqStatus() != 0; }
+#endif
- float currentLimit = 140; // Higher OCP limit for SX128x PA
+ protected:
/**
* Specific module instance
@@ -71,5 +73,3 @@ class SX128xInterface : public RadioLibInterface
private:
};
-
-#endif
\ No newline at end of file
diff --git a/src/mesh/generated/localonly.pb.h b/src/mesh/generated/localonly.pb.h
index c154a98db..596bb7b9a 100644
--- a/src/mesh/generated/localonly.pb.h
+++ b/src/mesh/generated/localonly.pb.h
@@ -151,7 +151,7 @@ extern const pb_msgdesc_t LocalModuleConfig_msg;
/* Maximum encoded size of messages (where known) */
#define LocalConfig_size 387
-#define LocalModuleConfig_size 376
+#define LocalModuleConfig_size 358
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/mesh/generated/module_config.pb.h b/src/mesh/generated/module_config.pb.h
index 47f410a5c..4b2e29f17 100644
--- a/src/mesh/generated/module_config.pb.h
+++ b/src/mesh/generated/module_config.pb.h
@@ -93,13 +93,6 @@ typedef struct _ModuleConfig_ExternalNotificationConfig {
bool alert_message;
bool alert_bell;
bool use_pwm;
- uint8_t output_vibra;
- uint8_t output_buzzer;
- bool alert_message_vibra;
- bool alert_message_buzzer;
- bool alert_bell_vibra;
- bool alert_bell_buzzer;
- uint16_t nag_timeout;
} ModuleConfig_ExternalNotificationConfig;
typedef struct _ModuleConfig_MQTTConfig {
@@ -194,7 +187,7 @@ extern "C" {
#define ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0}
#define ModuleConfig_AudioConfig_init_default {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0}
#define ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN}
-#define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0}
#define ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0}
#define ModuleConfig_RangeTestConfig_init_default {0, 0, 0}
#define ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0}
@@ -203,7 +196,7 @@ extern "C" {
#define ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0}
#define ModuleConfig_AudioConfig_init_zero {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0}
#define ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN}
-#define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0}
#define ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0}
#define ModuleConfig_RangeTestConfig_init_zero {0, 0, 0}
#define ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0}
@@ -235,13 +228,6 @@ extern "C" {
#define ModuleConfig_ExternalNotificationConfig_alert_message_tag 5
#define ModuleConfig_ExternalNotificationConfig_alert_bell_tag 6
#define ModuleConfig_ExternalNotificationConfig_use_pwm_tag 7
-#define ModuleConfig_ExternalNotificationConfig_output_vibra_tag 8
-#define ModuleConfig_ExternalNotificationConfig_output_buzzer_tag 9
-#define ModuleConfig_ExternalNotificationConfig_alert_message_vibra_tag 10
-#define ModuleConfig_ExternalNotificationConfig_alert_message_buzzer_tag 11
-#define ModuleConfig_ExternalNotificationConfig_alert_bell_vibra_tag 12
-#define ModuleConfig_ExternalNotificationConfig_alert_bell_buzzer_tag 13
-#define ModuleConfig_ExternalNotificationConfig_nag_timeout_tag 14
#define ModuleConfig_MQTTConfig_enabled_tag 1
#define ModuleConfig_MQTTConfig_address_tag 2
#define ModuleConfig_MQTTConfig_username_tag 3
@@ -337,14 +323,7 @@ X(a, STATIC, SINGULAR, UINT32, output, 3) \
X(a, STATIC, SINGULAR, BOOL, active, 4) \
X(a, STATIC, SINGULAR, BOOL, alert_message, 5) \
X(a, STATIC, SINGULAR, BOOL, alert_bell, 6) \
-X(a, STATIC, SINGULAR, BOOL, use_pwm, 7) \
-X(a, STATIC, SINGULAR, UINT32, output_vibra, 8) \
-X(a, STATIC, SINGULAR, UINT32, output_buzzer, 9) \
-X(a, STATIC, SINGULAR, BOOL, alert_message_vibra, 10) \
-X(a, STATIC, SINGULAR, BOOL, alert_message_buzzer, 11) \
-X(a, STATIC, SINGULAR, BOOL, alert_bell_vibra, 12) \
-X(a, STATIC, SINGULAR, BOOL, alert_bell_buzzer, 13) \
-X(a, STATIC, SINGULAR, UINT32, nag_timeout, 14)
+X(a, STATIC, SINGULAR, BOOL, use_pwm, 7)
#define ModuleConfig_ExternalNotificationConfig_CALLBACK NULL
#define ModuleConfig_ExternalNotificationConfig_DEFAULT NULL
@@ -412,7 +391,7 @@ extern const pb_msgdesc_t ModuleConfig_CannedMessageConfig_msg;
/* Maximum encoded size of messages (where known) */
#define ModuleConfig_AudioConfig_size 19
#define ModuleConfig_CannedMessageConfig_size 49
-#define ModuleConfig_ExternalNotificationConfig_size 40
+#define ModuleConfig_ExternalNotificationConfig_size 22
#define ModuleConfig_MQTTConfig_size 169
#define ModuleConfig_RangeTestConfig_size 10
#define ModuleConfig_SerialConfig_size 26
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 3ce8731e7..0b617adea 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -112,12 +112,15 @@ bool AdminModule::handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *r)
#ifdef ARCH_ESP32
if (BleOta::getOtaAppVersion().isEmpty()) {
DEBUG_MSG("No OTA firmware available, scheduling regular reboot in %d seconds\n", s);
+ screen->startRebootScreen();
}else{
+ screen->startFirmwareUpdateScreen();
BleOta::switchToOtaApp();
DEBUG_MSG("Rebooting to OTA in %d seconds\n", s);
}
#else
DEBUG_MSG("Not on ESP32, scheduling regular reboot in %d seconds\n", s);
+ screen->startRebootScreen();
#endif
rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000);
break;
@@ -192,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);
@@ -207,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/Modules.cpp b/src/modules/Modules.cpp
index d24fa8f26..ece160ced 100644
--- a/src/modules/Modules.cpp
+++ b/src/modules/Modules.cpp
@@ -11,6 +11,7 @@
#include "modules/ReplyModule.h"
#include "modules/RoutingModule.h"
#include "modules/TextMessageModule.h"
+#include "modules/TraceRouteModule.h"
#include "modules/WaypointModule.h"
#if HAS_TELEMETRY
#include "modules/Telemetry/DeviceTelemetry.h"
@@ -19,10 +20,8 @@
#ifdef ARCH_ESP32
#include "modules/esp32/RangeTestModule.h"
#include "modules/esp32/StoreForwardModule.h"
-#ifdef USE_SX1280
#include "modules/esp32/AudioModule.h"
#endif
-#endif
#if defined(ARCH_ESP32) || defined(ARCH_NRF52)
#include "modules/ExternalNotificationModule.h"
#if !defined(TTGO_T_ECHO)
@@ -42,6 +41,7 @@ void setupModules()
positionModule = new PositionModule();
waypointModule = new WaypointModule();
textMessageModule = new TextMessageModule();
+ traceRouteModule = new TraceRouteModule();
// Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance
// to a global variable.
@@ -68,9 +68,7 @@ void setupModules()
#endif
#ifdef ARCH_ESP32
// Only run on an esp32 based device.
-#ifdef USE_SX1280
- new AudioModule();
-#endif
+ audioModule = new AudioModule();
new ExternalNotificationModule();
storeForwardModule = new StoreForwardModule();
diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp
index 116690d74..c25f69fa4 100644
--- a/src/modules/SerialModule.cpp
+++ b/src/modules/SerialModule.cpp
@@ -82,7 +82,7 @@ SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio")
int32_t SerialModule::runOnce()
{
-#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO)
+#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2)
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
@@ -239,7 +239,7 @@ void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp)
{
-#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO)
+#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2)
if (moduleConfig.serial.enabled) {
auto &p = mp.decoded;
diff --git a/src/modules/TraceRouteModule.cpp b/src/modules/TraceRouteModule.cpp
new file mode 100644
index 000000000..1c5fd97d3
--- /dev/null
+++ b/src/modules/TraceRouteModule.cpp
@@ -0,0 +1,86 @@
+#include "TraceRouteModule.h"
+#include "MeshService.h"
+#include "FloodingRouter.h"
+
+TraceRouteModule *traceRouteModule;
+
+
+bool TraceRouteModule::handleReceivedProtobuf(const MeshPacket &mp, RouteDiscovery *r)
+{
+ // Only handle a response
+ if (mp.decoded.request_id) {
+ printRoute(r, mp.to, mp.from);
+ }
+
+ return false; // let it be handled by RoutingModule
+}
+
+
+void TraceRouteModule::updateRoute(MeshPacket* p)
+{
+ auto &incoming = p->decoded;
+ // Only append an ID for the request (one way)
+ if (!incoming.request_id) {
+ RouteDiscovery scratch;
+ RouteDiscovery *updated = NULL;
+ memset(&scratch, 0, sizeof(scratch));
+ pb_decode_from_bytes(incoming.payload.bytes, incoming.payload.size, RouteDiscovery_fields, &scratch);
+ updated = &scratch;
+
+ appendMyID(updated);
+ printRoute(updated, p->from, NODENUM_BROADCAST);
+
+ // Set updated route to the payload of the to be flooded packet
+ p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), RouteDiscovery_fields, updated);
+ }
+}
+
+
+void TraceRouteModule::appendMyID(RouteDiscovery* updated)
+{
+ // Length of route array can normally not be exceeded due to the max. hop_limit of 7
+ if (updated->route_count < sizeof(updated->route)/sizeof(updated->route[0])) {
+ updated->route[updated->route_count] = myNodeInfo.my_node_num;
+ updated->route_count += 1;
+ } else {
+ DEBUG_MSG("WARNING: Route exceeded maximum hop limit, are you bridging networks?\n");
+ }
+}
+
+
+void TraceRouteModule::printRoute(RouteDiscovery* r, uint32_t origin, uint32_t dest)
+{
+ DEBUG_MSG("Route traced:\n");
+ DEBUG_MSG("0x%x --> ", origin);
+ for (uint8_t i=0; iroute_count; i++) {
+ DEBUG_MSG("0x%x --> ", r->route[i]);
+ }
+ if (dest != NODENUM_BROADCAST) DEBUG_MSG("0x%x\n", dest); else DEBUG_MSG("...\n");
+}
+
+
+MeshPacket* TraceRouteModule::allocReply()
+{
+ assert(currentRequest);
+
+ // Copy the payload of the current request
+ auto req = *currentRequest;
+ auto &p = req.decoded;
+ RouteDiscovery scratch;
+ RouteDiscovery *updated = NULL;
+ memset(&scratch, 0, sizeof(scratch));
+ pb_decode_from_bytes(p.payload.bytes, p.payload.size, RouteDiscovery_fields, &scratch);
+ updated = &scratch;
+
+ printRoute(updated, req.from, req.to);
+
+ // Create a MeshPacket with this payload and set it as the reply
+ MeshPacket* reply = allocDataProtobuf(*updated);
+
+ return reply;
+}
+
+
+TraceRouteModule::TraceRouteModule() : ProtobufModule("traceroute", PortNum_TRACEROUTE_APP, RouteDiscovery_fields) {
+ ourPortNum = PortNum_TRACEROUTE_APP;
+}
diff --git a/src/modules/TraceRouteModule.h b/src/modules/TraceRouteModule.h
new file mode 100644
index 000000000..15b7a564d
--- /dev/null
+++ b/src/modules/TraceRouteModule.h
@@ -0,0 +1,36 @@
+#pragma once
+#include "ProtobufModule.h"
+
+
+/**
+ * A module that traces the route to a certain destination node
+ */
+class TraceRouteModule : public ProtobufModule
+{
+ public:
+ TraceRouteModule();
+
+ // Let FloodingRouter call updateRoute upon rebroadcasting a TraceRoute request
+ friend class FloodingRouter;
+
+ protected:
+ bool handleReceivedProtobuf(const MeshPacket &mp, RouteDiscovery *r) override;
+
+ virtual MeshPacket *allocReply() override;
+
+ /* Call before rebroadcasting a RouteDiscovery payload in order to update
+ the route array containing the IDs of nodes this packet went through */
+ void updateRoute(MeshPacket* p);
+
+ private:
+ // Call to add your ID to the route array of a RouteDiscovery message
+ void appendMyID(RouteDiscovery *r);
+
+ /* Call to print the route array of a RouteDiscovery message.
+ Set origin to where the request came from.
+ Set dest to the ID of its destination, or NODENUM_BROADCAST if it has not yet arrived there. */
+ void printRoute(RouteDiscovery* r, uint32_t origin, uint32_t dest);
+
+};
+
+extern TraceRouteModule *traceRouteModule;
\ No newline at end of file
diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp
index c37a1b2a8..e7132fcc4 100644
--- a/src/modules/esp32/AudioModule.cpp
+++ b/src/modules/esp32/AudioModule.cpp
@@ -1,4 +1,6 @@
+
#include "configuration.h"
+#if defined(ARCH_ESP32)
#include "AudioModule.h"
#include "MeshService.h"
#include "NodeDB.h"
@@ -8,6 +10,10 @@
#include
+#ifdef OLED_RU
+#include "graphics/fonts/OLEDDisplayFontsRU.h"
+#endif
+
/*
AudioModule
A interface to send raw codec2 audio data over the mesh network. Based on the example code from the ESP32_codec2 project.
@@ -19,201 +25,269 @@
Basic Usage:
1) Enable the module by setting audio.codec2_enabled to 1.
- 2) Set the pins (audio.mic_pin / audio.amp_pin) for your preferred microphone and amplifier GPIO pins.
- On tbeam, recommend to use:
- audio.mic_chan 6 (GPIO 34)
- audio.amp_pin 14
- audio.ptt_pin 39
- 3) Set audio.timeout to the amount of time to wait before we consider
- your voice stream as "done".
- 4) Set audio.bitrate to the desired codec2 rate (CODEC2_3200, CODEC2_2400, CODEC2_1600, CODEC2_1400, CODEC2_1300, CODEC2_1200, CODEC2_700, CODEC2_700B)
+ 2) Set the pins for the I2S interface. Recommended on TLora is I2S_WS 13/I2S_SD 15/I2S_SIN 2/I2S_SCK 14
+ 3) Set audio.bitrate to the desired codec2 rate (CODEC2_3200, CODEC2_2400, CODEC2_1600, CODEC2_1400, CODEC2_1300, CODEC2_1200, CODEC2_700, CODEC2_700B)
KNOWN PROBLEMS
- * Until the module is initilized by the startup sequence, the amp_pin pin is in a floating
- state. This may produce a bit of "noise".
- * Will not work on NRF and the Linux device targets.
+ * Half Duplex
+ * Will not work on NRF and the Linux device targets (yet?).
*/
-#define AMIC 6
-#define AAMP 14
-#define PTT_PIN 39
-
-#define AUDIO_MODULE_RX_BUFFER 128
-#define AUDIO_MODULE_DATA_MAX Constants_DATA_PAYLOAD_LEN
-#define AUDIO_MODULE_MODE 7 // 700B
-#define AUDIO_MODULE_ACK 1
-
-#if defined(ARCH_ESP32) && defined(USE_SX1280)
-
-AudioModule *audioModule;
-
ButterworthFilter hp_filter(240, 8000, ButterworthFilter::ButterworthFilter::Highpass, 1);
-//int16_t 1KHz sine test tone
-int16_t Sine1KHz[8] = { -21210 , -30000, -21210, 0 , 21210 , 30000 , 21210, 0 };
-int Sine1KHz_index = 0;
+TaskHandle_t codec2HandlerTask;
+AudioModule *audioModule;
-uint8_t rx_raw_audio_value = 127;
+#ifdef ARCH_ESP32
+// ESP32 doesn't use that flag
+#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR()
+#else
+#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR(x)
+#endif
-AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") {
- audio_fifo.init();
-}
+#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
+#else
+#ifdef OLED_RU
+#define FONT_SMALL ArialMT_Plain_10_RU
+#else
+#define FONT_SMALL ArialMT_Plain_10
+#endif
+#define FONT_MEDIUM ArialMT_Plain_16
+#define FONT_LARGE ArialMT_Plain_24
+#endif
-void AudioModule::run_codec2()
+#define fontHeight(font) ((font)[1] + 1) // height is position 1
+
+#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
+#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
+#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE)
+
+void run_codec2(void* parameter)
{
- if (state == State::tx)
- {
- for (int i = 0; i < ADC_BUFFER_SIZE; i++)
- speech[i] = (int16_t)hp_filter.Update((float)speech[i]);
+ // 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));
- codec2_encode(codec2_state, tx_encode_frame + tx_encode_frame_index, speech);
+ DEBUG_MSG("Starting codec2 task\n");
- //increment the pointer where the encoded frame must be saved
- tx_encode_frame_index += 8;
+ while (true) {
+ uint32_t tcount = ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(10000));
- //If it is the 5th time then we have a ready trasnmission frame
- if (tx_encode_frame_index == ENCODE_FRAME_SIZE)
- {
- tx_encode_frame_index = 0;
- //Transmit it
- sendPayload();
+ if (tcount != 0) {
+ if (audioModule->radio_state == RadioState::tx) {
+ for (int i = 0; i < audioModule->adc_buffer_size; i++)
+ audioModule->speech[i] = (int16_t)hp_filter.Update((float)audioModule->speech[i]);
+
+ codec2_encode(audioModule->codec2, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index, audioModule->speech);
+ audioModule->tx_encode_frame_index += audioModule->encode_codec_size;
+
+ if (audioModule->tx_encode_frame_index == (audioModule->encode_frame_size + sizeof(audioModule->tx_header)))
+ {
+ DEBUG_MSG("Sending %d codec2 bytes\n", audioModule->encode_frame_size);
+ audioModule->sendPayload();
+ audioModule->tx_encode_frame_index = sizeof(audioModule->tx_header);
+ }
+ }
+ if (audioModule->radio_state == RadioState::rx) {
+ size_t bytesOut = 0;
+ if (memcmp(audioModule->rx_encode_frame, &audioModule->tx_header, sizeof(audioModule->tx_header)) == 0) {
+ for (int i = 4; i < audioModule->rx_encode_frame_index; i += audioModule->encode_codec_size)
+ {
+ 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));
+ }
+ } else {
+ // if the buffer header does not match our own codec, make a temp decoding setup.
+ CODEC2* tmp_codec2 = codec2_create(audioModule->rx_encode_frame[3]);
+ 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);
+ for (int i = 4; i < audioModule->rx_encode_frame_index; i += tmp_encode_codec_size)
+ {
+ 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));
+ }
+ codec2_destroy(tmp_codec2);
+ }
+ }
}
}
- if (state == State::rx) //Receiving
- {
- //Make a cycle to get each codec2 frame from the received frame
- for (int i = 0; i < ENCODE_FRAME_SIZE; i += 8)
- {
- //Decode the codec2 frame
- codec2_decode(codec2_state, output_buffer, rx_encode_frame + i);
-
- // Add to the audio buffer the 320 samples resulting of the decode of the codec2 frame.
- for (int g = 0; g < ADC_BUFFER_SIZE; g++)
- audio_fifo.put(output_buffer[g]);
- }
+}
+
+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);
+ 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;
+ codec2_set_lpc_post_filter(codec2, 1, 0, 0.8, 0.2);
+ encode_codec_size = (codec2_bits_per_frame(codec2) + 7) / 8;
+ encode_frame_num = (Constants_DATA_PAYLOAD_LEN - sizeof(tx_header)) / encode_codec_size;
+ encode_frame_size = encode_frame_num * encode_codec_size; // max 233 bytes + 4 header bytes
+ adc_buffer_size = codec2_samples_per_frame(codec2);
+ 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);
}
- state = State::standby;
}
-void AudioModule::handleInterrupt()
+void AudioModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
- audioModule->onTimer();
-}
+ displayedNodeNum = 0; // Not currently showing a node pane
-void AudioModule::onTimer()
-{
- if (state == State::tx) {
- adc_buffer[adc_buffer_index++] = (16 * adc1_get_raw(mic_chan)) - 32768;
+ char buffer[50];
- //If you want to test with a 1KHz tone, comment the line above and descomment the three lines below
-
- // adc_buffer[adc_buffer_index++] = Sine1KHz[Sine1KHz_index++];
- // if (Sine1KHz_index >= 8)
- // Sine1KHz_index = 0;
-
- if (adc_buffer_index == ADC_BUFFER_SIZE) {
- adc_buffer_index = 0;
- memcpy((void*)speech, (void*)adc_buffer, 2 * ADC_BUFFER_SIZE);
- audioModule->setIntervalFromNow(0); // process buffer immediately
- }
- } else if (state == State::rx) {
-
- int16_t v;
-
- //Get a value from audio_fifo and convert it to 0 - 255 to play it in the ADC
- //If none value is available the DAC will play the last one that was read, that's
- //why the rx_raw_audio_value variable is a global one.
- if (audio_fifo.get(&v))
- rx_raw_audio_value = (uint8_t)((v + 32768) / 256);
-
- //Play
- dacWrite(moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP, rx_raw_audio_value);
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+ display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
+ display->setColor(BLACK);
+ display->drawStringf(0 + x, 0 + y, buffer, "Codec2 Mode %d Audio", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1);
+ display->setColor(WHITE);
+ display->setFont(FONT_LARGE);
+ display->setTextAlignment(TEXT_ALIGN_CENTER);
+ switch (radio_state) {
+ case RadioState::tx:
+ display->drawString(display->getWidth() / 2 + x, (display->getHeight() - FONT_HEIGHT_SMALL) / 2 + y, "PTT");
+ break;
+ default:
+ display->drawString(display->getWidth() / 2 + x, (display->getHeight() - FONT_HEIGHT_SMALL) / 2 + y, "Receive");
+ break;
}
}
int32_t AudioModule::runOnce()
{
- if (moduleConfig.audio.codec2_enabled) {
-
+ if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) {
+ 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);
+ 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,
+ .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
+ .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
+ .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S),
+ .intr_alloc_flags = 0,
+ .dma_buf_count = 8,
+ .dma_buf_len = adc_buffer_size, // 320 * 2 bytes
+ .use_apll = false,
+ .tx_desc_auto_clear = true,
+ .fixed_mclk = 0
+ };
+ 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("Initializing ADC on Channel %u\n", moduleConfig.audio.mic_chan ? moduleConfig.audio.mic_chan : AMIC);
+ const i2s_pin_config_t pin_config = {
+ .bck_io_num = moduleConfig.audio.i2s_sck,
+ .ws_io_num = moduleConfig.audio.i2s_ws,
+ .data_out_num = moduleConfig.audio.i2s_din ? moduleConfig.audio.i2s_din : I2S_PIN_NO_CHANGE,
+ .data_in_num = moduleConfig.audio.i2s_sd ? moduleConfig.audio.i2s_sd : I2S_PIN_NO_CHANGE
+ };
+ res = i2s_set_pin(I2S_PORT, &pin_config);
+ if(res != ESP_OK)
+ DEBUG_MSG("Failed to set I2S pin config: %d\n", res);
- mic_chan = moduleConfig.audio.mic_chan ? (adc1_channel_t)(int)moduleConfig.audio.mic_chan : (adc1_channel_t)AMIC;
- adc1_config_width(ADC_WIDTH_12Bit);
- adc1_config_channel_atten(mic_chan, ADC_ATTEN_DB_6);
-
- // Start a timer at 8kHz to sample the ADC and play the audio on the DAC.
- uint32_t cpufreq = getCpuFrequencyMhz();
- switch (cpufreq){
- case 160:
- adcTimer = timerBegin(3, 1000, true); // 160 MHz / 1000 = 160KHz
- break;
- case 240:
- adcTimer = timerBegin(3, 1500, true); // 240 MHz / 1500 = 160KHz
- break;
- case 320:
- adcTimer = timerBegin(3, 2000, true); // 320 MHz / 2000 = 160KHz
- break;
- case 80:
- default:
- adcTimer = timerBegin(3, 500, true); // 80 MHz / 500 = 160KHz
- break;
- }
- timerAttachInterrupt(adcTimer, &AudioModule::handleInterrupt, true);
- timerAlarmWrite(adcTimer, 20, true); // Interrupts when counter == 20, 8.000 times a second
- timerAlarmEnable(adcTimer);
-
- DEBUG_MSG("Initializing DAC on Pin %u\n", moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP);
- DEBUG_MSG("Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN);
+ res = i2s_start(I2S_PORT);
+ if(res != ESP_OK)
+ DEBUG_MSG("Failed to start I2S: %d\n", res);
+
+ radio_state = RadioState::rx;
// Configure PTT input
- pinMode(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN, INPUT_PULLUP);
+ 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);
- state = State::rx;
-
- DEBUG_MSG("Setting up codec2 in mode %u\n", moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE);
-
- codec2_state = codec2_create(moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE);
- codec2_set_lpc_post_filter(codec2_state, 1, 0, 0.8, 0.2);
-
- firstTime = 0;
+ firstTime = false;
} else {
- // Check if we have a PTT press
- if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == LOW) {
- // PTT pressed, recording
- state = State::tx;
+ UIFrameEvent e = {false, true};
+ // 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");
+ 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);
+ sendPayload();
+ }
+ tx_encode_frame_index = sizeof(tx_header);
+ radio_state = RadioState::rx;
+ e.frameChanged = true;
+ this->notifyObservers(&e);
+ }
}
- if (state != State::standby) {
- run_codec2();
+ if (radio_state == RadioState::tx) {
+ // Get I2S data from the microphone and place in data buffer
+ size_t bytesIn = 0;
+ res = i2s_read(I2S_PORT, adc_buffer + adc_buffer_index, adc_buffer_size - adc_buffer_index, &bytesIn, pdMS_TO_TICKS(40)); // wait 40ms for audio to arrive.
+
+ if (res == ESP_OK) {
+ adc_buffer_index += bytesIn;
+ if (adc_buffer_index == adc_buffer_size) {
+ adc_buffer_index = 0;
+ memcpy((void*)speech, (void*)adc_buffer, 2 * adc_buffer_size);
+ // Notify run_codec2 task that the buffer is ready.
+ radio_state = RadioState::tx;
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ vTaskNotifyGiveFromISR(codec2HandlerTask, &xHigherPriorityTaskWoken);
+ if (xHigherPriorityTaskWoken == pdTRUE)
+ YIELD_FROM_ISR(xHigherPriorityTaskWoken);
+ }
+ }
}
}
-
return 100;
} else {
DEBUG_MSG("Audio Module Disabled\n");
-
return INT32_MAX;
}
+
}
MeshPacket *AudioModule::allocReply()
{
-
- auto reply = allocDataPacket(); // Allocate a packet for sending
-
+ auto reply = allocDataPacket();
return reply;
}
+bool AudioModule::shouldDraw()
+{
+ if (!moduleConfig.audio.codec2_enabled) {
+ return false;
+ }
+ return (radio_state == RadioState::tx);
+}
+
void AudioModule::sendPayload(NodeNum dest, bool wantReplies)
{
MeshPacket *p = allocReply();
p->to = dest;
p->decoded.want_response = wantReplies;
- p->want_ack = AUDIO_MODULE_ACK;
+ 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 = ENCODE_FRAME_SIZE;
+ p->decoded.payload.size = tx_encode_frame_index;
memcpy(p->decoded.payload.bytes, tx_encode_frame, p->decoded.payload.size);
service.sendToMesh(p);
@@ -221,17 +295,17 @@ void AudioModule::sendPayload(NodeNum dest, bool wantReplies)
ProcessMessage AudioModule::handleReceived(const MeshPacket &mp)
{
- if (moduleConfig.audio.codec2_enabled) {
+ if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) {
auto &p = mp.decoded;
if (getFrom(&mp) != nodeDB.getNodeNum()) {
- if (p.payload.size == ENCODE_FRAME_SIZE) {
- memcpy(rx_encode_frame, p.payload.bytes, p.payload.size);
- state = State::rx;
- audioModule->setIntervalFromNow(0);
- run_codec2();
- } else {
- DEBUG_MSG("Invalid payload size %u != %u\n", p.payload.size, ENCODE_FRAME_SIZE);
- }
+ memcpy(rx_encode_frame, p.payload.bytes, p.payload.size);
+ radio_state = RadioState::rx;
+ rx_encode_frame_index = p.payload.size;
+ // Notify run_codec2 task that the buffer is ready.
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ vTaskNotifyGiveFromISR(codec2HandlerTask, &xHigherPriorityTaskWoken);
+ if (xHigherPriorityTaskWoken == pdTRUE)
+ YIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
diff --git a/src/modules/esp32/AudioModule.h b/src/modules/esp32/AudioModule.h
index d82af4d43..1a41ab6c4 100644
--- a/src/modules/esp32/AudioModule.h
+++ b/src/modules/esp32/AudioModule.h
@@ -1,68 +1,87 @@
#pragma once
#include "SinglePortModule.h"
-#include "concurrency/OSThread.h"
+#include "concurrency/NotifiedWorkerThread.h"
#include "configuration.h"
+#if defined(ARCH_ESP32)
#include "NodeDB.h"
#include
-#include
+#include
#include
-#if defined(ARCH_ESP32) && defined(USE_SX1280)
#include
#include
-#include
-#endif
+#include
+#include
-#define ADC_BUFFER_SIZE 320 // 40ms of voice in 8KHz sampling frequency
-#define ENCODE_FRAME_SIZE 40 // 5 codec2 frames of 8 bytes each
+enum RadioState { standby, rx, tx };
-class AudioModule : public SinglePortModule, private concurrency::OSThread
+const char c2_magic[3] = {0xc0, 0xde, 0xc2}; // Magic number for codec2 header
+
+struct c2_header {
+ char magic[3];
+ char mode;
+};
+
+#define ADC_BUFFER_SIZE_MAX 320
+#define PTT_PIN 39
+
+#define I2S_PORT I2S_NUM_0
+
+#define AUDIO_MODULE_RX_BUFFER 128
+#define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700
+
+class AudioModule : public SinglePortModule, public Observable, private concurrency::OSThread
{
-#if defined(ARCH_ESP32) && defined(USE_SX1280)
- bool firstTime = 1;
- hw_timer_t* adcTimer = NULL;
- uint16_t adc_buffer[ADC_BUFFER_SIZE] = {};
- int16_t speech[ADC_BUFFER_SIZE] = {};
- int16_t output_buffer[ADC_BUFFER_SIZE] = {};
- unsigned char rx_encode_frame[ENCODE_FRAME_SIZE] = {};
- unsigned char tx_encode_frame[ENCODE_FRAME_SIZE] = {};
- int tx_encode_frame_index = 0;
- FastAudioFIFO audio_fifo;
- uint16_t adc_buffer_index = 0;
- adc1_channel_t mic_chan = (adc1_channel_t)0;
- struct CODEC2* codec2_state = NULL;
-
- enum State
- {
- standby, rx, tx
- };
- volatile State state = State::tx;
-
public:
+ unsigned char rx_encode_frame[Constants_DATA_PAYLOAD_LEN] = {};
+ unsigned char tx_encode_frame[Constants_DATA_PAYLOAD_LEN] = {};
+ c2_header tx_header = {};
+ int16_t speech[ADC_BUFFER_SIZE_MAX] = {};
+ int16_t output_buffer[ADC_BUFFER_SIZE_MAX] = {};
+ uint16_t adc_buffer[ADC_BUFFER_SIZE_MAX] = {};
+ int adc_buffer_size = 0;
+ uint16_t adc_buffer_index = 0;
+ int tx_encode_frame_index = sizeof(c2_header); // leave room for header
+ int rx_encode_frame_index = 0;
+ int encode_codec_size = 0;
+ int encode_frame_size = 0;
+ volatile RadioState radio_state = RadioState::rx;
+
+ struct CODEC2* codec2 = NULL;
+ // int16_t sample;
+
AudioModule();
+ bool shouldDraw();
+
/**
* Send our payload into the mesh
*/
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
protected:
+ int encode_frame_num = 0;
+ bool firstTime = true;
+
virtual int32_t runOnce() override;
- static void handleInterrupt();
-
- void onTimer();
-
- void run_codec2();
-
virtual MeshPacket *allocReply() override;
+ virtual bool wantUIFrame() override { return this->shouldDraw(); }
+ virtual Observable* getUIFrameObservable() override { return this; }
+#if !HAS_SCREEN
+ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+#else
+ virtual void drawFrame(
+ OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
+#endif
+
/** Called to handle a particular incoming message
* @return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual ProcessMessage handleReceived(const MeshPacket &mp) override;
-#endif
};
extern AudioModule *audioModule;
+#endif
\ No newline at end of file
diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/esp32/StoreForwardModule.cpp
index e51296824..9d1c44c18 100644
--- a/src/modules/esp32/StoreForwardModule.cpp
+++ b/src/modules/esp32/StoreForwardModule.cpp
@@ -16,52 +16,50 @@ StoreForwardModule *storeForwardModule;
int32_t StoreForwardModule::runOnce()
{
-
#ifdef ARCH_ESP32
+ if (moduleConfig.store_forward.enabled && is_server) {
+ // Send out the message queue.
+ if (this->busy) {
+ // Only send packets if the channel is less than 25% utilized.
+ if (airTime->channelUtilizationPercent() < polite_channel_util_percent) {
- if (moduleConfig.store_forward.enabled) {
+ // DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index);
+ storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
- if (config.device.role == Config_DeviceConfig_Role_ROUTER) {
-
- // Send out the message queue.
- if (this->busy) {
-
- // Only send packets if the channel is less than 25% utilized.
- if (airTime->channelUtilizationPercent() < polite_channel_util_percent) {
-
- // DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index);
- storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
-
- if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
- strcpy(this->routerMessage, "** S&F - Done");
- storeForwardModule->sendMessage(this->busyTo, this->routerMessage);
-
- // DEBUG_MSG("--- --- --- In busy loop - Done \n");
- this->packetHistoryTXQueue_index = 0;
- this->busy = false;
- } else {
- this->packetHistoryTXQueue_index++;
- }
+ if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
+ strcpy(this->routerMessage, "** S&F - Done");
+ storeForwardModule->sendMessage(this->busyTo, this->routerMessage);
+ // DEBUG_MSG("--- --- --- In busy loop - Done \n");
+ this->packetHistoryTXQueue_index = 0;
+ this->busy = false;
} else {
- DEBUG_MSG("Channel utilization is too high. Skipping this opportunity to send and will retry later.\n");
+ this->packetHistoryTXQueue_index++;
}
+
+ } else {
+ DEBUG_MSG("Channel utilization is too high. Retrying later.\n");
}
- DEBUG_MSG("SF myNodeInfo.bitrate = %f bytes / sec\n", myNodeInfo.bitrate);
+ DEBUG_MSG("SF bitrate = %f bytes / sec\n", myNodeInfo.bitrate);
- return (this->packetTimeMax);
- } else {
- DEBUG_MSG("Store & Forward Module - Disabled (is_router = false)\n");
+ } else if (millis() - lastHeartbeat > 300000) {
+ lastHeartbeat = millis();
+ DEBUG_MSG("Sending heartbeat\n");
+
+ StoreAndForward sf;
+ sf.rr = StoreAndForward_RequestResponse_ROUTER_HEARTBEAT;
+ sf.has_heartbeat = true;
+ sf.heartbeat.period = 300;
+ sf.heartbeat.secondary = 0; // TODO we always have one primary router for now
- return (INT32_MAX);
+ MeshPacket *p = allocDataProtobuf(sf);
+ p->to = NODENUM_BROADCAST;
+ p->decoded.want_response = false;
+ p->priority = MeshPacket_Priority_MIN;
+ service.sendToMesh(p);
}
-
- } else {
- DEBUG_MSG("Store & Forward Module - Disabled\n");
-
- return (INT32_MAX);
+ return (this->packetTimeMax);
}
-
#endif
return (INT32_MAX);
}
@@ -76,12 +74,7 @@ void StoreForwardModule::populatePSRAM()
https://learn.upesy.com/en/programmation/psram.html#psram-tab
*/
- DEBUG_MSG("Before PSRAM initilization:\n");
-
- DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize());
- DEBUG_MSG(" Free heap: %d\n", ESP.getFreeHeap());
- DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize());
- DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram());
+ DEBUG_MSG("Before PSRAM initilization: heap %d/%d PSRAM %d/%d\n", ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getFreePsram(), ESP.getPsramSize());
this->packetHistoryTXQueue =
static_cast(ps_calloc(this->historyReturnMax, sizeof(PacketHistoryStruct)));
@@ -93,19 +86,12 @@ void StoreForwardModule::populatePSRAM()
this->packetHistory = static_cast(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
- DEBUG_MSG("After PSRAM initilization:\n");
-
- DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize());
- DEBUG_MSG(" Free heap: %d\n", ESP.getFreeHeap());
- DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize());
- DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram());
- DEBUG_MSG("Store and Forward Stats:\n");
- DEBUG_MSG(" numberOfPackets for packetHistory - %u\n", numberOfPackets);
+ DEBUG_MSG("After PSRAM initilization: heap %d/%d PSRAM %d/%d\n", ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getFreePsram(), ESP.getPsramSize());
+ DEBUG_MSG("numberOfPackets for packetHistory - %u\n", numberOfPackets);
}
void StoreForwardModule::historyReport()
{
- DEBUG_MSG("Iterating through the message history...\n");
DEBUG_MSG("Message history contains %u records\n", this->packetHistoryCurrent);
}
@@ -246,8 +232,8 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp)
DEBUG_MSG("--- S&F Received something\n");
- // The router node should not be sending messages as a client.
- if (getFrom(&mp) != nodeDB.getNodeNum()) {
+ // The router node should not be sending messages as a client. Unless he is a ROUTER_CLIENT
+ if ((getFrom(&mp) != nodeDB.getNodeNum()) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) {
if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) {
DEBUG_MSG("Packet came from - PortNum_TEXT_MESSAGE_APP\n");
@@ -264,6 +250,7 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp)
} else {
storeForwardModule->historySend(1000 * 60, getFrom(&mp));
}
+
} else if ((p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && (p.payload.bytes[2] == 'm') &&
(p.payload.bytes[3] == 0x00)) {
strlcpy(this->routerMessage,
@@ -278,14 +265,12 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp)
}
} else if (mp.decoded.portnum == PortNum_STORE_FORWARD_APP) {
+ DEBUG_MSG("Packet came from an PortNum_STORE_FORWARD_APP port %u\n", mp.decoded.portnum);
} else {
DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum);
}
}
-
- } else {
- DEBUG_MSG("Store & Forward Module - Disabled\n");
}
#endif
@@ -293,92 +278,107 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp)
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
-ProcessMessage StoreForwardModule::handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p)
+bool StoreForwardModule::handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p)
{
if (!moduleConfig.store_forward.enabled) {
// If this module is not enabled in any capacity, don't handle the packet, and allow other modules to consume
- return ProcessMessage::CONTINUE;
+ return false;
}
- if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) {
- DEBUG_MSG("Packet came from an PortNum_TEXT_MESSAGE_APP port %u\n", mp.decoded.portnum);
- return ProcessMessage::CONTINUE;
- } else if (mp.decoded.portnum == PortNum_STORE_FORWARD_APP) {
- DEBUG_MSG("Packet came from an PortNum_STORE_FORWARD_APP port %u\n", mp.decoded.portnum);
-
+ if (mp.decoded.portnum != PortNum_STORE_FORWARD_APP) {
+ DEBUG_MSG("Packet came from port %u\n", mp.decoded.portnum);
+ return false;
} else {
- DEBUG_MSG("Packet came from an UNKNOWN port %u\n", mp.decoded.portnum);
- return ProcessMessage::CONTINUE;
- }
+ DEBUG_MSG("Packet came from PortNum_STORE_FORWARD_APP port %u\n", mp.decoded.portnum);
+
switch (p->rr) {
- case StoreAndForward_RequestResponse_CLIENT_ERROR:
- // Do nothing
- DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_ERROR\n");
- break;
+ case StoreAndForward_RequestResponse_CLIENT_ERROR:
+ if(is_server) {
+ // Do nothing
+ DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_ERROR\n");
+ }
+ break;
- case StoreAndForward_RequestResponse_CLIENT_HISTORY:
- DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_HISTORY\n");
+ case StoreAndForward_RequestResponse_CLIENT_HISTORY:
+ if(is_server) {
+ DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_HISTORY\n");
+ // Send the last 60 minutes of messages.
+ if (this->busy) {
+ strcpy(this->routerMessage, "** S&F - Busy. Try again shortly.");
+ storeForwardModule->sendMessage(getFrom(&mp), this->routerMessage);
+ } else {
+ storeForwardModule->historySend(1000 * 60, getFrom(&mp));
+ }
+ }
+ break;
- // Send the last 60 minutes of messages.
- if (this->busy) {
- strcpy(this->routerMessage, "** S&F - Busy. Try again shortly.");
- storeForwardModule->sendMessage(getFrom(&mp), this->routerMessage);
- } else {
- storeForwardModule->historySend(1000 * 60, getFrom(&mp));
+ case StoreAndForward_RequestResponse_CLIENT_PING:
+ if(is_server) {
+ // Do nothing
+ DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PING\n");
+ }
+ break;
+
+ case StoreAndForward_RequestResponse_CLIENT_PONG:
+ if(is_server) {
+ // Do nothing
+ DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PONG\n");
+ }
+ break;
+
+ case StoreAndForward_RequestResponse_CLIENT_STATS:
+ if(is_server) {
+ // Do nothing
+ DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_STATS\n");
+ }
+ break;
+
+ case StoreAndForward_RequestResponse_ROUTER_BUSY:
+ if(is_client) {
+ // Do nothing
+ DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_BUSY\n");
+ }
+ break;
+
+ case StoreAndForward_RequestResponse_ROUTER_ERROR:
+ if(is_client) {
+ // Do nothing
+ DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_ERROR\n");
+ }
+ break;
+
+ case StoreAndForward_RequestResponse_ROUTER_HEARTBEAT:
+ if(is_client) {
+ // Do nothing
+ DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_HEARTBEAT\n");
+ }
+ break;
+
+ case StoreAndForward_RequestResponse_ROUTER_PING:
+ if(is_client) {
+ // Do nothing
+ DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PING\n");
+ }
+ break;
+
+ case StoreAndForward_RequestResponse_ROUTER_PONG:
+ if(is_client) {
+ // Do nothing
+ DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PONG\n");
+ }
+ break;
+
+ default:
+ assert(0); // unexpected state - FIXME, make an error code and reboot
}
-
- break;
-
- case StoreAndForward_RequestResponse_CLIENT_PING:
- // Do nothing
- DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PING\n");
- break;
-
- case StoreAndForward_RequestResponse_CLIENT_PONG:
- // Do nothing
- DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PONG\n");
- break;
-
- case StoreAndForward_RequestResponse_CLIENT_STATS:
- // Do nothing
- DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_STATS\n");
- break;
-
- case StoreAndForward_RequestResponse_ROUTER_BUSY:
- // Do nothing
- DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_BUSY\n");
- break;
-
- case StoreAndForward_RequestResponse_ROUTER_ERROR:
- // Do nothing
- DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_ERROR\n");
- break;
-
- case StoreAndForward_RequestResponse_ROUTER_HEARTBEAT:
- // Do nothing
- DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_HEARTBEAT\n");
- break;
-
- case StoreAndForward_RequestResponse_ROUTER_PING:
- // Do nothing
- DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PING\n");
- break;
-
- case StoreAndForward_RequestResponse_ROUTER_PONG:
- // Do nothing
- DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PONG\n");
- break;
-
- default:
- assert(0); // unexpected state - FIXME, make an error code and reboot
}
- return ProcessMessage::STOP; // There's no need for others to look at this message.
+ return true; // There's no need for others to look at this message.
}
StoreForwardModule::StoreForwardModule()
- : SinglePortModule("StoreForwardModule", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("StoreForwardModule")
+ : concurrency::OSThread("StoreForwardModule"), ProtobufModule("StoreForward", PortNum_STORE_FORWARD_APP, &StoreAndForward_msg)
{
#ifdef ARCH_ESP32
@@ -397,9 +397,9 @@ StoreForwardModule::StoreForwardModule()
if (moduleConfig.store_forward.enabled) {
// Router
- if (config.device.role == Config_DeviceConfig_Role_ROUTER) {
- DEBUG_MSG("Initializing Store & Forward Module - Enabled as Router\n");
- if (ESP.getPsramSize()) {
+ if ((config.device.role == Config_DeviceConfig_Role_ROUTER) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) {
+ DEBUG_MSG("Initializing Store & Forward Module in Router mode\n");
+ if (ESP.getPsramSize() > 0) {
if (ESP.getFreePsram() >= 1024 * 1024) {
// Do the startup here
@@ -416,26 +416,27 @@ StoreForwardModule::StoreForwardModule()
if (moduleConfig.store_forward.records)
this->records = moduleConfig.store_forward.records;
- // Maximum number of records to store in memory
+ // send heartbeat advertising?
if (moduleConfig.store_forward.heartbeat)
this->heartbeat = moduleConfig.store_forward.heartbeat;
// Popupate PSRAM with our data structures.
this->populatePSRAM();
-
+ is_server = true;
} else {
- DEBUG_MSG("Device has less than 1M of PSRAM free. Aborting startup.\n");
- DEBUG_MSG("Store & Forward Module - Aborting Startup.\n");
+ DEBUG_MSG("Device has less than 1M of PSRAM free.\n");
+ DEBUG_MSG("Store & Forward Module - disabling server.\n");
}
-
} else {
DEBUG_MSG("Device doesn't have PSRAM.\n");
- DEBUG_MSG("Store & Forward Module - Aborting Startup.\n");
+ DEBUG_MSG("Store & Forward Module - disabling server.\n");
}
// Client
- } else {
- DEBUG_MSG("Initializing Store & Forward Module - Enabled as Client\n");
+ }
+ if ((config.device.role == Config_DeviceConfig_Role_CLIENT) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) {
+ is_client = true;
+ DEBUG_MSG("Initializing Store & Forward Module in Client mode\n");
}
}
#endif
diff --git a/src/modules/esp32/StoreForwardModule.h b/src/modules/esp32/StoreForwardModule.h
index 2c6d6ec6e..32d3cddc9 100644
--- a/src/modules/esp32/StoreForwardModule.h
+++ b/src/modules/esp32/StoreForwardModule.h
@@ -1,6 +1,6 @@
#pragma once
-#include "SinglePortModule.h"
+#include "ProtobufModule.h"
#include "concurrency/OSThread.h"
#include "mesh/generated/storeforward.pb.h"
@@ -18,9 +18,8 @@ struct PacketHistoryStruct {
pb_size_t payload_size;
};
-class StoreForwardModule : public SinglePortModule, private concurrency::OSThread
+class StoreForwardModule : private concurrency::OSThread, public ProtobufModule
{
- // bool firstTime = 1;
bool busy = 0;
uint32_t busyTo = 0;
char routerMessage[Constants_DATA_PAYLOAD_LEN] = {0};
@@ -34,7 +33,12 @@ class StoreForwardModule : public SinglePortModule, private concurrency::OSThrea
uint32_t packetHistoryTXQueue_size = 0;
uint32_t packetHistoryTXQueue_index = 0;
- uint32_t packetTimeMax = 2000;
+ uint32_t packetTimeMax = 5000;
+
+ unsigned long lastHeartbeat = 0;
+
+ bool is_client = false;
+ bool is_server = false;
public:
StoreForwardModule();
@@ -78,7 +82,7 @@ class StoreForwardModule : public SinglePortModule, private concurrency::OSThrea
it
*/
virtual ProcessMessage handleReceived(const MeshPacket &mp) override;
- virtual ProcessMessage handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p);
+ virtual bool handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p);
};
diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp
index 21df018d8..e36a8cfd8 100644
--- a/src/platform/esp32/main-esp32.cpp
+++ b/src/platform/esp32/main-esp32.cpp
@@ -3,7 +3,9 @@
#include "esp_task_wdt.h"
#include "main.h"
+#if !defined(CONFIG_IDF_TARGET_ESP32S2)
#include "nimble/NimbleBluetooth.h"
+#endif
#include "BleOta.h"
#include "mesh/http/WiFiAPClient.h"
@@ -16,13 +18,9 @@
#include
#include "soc/rtc.h"
+#if !defined(CONFIG_IDF_TARGET_ESP32S2)
NimbleBluetooth *nimbleBluetooth;
-void getMacAddr(uint8_t *dmac)
-{
- assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
-}
-
void setBluetoothEnable(bool on) {
if (!isWifiAvailable() && config.bluetooth.enabled == true) {
@@ -36,6 +34,15 @@ void setBluetoothEnable(bool on) {
}
}
}
+#else
+void setBluetoothEnable(bool on) { }
+void updateBatteryLevel(uint8_t level) { }
+#endif
+
+void getMacAddr(uint8_t *dmac)
+{
+ assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
+}
#ifdef HAS_32768HZ
#define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk)
diff --git a/src/platform/portduino/SimRadio.h b/src/platform/portduino/SimRadio.h
index dad419c62..a71cf22f8 100644
--- a/src/platform/portduino/SimRadio.h
+++ b/src/platform/portduino/SimRadio.h
@@ -4,7 +4,6 @@
#include "MeshPacketQueue.h"
#include "wifi/WiFiServerAPI.h"
-#define RADIOLIB_EXCLUDE_HTTP
#include
class SimRadio : public RadioInterface
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;
diff --git a/src/sleep.cpp b/src/sleep.cpp
index 390ab7f65..40426a777 100644
--- a/src/sleep.cpp
+++ b/src/sleep.cpp
@@ -29,7 +29,9 @@ Observable preflightSleep;
/// Called to tell observers we are now entering sleep and you should prepare. Must return 0
/// notifySleep will be called for light or deep sleep, notifyDeepSleep is only called for deep sleep
+/// notifyGPSSleep will be called when config.position.gps_enabled is set to 0 or from buttonthread when GPS_POWER_TOGGLE is enabled.
Observable notifySleep, notifyDeepSleep;
+Observable notifyGPSSleep;
// deep sleep support
RTC_DATA_ATTR int bootCount = 0;
@@ -167,6 +169,36 @@ static void waitEnterSleep()
notifySleep.notifyObservers(NULL);
}
+void doGPSpowersave(bool on)
+{
+ #ifdef HAS_PMU
+ if (on)
+ {
+ DEBUG_MSG("Turning GPS back on\n");
+ gps->forceWake(1);
+ setGPSPower(1);
+ }
+ else
+ {
+ DEBUG_MSG("Turning off GPS chip\n");
+ notifyGPSSleep.notifyObservers(NULL);
+ setGPSPower(0);
+ }
+ #endif
+ #ifdef PIN_GPS_WAKE
+ if (on)
+ {
+ DEBUG_MSG("Waking GPS");
+ gps->forceWake(1);
+ }
+ else
+ {
+ DEBUG_MSG("GPS entering sleep");
+ notifyGPSSleep.notifyObservers(NULL);
+ }
+ #endif
+}
+
void doDeepSleep(uint64_t msecToWake)
{
DEBUG_MSG("Entering deep sleep for %lu seconds\n", msecToWake / 1000);
@@ -237,7 +269,7 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
-#ifdef BUTTON_NEED_PULLUP
+#if defined(BUTTON_PIN) && defined(BUTTON_NEED_PULLUP)
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
#endif
@@ -300,6 +332,8 @@ void enableModemSleep()
#if CONFIG_IDF_TARGET_ESP32S3
esp32_config.max_freq_mhz = CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ;
+#elif CONFIG_IDF_TARGET_ESP32S2
+ esp32_config.max_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ;
#else
esp32_config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
#endif
diff --git a/src/sleep.h b/src/sleep.h
index 7f0592af4..af59a8dad 100644
--- a/src/sleep.h
+++ b/src/sleep.h
@@ -13,7 +13,7 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t msecToWake);
extern esp_sleep_source_t wakeCause;
#endif
void setGPSPower(bool on);
-
+void doGPSpowersave(bool on);
// Perform power on init that we do on each wake from deep sleep
void initDeepSleep();
@@ -37,4 +37,6 @@ extern Observable notifySleep;
/// Called to tell observers we are now entering (deep) sleep and you should prepare. Must return 0
extern Observable notifyDeepSleep;
+/// Called to tell GPS thread to enter deep sleep independently of LoRa/MCU sleep, prior to full poweroff. Must return 0
+extern Observable notifyGPSSleep;
void enableModemSleep();
\ No newline at end of file
diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini
index 6cd75bed8..372f42d64 100644
--- a/variants/heltec_v3/platformio.ini
+++ b/variants/heltec_v3/platformio.ini
@@ -1,6 +1,6 @@
[env:heltec-v3]
platform = https://github.com/Baptou88/platform-espressif32.git
-extends = esp32_base
+extends = esp32s3_base
board = heltec_wifi_lora_32_V3
# Temporary: https://community.platformio.org/t/heltec-esp32-lora-v3-board-support/30406/2
platform_packages =
diff --git a/variants/heltec_wsl_v3/platformio.ini b/variants/heltec_wsl_v3/platformio.ini
index 8854b1a44..336e44936 100644
--- a/variants/heltec_wsl_v3/platformio.ini
+++ b/variants/heltec_wsl_v3/platformio.ini
@@ -1,6 +1,6 @@
[env:heltec-wsl-v3]
platform = https://github.com/Baptou88/platform-espressif32.git
-extends = esp32_base
+extends = esp32s3_base
board = heltec_wifi_lora_32_V3
# Temporary: https://community.platformio.org/t/heltec-esp32-lora-v3-board-support/30406/2
platform_packages =
diff --git a/variants/rak4631/variant.h b/variants/rak4631/variant.h
index 0c1e15f4b..7b07c9aff 100644
--- a/variants/rak4631/variant.h
+++ b/variants/rak4631/variant.h
@@ -169,6 +169,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
IO4 <-> P0.04 (Arduino GPIO number 4)
IO5 <-> P0.09 (Arduino GPIO number 9)
IO6 <-> P0.10 (Arduino GPIO number 10)
+ IO7 <-> P0.28 (Arduino GPIO number 28)
SW1 <-> P0.01 (Arduino GPIO number 1)
A0 <-> P0.04/AIN2 (Arduino Analog A2
A1 <-> P0.31/AIN7 (Arduino Analog A7
diff --git a/variants/rak4631_epaper/variant.h b/variants/rak4631_epaper/variant.h
index 2b626cf91..890f00ddd 100644
--- a/variants/rak4631_epaper/variant.h
+++ b/variants/rak4631_epaper/variant.h
@@ -169,6 +169,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
IO4 <-> P0.04 (Arduino GPIO number 4)
IO5 <-> P0.09 (Arduino GPIO number 9)
IO6 <-> P0.10 (Arduino GPIO number 10)
+ IO7 <-> P0.28 (Arduino GPIO number 28)
SW1 <-> P0.01 (Arduino GPIO number 1)
A0 <-> P0.04/AIN2 (Arduino Analog A2
A1 <-> P0.31/AIN7 (Arduino Analog A7
diff --git a/variants/tbeam/platformio.ini b/variants/tbeam/platformio.ini
index 578a74f7b..76a03d126 100644
--- a/variants/tbeam/platformio.ini
+++ b/variants/tbeam/platformio.ini
@@ -6,4 +6,5 @@ lib_deps =
${esp32_base.lib_deps}
build_flags =
${esp32_base.build_flags} -D TBEAM_V10 -I variants/tbeam
+ -DGPS_POWER_TOGGLE ; comment this line to disable double press function on the user button to turn off gps entirely.
upload_speed = 921600
diff --git a/variants/tlora_v2_1_16/variant.h b/variants/tlora_v2_1_16/variant.h
index a58b10b81..14175f48b 100644
--- a/variants/tlora_v2_1_16/variant.h
+++ b/variants/tlora_v2_1_16/variant.h
@@ -3,8 +3,6 @@
#define GPS_RX_PIN 15 // per @der_bear on the forum, 36 is incorrect for this board type and 15 is a better pick
#define GPS_TX_PIN 13
-#define EXT_NOTIFY_OUT 2 // Default pin to use for Ext Notify Module.
-
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
// ratio of voltage divider = 2.0 (R42=100k, R43=100k)
#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage.
@@ -12,9 +10,6 @@
#define I2C_SDA 21 // I2C pins for this board
#define I2C_SCL 22
-// #define RESET_OLED 16 // If defined, this pin will be used to reset the display controller. Crashes on newer ESP-IDF and not needed per schematic
-
-#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
#define LED_PIN 25 // If defined we will blink this LED
#define BUTTON_PIN 12 // If defined, this will be used for user button presses,
diff --git a/variants/tlora_v2_1_18/variant.h b/variants/tlora_v2_1_18/variant.h
index dd94847be..95d699767 100644
--- a/variants/tlora_v2_1_18/variant.h
+++ b/variants/tlora_v2_1_18/variant.h
@@ -1,9 +1,5 @@
#undef GPS_RX_PIN
#undef GPS_TX_PIN
-#define GPS_RX_PIN 15 // per @der_bear on the forum, 36 is incorrect for this board type and 15 is a better pick
-#define GPS_TX_PIN 13
-
-#define EXT_NOTIFY_OUT 2 // Default pin to use for Ext Notify Module.
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
// ratio of voltage divider = 2.0 (R42=100k, R43=100k)
@@ -20,11 +16,7 @@
#define USE_SX1280
#define LORA_RESET 23
-#define SX128X_CS 18 // FIXME - we really should define LORA_CS instead
+#define SX128X_CS 18
#define SX128X_DIO1 26
-#define SX128X_DIO2 33
#define SX128X_BUSY 32
-#define SX128X_RESET LORA_RESET
-#define SX128X_E22 // Not really an E22 but TTGO seems to be trying to clone that
-// Internally the TTGO module hooks the SX1280-DIO2 in to control the TX/RX switch (which is the default for the sx1280interface
-// code)
+#define SX128X_RESET LORA_RESET
\ No newline at end of file