diff --git a/.github/workflows/update_protobufs.yml b/.github/workflows/update_protobufs.yml
new file mode 100644
index 000000000..f84ad98a8
--- /dev/null
+++ b/.github/workflows/update_protobufs.yml
@@ -0,0 +1,24 @@
+name: "Update protobufs"
+on: workflow_dispatch
+
+jobs:
+ update-protobufs:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ with:
+ submodules: true
+
+ - name: Update Submodule
+ run: |
+ git pull --recurse-submodules
+ git submodule update --remote --recursive
+ - name: Commit update
+ run: |
+ git config --global user.name 'github-actions'
+ git config --global user.email 'bot@noreply.github.com'
+ git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
+ git add proto
+ git commit -m "Update protobuf submodule" && git push || echo "No changes to commit"
diff --git a/.gitignore b/.gitignore
index 40bba6f69..3d181a39a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,6 @@ flash.uf2
cmake-build*
__pycache__
+*.swp
+*.swo
+*~
diff --git a/bin/.promote-release.sh.swp b/bin/.promote-release.sh.swp
deleted file mode 100644
index 0dddde02b..000000000
Binary files a/bin/.promote-release.sh.swp and /dev/null differ
diff --git a/bin/build-all.sh b/bin/build-all.sh
index 922fd4b7d..1cf83c62f 100755
--- a/bin/build-all.sh
+++ b/bin/build-all.sh
@@ -5,7 +5,7 @@ set -e
VERSION=`bin/buildinfo.py long`
SHORT_VERSION=`bin/buildinfo.py short`
-BOARDS_ESP32="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec-v2.0 heltec-v2.1 tbeam0.7 meshtastic-diy-v1"
+BOARDS_ESP32="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec-v1 heltec-v2.0 heltec-v2.1 tbeam0.7 meshtastic-diy-v1"
#BOARDS_ESP32=tbeam
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
diff --git a/bin/build-nightly.sh b/bin/build-nightly.sh
deleted file mode 100755
index de31c6ed1..000000000
--- a/bin/build-nightly.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/bash
-source ~/.bashrc
-
-# Meshtastic Nightly Build Script.
-# McHamster (jm@casler.org)
-#
-# This is the script that is used for the nightly build server.
-#
-# It's probably not useful for most people, but you may want to run your own
-# nightly builds.
-#
-# The last line of ~/.bashrc contains an inclusion of platformio in the path.
-# Without this, the build script won't run from the crontab:
-#
-# export PATH="$HOME/.platformio/penv/bin:$PATH"
-#
-# The crontab contains:
-# 0 2 * * * cd ~/meshtastic/github/meshtastic && source "~/.bashrc"; ./build-nightly.sh > ~/cronout.txt 2> ~/cronout.txt
-
-cd Meshtastic-device
-
-git pull
-
-bin/build-all.sh
-
-date_stamp=$(date +'%Y-%m-%d')
-
-cd ..
-
-# TODO: Archive the same binaries used by the build-all script.
-#zip -r meshtastic_device_nightly_${date_stamp} Meshtastic-device/release/latest/bins
-cp Meshtastic-device/release/archive/`ls -t ./Meshtastic-device/release/archive/| head -1` meshtastic_device_nightly_${date_stamp}.zip
-
-# Copy the file to the webserver
-scp meshtastic_device_nightly_${date_stamp}.zip jm@10.11.12.20:/volume1/web/meshtastic/nightly_builds/
-
-# Delete the local copy
-rm meshtastic_device_nightly_${date_stamp}.zip
diff --git a/platformio.ini b/platformio.ini
index 3e1056b7d..fa1e8f622 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -9,10 +9,12 @@
; https://docs.platformio.org/page/projectconf.html
[platformio]
-;default_envs = tbeam
+default_envs = tbeam
;default_envs = tbeam0.7
;default_envs = heltec-v2.0
-default_envs = tlora-v1
+;default_envs = heltec-v1
+;default_envs = tlora-v1
+;default_envs = tlora-v1
;default_envs = tlora_v1_3
;default_envs = tlora-v2
;default_envs = lora-relay-v1 # nrf board
@@ -114,7 +116,9 @@ lib_deps =
paulstoffregen/OneWire@^2.3.5
robtillaart/DS18B20@^0.1.11
h2zero/NimBLE-Arduino@1.3.1
+ tobozo/ESP32-targz@^1.1.4
arduino-libraries/NTPClient#531eff39d9fbc831f3d03f706a161739203fbe2a
+
# Hmm - this doesn't work yet
# board_build.ldscript = linker/esp32.extram.bss.ld
lib_ignore =
@@ -128,8 +132,8 @@ platform_packages =
;upload_port = /dev/ttyUSB0
;monitor_port = /dev/ttyUSB0
-upload_port = /dev/cu.SLAB_USBtoUART
-monitor_port = /dev/cu.SLAB_USBtoUART
+;upload_port = /dev/cu.SLAB_USBtoUART
+;monitor_port = /dev/cu.SLAB_USBtoUART
; customize the partition table
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
@@ -159,6 +163,13 @@ board = ttgo-t-beam
build_flags =
${esp32_base.build_flags} -D TBEAM_V07
+[env:heltec-v1]
+;build_type = debug ; to make it possible to step through our jtag debugger
+extends = esp32_base
+board = heltec_wifi_lora_32
+build_flags =
+ ${esp32_base.build_flags} -D HELTEC_V1
+
[env:heltec-v2.0]
;build_type = debug ; to make it possible to step through our jtag debugger
extends = esp32_base
diff --git a/proto b/proto
index c52c8ef99..7b80bde42 160000
--- a/proto
+++ b/proto
@@ -1 +1 @@
-Subproject commit c52c8ef99ead88a5edb73891b33247c54d37cb28
+Subproject commit 7b80bde4213c530ab3d85a19d1795025299d4633
diff --git a/src/airtime.cpp b/src/airtime.cpp
index 683b8f2c0..3f744d4ac 100644
--- a/src/airtime.cpp
+++ b/src/airtime.cpp
@@ -1,117 +1,135 @@
-#include "configuration.h"
#include "airtime.h"
#include "NodeDB.h"
+#include "configuration.h"
-#define periodsToLog 24
AirTime *airTime;
-uint32_t secondsPerPeriod = 3600;
-uint32_t lastMillis = 0;
-uint32_t secSinceBoot = 0;
-
-// AirTime at;
-
// Don't read out of this directly. Use the helper functions.
-struct airtimeStruct {
- uint32_t periodTX[periodsToLog]; // AirTime transmitted
- uint32_t periodRX[periodsToLog]; // AirTime received and repeated (Only valid mesh packets)
- uint32_t periodRX_ALL[periodsToLog]; // AirTime received regardless of valid mesh packet. Could include noise.
- uint8_t lastPeriodIndex;
-} airtimes;
+
void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms)
{
+
+ // TODO: Is the airtimes array still necessary? It's now in myNodeInfo anyway
+
if (reportType == TX_LOG) {
DEBUG_MSG("AirTime - Packet transmitted : %ums\n", airtime_ms);
- airtimes.periodTX[0] = airtimes.periodTX[0] + airtime_ms;
+ this->airtimes.periodTX[0] = this->airtimes.periodTX[0] + airtime_ms;
myNodeInfo.air_period_tx[0] = myNodeInfo.air_period_tx[0] + airtime_ms;
} else if (reportType == RX_LOG) {
DEBUG_MSG("AirTime - Packet received : %ums\n", airtime_ms);
- airtimes.periodRX[0] = airtimes.periodRX[0] + airtime_ms;
+ this->airtimes.periodRX[0] = this->airtimes.periodRX[0] + airtime_ms;
myNodeInfo.air_period_rx[0] = myNodeInfo.air_period_rx[0] + airtime_ms;
} else if (reportType == RX_ALL_LOG) {
DEBUG_MSG("AirTime - Packet received (noise?) : %ums\n", airtime_ms);
- airtimes.periodRX_ALL[0] = airtimes.periodRX_ALL[0] + airtime_ms;
- } else {
- DEBUG_MSG("AirTime - Unknown report time. This should never happen!!\n");
+ this->airtimes.periodRX_ALL[0] = this->airtimes.periodRX_ALL[0] + airtime_ms;
}
+
+ uint8_t channelUtilPeriod = (getSecondsSinceBoot() / 10) % CHANNEL_UTILIZATION_PERIODS;
+ this->channelUtilization[channelUtilPeriod] = channelUtilization[channelUtilPeriod] + airtime_ms;
}
-uint8_t currentPeriodIndex()
+uint8_t AirTime::currentPeriodIndex()
{
- return ((getSecondsSinceBoot() / secondsPerPeriod) % periodsToLog);
+ return ((getSecondsSinceBoot() / SECONDS_PER_PERIOD) % PERIODS_TO_LOG);
}
-void airtimeRotatePeriod()
+void AirTime::airtimeRotatePeriod()
{
- if (airtimes.lastPeriodIndex != currentPeriodIndex()) {
+ if (this->airtimes.lastPeriodIndex != currentPeriodIndex()) {
DEBUG_MSG("Rotating airtimes to a new period = %u\n", currentPeriodIndex());
- for (int i = periodsToLog - 2; i >= 0; --i) {
- airtimes.periodTX[i + 1] = airtimes.periodTX[i];
- airtimes.periodRX[i + 1] = airtimes.periodRX[i];
- airtimes.periodRX_ALL[i + 1] = airtimes.periodRX_ALL[i];
+ for (int i = PERIODS_TO_LOG - 2; i >= 0; --i) {
+ this->airtimes.periodTX[i + 1] = this->airtimes.periodTX[i];
+ this->airtimes.periodRX[i + 1] = this->airtimes.periodRX[i];
+ this->airtimes.periodRX_ALL[i + 1] = this->airtimes.periodRX_ALL[i];
myNodeInfo.air_period_tx[i + 1] = myNodeInfo.air_period_tx[i];
myNodeInfo.air_period_rx[i + 1] = myNodeInfo.air_period_rx[i];
}
- airtimes.periodTX[0] = 0;
- airtimes.periodRX[0] = 0;
- airtimes.periodRX_ALL[0] = 0;
+
+ this->airtimes.periodTX[0] = 0;
+ this->airtimes.periodRX[0] = 0;
+ this->airtimes.periodRX_ALL[0] = 0;
myNodeInfo.air_period_tx[0] = 0;
myNodeInfo.air_period_rx[0] = 0;
-
- airtimes.lastPeriodIndex = currentPeriodIndex();
+ this->airtimes.lastPeriodIndex = currentPeriodIndex();
}
}
-uint32_t *airtimeReport(reportTypes reportType)
+uint32_t *AirTime::airtimeReport(reportTypes reportType)
{
if (reportType == TX_LOG) {
- return airtimes.periodTX;
+ return this->airtimes.periodTX;
} else if (reportType == RX_LOG) {
- return airtimes.periodRX;
+ return this->airtimes.periodRX;
} else if (reportType == RX_ALL_LOG) {
- return airtimes.periodRX_ALL;
+ return this->airtimes.periodRX_ALL;
}
return 0;
}
-uint8_t getPeriodsToLog()
+uint8_t AirTime::getPeriodsToLog()
{
- return periodsToLog;
+ return PERIODS_TO_LOG;
}
-uint32_t getSecondsPerPeriod()
+uint32_t AirTime::getSecondsPerPeriod()
{
- return secondsPerPeriod;
+ return SECONDS_PER_PERIOD;
}
-uint32_t getSecondsSinceBoot()
+uint32_t AirTime::getSecondsSinceBoot()
{
- return secSinceBoot;
+ return this->secSinceBoot;
+}
+
+float AirTime::channelUtilizationPercent()
+{
+ uint32_t sum = 0;
+ for (uint32_t i = 0; i < CHANNEL_UTILIZATION_PERIODS; i++) {
+ sum += this->channelUtilization[i];
+ // DEBUG_MSG("ChanUtilArray %u %u\n", i, this->channelUtilization[i]);
+ }
+
+ return (float(sum) / float(CHANNEL_UTILIZATION_PERIODS * 10 * 1000)) * 100;
}
AirTime::AirTime() : concurrency::OSThread("AirTime") {}
int32_t AirTime::runOnce()
{
- //DEBUG_MSG("AirTime::runOnce()\n");
-
- airtimeRotatePeriod();
secSinceBoot++;
- /*
- This actually doesn't need to be run once per second but we currently use it for the
- secSinceBoot counter.
+ uint8_t utilPeriod = (getSecondsSinceBoot() / 10) % CHANNEL_UTILIZATION_PERIODS;
+
+ if (firstTime) {
+ airtimeRotatePeriod();
+
+ for (uint32_t i = 0; i < CHANNEL_UTILIZATION_PERIODS; i++) {
+ this->channelUtilization[i] = 0;
+ }
+
+ firstTime = false;
+ lastUtilPeriod = utilPeriod;
+
+ } else {
+
+ // Reset the channelUtilization window when we roll over
+ if (lastUtilPeriod != utilPeriod) {
+ lastUtilPeriod = utilPeriod;
+
+ this->channelUtilization[utilPeriod] = 0;
+ }
+
+ // Update channel_utilization every second.
+ myNodeInfo.channel_utilization = airTime->channelUtilizationPercent();
+ }
- If we have a better counter of how long the device has been online (and not millis())
- then we can change this to something less frequent. Maybe once ever 5 seconds?
- */
return (1000 * 1);
}
\ No newline at end of file
diff --git a/src/airtime.h b/src/airtime.h
index 134bad47d..bf78ac3a9 100644
--- a/src/airtime.h
+++ b/src/airtime.h
@@ -23,21 +23,18 @@
RX_ALL_LOG - RX_LOG = Other lora radios on our frequency channel.
*/
+
+#define CHANNEL_UTILIZATION_PERIODS 6
+#define SECONDS_PER_PERIOD 3600
+#define PERIODS_TO_LOG 24
+
+
enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG };
void logAirtime(reportTypes reportType, uint32_t airtime_ms);
-void airtimeRotatePeriod();
-
-uint8_t currentPeriodIndex();
-uint8_t getPeriodsToLog();
-
-uint32_t getSecondsSinceBoot();
-
uint32_t *airtimeReport(reportTypes reportType);
-uint32_t getSecondsPerPeriod();
-
class AirTime : private concurrency::OSThread
{
@@ -45,6 +42,27 @@ class AirTime : private concurrency::OSThread
AirTime();
void logAirtime(reportTypes reportType, uint32_t airtime_ms);
+ float channelUtilizationPercent();
+ uint32_t channelUtilization[CHANNEL_UTILIZATION_PERIODS];
+
+ uint8_t currentPeriodIndex();
+ void airtimeRotatePeriod();
+ uint8_t getPeriodsToLog();
+ uint32_t getSecondsPerPeriod();
+ uint32_t getSecondsSinceBoot();
+ uint32_t *airtimeReport(reportTypes reportType);
+
+ private:
+ bool firstTime = true;
+ uint8_t lastUtilPeriod = 0;
+ uint32_t secSinceBoot = 0;
+
+ struct airtimeStruct {
+ uint32_t periodTX[PERIODS_TO_LOG]; // AirTime transmitted
+ uint32_t periodRX[PERIODS_TO_LOG]; // AirTime received and repeated (Only valid mesh packets)
+ uint32_t periodRX_ALL[PERIODS_TO_LOG]; // AirTime received regardless of valid mesh packet. Could include noise.
+ uint8_t lastPeriodIndex;
+ } airtimes;
protected:
virtual int32_t runOnce() override;
diff --git a/src/configuration.h b/src/configuration.h
index 597f60094..89fa106ff 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -244,6 +244,7 @@ along with this program. If not, see .
#define BUTTON_PIN 39 // The middle button GPIO on the T-Beam
#define EXT_NOTIFY_OUT 12 // Overridden default pin to use for Ext Notify Plugin (#975).
+#define LED_PIN 2 // add status LED (compatible with core-pcb and DIY targets)
#define LORA_DIO0 26 // a No connect on the SX1262/SX1268 module
#define LORA_RESET 23 // RST for SX1276, and for SX1262/SX1268
@@ -323,6 +324,41 @@ along with this program. If not, see .
#endif
+#elif defined(ARDUINO_HELTEC_WIFI_LORA_32)
+
+// the default ESP32 Pin of 15 is the Oled SCL, set to 36 and 37 and works fine.
+// Tested on Neo6m module.
+#undef GPS_RX_PIN
+#undef GPS_TX_PIN
+#define GPS_RX_PIN 36
+#define GPS_TX_PIN 37
+
+#ifndef USE_JTAG // gpio15 is TDO for JTAG, so no I2C on this board while doing jtag
+#define I2C_SDA 4 // I2C pins for this board
+#define I2C_SCL 15
+#endif
+
+#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
+
+#define LED_PIN 25 // If defined we will blink this LED
+#define BUTTON_PIN 0 // If defined, this will be used for user button presses
+
+#define USE_RF95
+#define LORA_DIO0 26 // a No connect on the SX1262 module
+#ifndef USE_JTAG
+#define LORA_RESET 14
+#endif
+#define LORA_DIO1 33 // Not really used
+#define LORA_DIO2 32 // Not really used
+
+// ratio of voltage divider = 3.20 (R1=100k, R2=220k)
+#define ADC_MULTIPLIER 3.2
+
+// This string must exactly match the case used in release file names or the android updater won't work
+#define HW_VENDOR HardwareModel_HELTEC_V1
+
+#define BATTERY_PIN 13 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
+
#elif defined(TLORA_V1)
// This string must exactly match the case used in release file names or the android updater won't work
#define HW_VENDOR HardwareModel_TLORA_V1
diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index 502c60b02..cdf5ab1a5 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -1398,11 +1398,11 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, uptime);
-#ifndef NO_ESP32
- // Show CPU Frequency.
- display->drawString(x + SCREEN_WIDTH - display->getStringWidth("CPU " + String(getCpuFrequencyMhz()) + "MHz"),
- y + FONT_HEIGHT_SMALL * 1, "CPU " + String(getCpuFrequencyMhz()) + "MHz");
-#endif
+ // Display Channel Utilization
+ char chUtil[13];
+ sprintf(chUtil, "ChUtil %2.0f%%", airTime->channelUtilizationPercent());
+ display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil),
+ y + FONT_HEIGHT_SMALL * 1, chUtil);
// Line 3
if (radioConfig.preferences.gps_format != GpsCoordinateFormat_GpsFormatDMS) // if DMS then don't draw altitude
diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp
index f801682d1..85988ec7b 100644
--- a/src/mesh/MeshPlugin.cpp
+++ b/src/mesh/MeshPlugin.cpp
@@ -96,7 +96,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
assert(!pi.myReply); // If it is !null it means we have a bug, because it should have been sent the previous time
if (wantsPacket) {
- DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket);
+ DEBUG_MSG("Plugin '%s' wantsPacket=%d\n", pi.name, wantsPacket);
pluginFound = true;
@@ -109,7 +109,10 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
/// Also: if a packet comes in on the local PC interface, we don't check for bound channels, because it is TRUSTED and it needs to
/// to be able to fetch the initial admin packets without yet knowing any channels.
- bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (ch && (strcmp(ch->settings.name, pi.boundChannel) == 0));
+ bool rxChannelOk = !pi.boundChannel || (mp.from == 0) ||
+ !ch ||
+ strlen(ch->settings.name) > 0 ||
+ strcmp(ch->settings.name, pi.boundChannel);
if (!rxChannelOk) {
// no one should have already replied!
@@ -134,9 +137,9 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
// any other node.
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
pi.sendResponse(mp);
- DEBUG_MSG("Plugin %s sent a response\n", pi.name);
+ DEBUG_MSG("Plugin '%s' sent a response\n", pi.name);
} else {
- DEBUG_MSG("Plugin %s considered\n", pi.name);
+ DEBUG_MSG("Plugin '%s' considered\n", pi.name);
}
// If the requester didn't ask for a response we might need to discard unused replies to prevent memory leaks
@@ -147,7 +150,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
}
if (handled == ProcessMessage::STOP) {
- DEBUG_MSG("Plugin %s handled and skipped other processing\n", pi.name);
+ DEBUG_MSG("Plugin '%s' handled and skipped other processing\n", pi.name);
break;
}
}
diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp
index cb77d9ec3..4455b971c 100644
--- a/src/mesh/RadioLibInterface.cpp
+++ b/src/mesh/RadioLibInterface.cpp
@@ -101,7 +101,6 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
// Sometimes when testing it is useful to be able to never turn on the xmitter
#ifndef LORA_DISABLE_SENDING
printPacket("enqueuing for send", p);
- uint32_t xmitMsec = getPacketTime(p);
DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad);
ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN;
@@ -111,10 +110,6 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
return res;
}
- // Count the packet toward our TX airtime utilization.
- // We only count it if it can be added to the TX queue.
- airTime->logAirtime(TX_LOG, xmitMsec);
-
// We want all sending/receiving to be done by our daemon thread, We use a delay here because this packet might have been sent
// in response to a packet we just received. So we want to make sure the other side has had a chance to reconfigure its radio
startTransmitTimer(true);
@@ -188,6 +183,10 @@ void RadioLibInterface::onNotify(uint32_t notification)
MeshPacket *txp = txQueue.dequeue();
assert(txp);
startSend(txp);
+
+ // Packet has been sent, count it toward our TX airtime utilization.
+ uint32_t xmitMsec = getPacketTime(txp);
+ airTime->logAirtime(TX_LOG, xmitMsec);
}
} else {
// DEBUG_MSG("done with txqueue\n");
diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h
index 54d9e6744..de3505524 100644
--- a/src/mesh/RadioLibInterface.h
+++ b/src/mesh/RadioLibInterface.h
@@ -8,6 +8,7 @@
#define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED
#endif
+#define RADIOLIB_EXCLUDE_HTTP
#include
// ESP32 has special rules about ISR code
diff --git a/src/mesh/generated/admin.pb.h b/src/mesh/generated/admin.pb.h
index 08c647aea..ebc6fc9a0 100644
--- a/src/mesh/generated/admin.pb.h
+++ b/src/mesh/generated/admin.pb.h
@@ -86,7 +86,7 @@ extern const pb_msgdesc_t AdminMessage_msg;
#define AdminMessage_fields &AdminMessage_msg
/* Maximum encoded size of messages (where known) */
-#define AdminMessage_size 461
+#define AdminMessage_size 529
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/mesh/generated/deviceonly.pb.h b/src/mesh/generated/deviceonly.pb.h
index 2d4e53f0c..34338b9c4 100644
--- a/src/mesh/generated/deviceonly.pb.h
+++ b/src/mesh/generated/deviceonly.pb.h
@@ -125,7 +125,7 @@ extern const pb_msgdesc_t ChannelFile_msg;
/* Maximum encoded size of messages (where known) */
#define LegacyRadioConfig_size 4
#define LegacyRadioConfig_LegacyPreferences_size 2
-#define DeviceState_size 9943
+#define DeviceState_size 9967
#define ChannelFile_size 832
#ifdef __cplusplus
diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h
index 8f4a7ffbc..c5c980a35 100644
--- a/src/mesh/generated/mesh.pb.h
+++ b/src/mesh/generated/mesh.pb.h
@@ -23,6 +23,7 @@ typedef enum _HardwareModel {
HardwareModel_TLORA_V1_1p3 = 8,
HardwareModel_RAK4631 = 9,
HardwareModel_HELTEC_V2_1 = 10,
+ HardwareModel_HELTEC_V1 = 11,
HardwareModel_LORA_RELAY_V1 = 32,
HardwareModel_NRF52840DK = 33,
HardwareModel_PPR = 34,
@@ -164,6 +165,7 @@ typedef struct _MyNodeInfo {
pb_size_t air_period_rx_count;
uint32_t air_period_rx[24];
bool has_wifi;
+ float channel_utilization;
} MyNodeInfo;
typedef struct _Position {
@@ -233,6 +235,8 @@ typedef struct _MeshPacket {
MeshPacket_Priority priority;
int32_t rx_rssi;
MeshPacket_Delayed delayed;
+ uint32_t reply_id;
+ bool is_tapback;
} MeshPacket;
typedef struct _NodeInfo {
@@ -330,9 +334,9 @@ extern "C" {
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_default {0, {RouteDiscovery_init_default}}
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
-#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
+#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN, 0, 0}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
-#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0}
+#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, 0}
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
#define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}}
#define ToRadio_init_default {0, {MeshPacket_init_default}}
@@ -342,9 +346,9 @@ extern "C" {
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_zero {0, {RouteDiscovery_init_zero}}
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
-#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
+#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN, 0, 0}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
-#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0}
+#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, 0}
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
#define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}}
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
@@ -378,6 +382,7 @@ extern "C" {
#define MyNodeInfo_air_period_tx_tag 16
#define MyNodeInfo_air_period_rx_tag 17
#define MyNodeInfo_has_wifi_tag 18
+#define MyNodeInfo_channel_utilization_tag 19
#define Position_latitude_i_tag 1
#define Position_longitude_i_tag 2
#define Position_altitude_tag 3
@@ -427,6 +432,8 @@ extern "C" {
#define MeshPacket_priority_tag 12
#define MeshPacket_rx_rssi_tag 13
#define MeshPacket_delayed_tag 15
+#define MeshPacket_reply_id_tag 16
+#define MeshPacket_is_tapback_tag 17
#define NodeInfo_num_tag 1
#define NodeInfo_user_tag 2
#define NodeInfo_position_tag 3
@@ -526,7 +533,9 @@ X(a, STATIC, SINGULAR, UINT32, hop_limit, 10) \
X(a, STATIC, SINGULAR, BOOL, want_ack, 11) \
X(a, STATIC, SINGULAR, UENUM, priority, 12) \
X(a, STATIC, SINGULAR, INT32, rx_rssi, 13) \
-X(a, STATIC, SINGULAR, UENUM, delayed, 15)
+X(a, STATIC, SINGULAR, UENUM, delayed, 15) \
+X(a, STATIC, SINGULAR, FIXED32, reply_id, 16) \
+X(a, STATIC, SINGULAR, BOOL, is_tapback, 17)
#define MeshPacket_CALLBACK NULL
#define MeshPacket_DEFAULT NULL
#define MeshPacket_payloadVariant_decoded_MSGTYPE Data
@@ -559,7 +568,8 @@ X(a, STATIC, SINGULAR, UINT32, min_app_version, 14) \
X(a, STATIC, SINGULAR, UINT32, max_channels, 15) \
X(a, STATIC, REPEATED, UINT32, air_period_tx, 16) \
X(a, STATIC, REPEATED, UINT32, air_period_rx, 17) \
-X(a, STATIC, SINGULAR, BOOL, has_wifi, 18)
+X(a, STATIC, SINGULAR, BOOL, has_wifi, 18) \
+X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 19)
#define MyNodeInfo_CALLBACK NULL
#define MyNodeInfo_DEFAULT NULL
@@ -635,12 +645,12 @@ extern const pb_msgdesc_t ToRadio_PeerInfo_msg;
#define RouteDiscovery_size 40
#define Routing_size 42
#define Data_size 260
-#define MeshPacket_size 311
+#define MeshPacket_size 320
#define NodeInfo_size 270
-#define MyNodeInfo_size 445
+#define MyNodeInfo_size 451
#define LogRecord_size 81
-#define FromRadio_size 454
-#define ToRadio_size 314
+#define FromRadio_size 460
+#define ToRadio_size 323
#define ToRadio_PeerInfo_size 8
#ifdef __cplusplus
diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp
index 4174c0a23..c051e0cda 100644
--- a/src/mesh/http/ContentHandler.cpp
+++ b/src/mesh/http/ContentHandler.cpp
@@ -41,6 +41,13 @@ using namespace httpsserver;
#include "mesh/http/ContentHandler.h"
+#include
+#include
+HTTPClient http;
+
+#define DEST_FS_USES_SPIFFS
+#include
+
// We need to specify some content-type mapping, so the resources get delivered with the
// right content type and are displayed correctly in the browser
char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"},
@@ -50,9 +57,59 @@ char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"}
{".css", "text/css"}, {".ico", "image/vnd.microsoft.icon"},
{".svg", "image/svg+xml"}, {"", ""}};
+//const char *tarURL = "https://www.casler.org/temp/meshtastic-web.tar";
+const char *tarURL = "https://api-production-871d.up.railway.app/mirror/webui";
+const char *certificate = NULL; // change this as needed, leave as is for no TLS check (yolo security)
+
// Our API to handle messages to and from the radio.
HttpAPI webAPI;
+WiFiClient *getTarHTTPClientPtr(WiFiClientSecure *client, const char *url, const char *cert = NULL)
+{
+ if (cert == NULL) {
+ // New versions don't have setInsecure
+ // client->setInsecure();
+ } else {
+ client->setCACert(cert);
+ }
+ const char *UserAgent = "ESP32-HTTP-GzUpdater-Client";
+ http.setReuse(true); // handle 301 redirects gracefully
+ http.setUserAgent(UserAgent);
+ http.setConnectTimeout(10000); // 10s timeout = 10000
+ if (!http.begin(*client, url)) {
+ log_e("Can't open url %s", url);
+ return nullptr;
+ }
+ const char *headerKeys[] = {"location", "redirect", "Content-Type", "Content-Length", "Content-Disposition"};
+ const size_t numberOfHeaders = 5;
+ http.collectHeaders(headerKeys, numberOfHeaders);
+ int httpCode = http.GET();
+ // file found at server
+ if (httpCode == HTTP_CODE_FOUND || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
+ String newlocation = "";
+ String headerLocation = http.header("location");
+ String headerRedirect = http.header("redirect");
+ if (headerLocation != "") {
+ newlocation = headerLocation;
+ Serial.printf("302 (location): %s => %s\n", url, headerLocation.c_str());
+ } else if (headerRedirect != "") {
+ Serial.printf("301 (redirect): %s => %s\n", url, headerLocation.c_str());
+ newlocation = headerRedirect;
+ }
+ http.end();
+ if (newlocation != "") {
+ log_w("Found 302/301 location header: %s", newlocation.c_str());
+ return getTarHTTPClientPtr(client, newlocation.c_str(), cert);
+ } else {
+ log_e("Empty redirect !!");
+ return nullptr;
+ }
+ }
+ if (httpCode != 200)
+ return nullptr;
+ return http.getStreamPtr();
+}
+
void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
{
@@ -66,6 +123,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
ResourceNode *nodeHotspotApple = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
ResourceNode *nodeHotspotAndroid = new ResourceNode("/generate_204", "GET", &handleHotspot);
+ ResourceNode *nodeUpdateSPIFFS = new ResourceNode("/update", "GET", &handleUpdateSPIFFS);
+
ResourceNode *nodeRestart = new ResourceNode("/restart", "POST", &handleRestart);
ResourceNode *nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload);
@@ -90,7 +149,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
secureServer->registerNode(nodeJsonSpiffsBrowseStatic);
secureServer->registerNode(nodeJsonDelete);
secureServer->registerNode(nodeJsonReport);
- secureServer->registerNode(nodeRoot);
+ secureServer->registerNode(nodeUpdateSPIFFS);
+ secureServer->registerNode(nodeRoot); // This has to be last
// Insecure nodes
insecureServer->registerNode(nodeAPIv1ToRadioOptions);
@@ -105,13 +165,14 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
insecureServer->registerNode(nodeJsonSpiffsBrowseStatic);
insecureServer->registerNode(nodeJsonDelete);
insecureServer->registerNode(nodeJsonReport);
- insecureServer->registerNode(nodeRoot);
+ insecureServer->registerNode(nodeUpdateSPIFFS);
+ insecureServer->registerNode(nodeRoot); // This has to be last
}
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
{
- DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1FromRadio\n");
+ DEBUG_MSG("webAPI handleAPIv1FromRadio\n");
/*
For documentation, see:
@@ -156,12 +217,12 @@ void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
res->write(txBuf, len);
}
- DEBUG_MSG("--------------- webAPI handleAPIv1FromRadio, len %d\n", len);
+ DEBUG_MSG("webAPI handleAPIv1FromRadio, len %d\n", len);
}
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
{
- DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1ToRadio\n");
+ DEBUG_MSG("webAPI handleAPIv1ToRadio\n");
/*
For documentation, see:
@@ -188,7 +249,7 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
webAPI.handleToRadio(buffer, s);
res->write(buffer, s);
- DEBUG_MSG("--------------- webAPI handleAPIv1ToRadio\n");
+ DEBUG_MSG("webAPI handleAPIv1ToRadio\n");
}
void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
@@ -297,14 +358,21 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
file = SPIFFS.open(filenameGzip.c_str());
res->setHeader("Content-Encoding", "gzip");
if (!file.available()) {
- DEBUG_MSG("File not available\n");
+ DEBUG_MSG("File not available - %s\n", filenameGzip.c_str());
}
} else {
has_set_content_type = true;
filenameGzip = "/static/index.html.gz";
file = SPIFFS.open(filenameGzip.c_str());
- res->setHeader("Content-Encoding", "gzip");
res->setHeader("Content-Type", "text/html");
+ if (!file.available()) {
+ DEBUG_MSG("File not available - %s\n", filenameGzip.c_str());
+ res->println("Web server is running.
The content you are looking for can't be found. Please see: FAQ.
stats
Experemntal Web Content OTA Update");
+ } else {
+ res->setHeader("Content-Encoding", "gzip");
+ }
}
res->setHeader("Content-Length", httpsserver::intToString(file.size()));
@@ -502,12 +570,12 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->print("\"tx_log\": [");
- logArray = airtimeReport(TX_LOG);
- for (int i = 0; i < getPeriodsToLog(); i++) {
+ logArray = airTime->airtimeReport(TX_LOG);
+ for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
uint32_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
- if (i != getPeriodsToLog() - 1) {
+ if (i != airTime->getPeriodsToLog() - 1) {
res->print(", ");
}
}
@@ -515,12 +583,12 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->println("],");
res->print("\"rx_log\": [");
- logArray = airtimeReport(RX_LOG);
- for (int i = 0; i < getPeriodsToLog(); i++) {
+ logArray = airTime->airtimeReport(RX_LOG);
+ for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
uint32_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
- if (i != getPeriodsToLog() - 1) {
+ if (i != airTime->getPeriodsToLog() - 1) {
res->print(", ");
}
}
@@ -528,20 +596,20 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->println("],");
res->print("\"rx_all_log\": [");
- logArray = airtimeReport(RX_ALL_LOG);
- for (int i = 0; i < getPeriodsToLog(); i++) {
+ logArray = airTime->airtimeReport(RX_ALL_LOG);
+ for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
uint32_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
- if (i != getPeriodsToLog() - 1) {
+ if (i != airTime->getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
- res->printf("\"seconds_since_boot\": %u,\n", getSecondsSinceBoot());
- res->printf("\"seconds_per_period\": %u,\n", getSecondsPerPeriod());
- res->printf("\"periods_to_log\": %u\n", getPeriodsToLog());
+ res->printf("\"seconds_since_boot\": %u,\n", airTime->getSecondsSinceBoot());
+ res->printf("\"seconds_per_period\": %u,\n", airTime->getSecondsPerPeriod());
+ res->printf("\"periods_to_log\": %u\n", airTime->getPeriodsToLog());
res->println("},");
@@ -576,6 +644,7 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->println("},");
res->println("\"device\": {");
+ res->printf("\"channel_utilization\": %3.2f%,\n", airTime->channelUtilizationPercent());
res->printf("\"reboot_counter\": %d\n", myNodeInfo.reboot_count);
res->println("},");
@@ -612,6 +681,75 @@ void handleHotspot(HTTPRequest *req, HTTPResponse *res)
res->println("\n");
}
+void handleUpdateSPIFFS(HTTPRequest *req, HTTPResponse *res)
+{
+ res->setHeader("Content-Type", "text/html");
+ res->setHeader("Access-Control-Allow-Origin", "*");
+ res->setHeader("Access-Control-Allow-Methods", "GET");
+
+ res->println("Downloading Meshtastic Web Content...");
+
+ File root = SPIFFS.open("/");
+ File file = root.openNextFile();
+
+ DEBUG_MSG("Deleting files from /static\n");
+
+ while (file) {
+ String filePath = String(file.name());
+ if (filePath.indexOf("/static") == 0) {
+ DEBUG_MSG("%s\n", file.name());
+ SPIFFS.remove(file.name());
+ }
+ file = root.openNextFile();
+ }
+
+ // return;
+
+ WiFiClientSecure *client = new WiFiClientSecure;
+ Stream *streamptr = getTarHTTPClientPtr(client, tarURL, certificate);
+
+ if (streamptr != nullptr) {
+
+ TarUnpacker *TARUnpacker = new TarUnpacker();
+ TARUnpacker->haltOnError(false); // stop on fail (manual restart/reset required)
+ TARUnpacker->setTarVerify(false); // true = enables health checks but slows down the overall process
+ TARUnpacker->setupFSCallbacks(targzTotalBytesFn, targzFreeBytesFn); // prevent the partition from exploding, recommended
+ TARUnpacker->setLoggerCallback(BaseUnpacker::targzPrintLoggerCallback); // gz log verbosity
+ TARUnpacker->setTarProgressCallback(
+ BaseUnpacker::defaultProgressCallback); // prints the untarring progress for each individual file
+ TARUnpacker->setTarStatusProgressCallback(
+ BaseUnpacker::defaultTarStatusProgressCallback); // print the filenames as they're expanded
+ TARUnpacker->setTarMessageCallback(BaseUnpacker::targzPrintLoggerCallback); // tar log verbosity
+
+ String contentLengthStr = http.header("Content-Length");
+ contentLengthStr.trim();
+ int64_t streamSize = -1;
+ if (contentLengthStr != "") {
+ streamSize = atoi(contentLengthStr.c_str());
+ Serial.printf("Stream size %d\n", streamSize);
+ res->printf("Stream size %d\n", streamSize);
+ }
+
+ if (!TARUnpacker->tarStreamExpander(streamptr, streamSize, SPIFFS, "/static")) {
+ Serial.printf("tarStreamExpander failed with return code #%d\n", TARUnpacker->tarGzGetError());
+ } else {
+ // print leftover bytes if any (probably zero-fill from the server)
+ while (http.connected()) {
+ size_t streamSize = streamptr->available();
+ if (streamSize) {
+ Serial.printf("%02x ", streamptr->read());
+ } else
+ break;
+ }
+ }
+
+ } else {
+ Serial.println("Failed to establish http connection");
+ }
+
+ res->println("Done");
+}
+
void handleRestart(HTTPRequest *req, HTTPResponse *res)
{
res->setHeader("Content-Type", "text/html");
diff --git a/src/mesh/http/ContentHandler.h b/src/mesh/http/ContentHandler.h
index cc07cb833..f456d0a1f 100644
--- a/src/mesh/http/ContentHandler.h
+++ b/src/mesh/http/ContentHandler.h
@@ -1,5 +1,6 @@
#pragma once
+
void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer);
// Declare some handler functions for the various URLs on the server
@@ -14,9 +15,7 @@ void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res);
void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res);
void handleReport(HTTPRequest *req, HTTPResponse *res);
-
-void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function next);
-void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function next);
+void handleUpdateSPIFFS(HTTPRequest *req, HTTPResponse *res);
// Interface to the PhoneAPI to access the protobufs with messages
class HttpAPI : public PhoneAPI
diff --git a/src/mesh/http/WiFiAPClient.cpp b/src/mesh/http/WiFiAPClient.cpp
index b1d1ba917..2037d06eb 100644
--- a/src/mesh/http/WiFiAPClient.cpp
+++ b/src/mesh/http/WiFiAPClient.cpp
@@ -164,20 +164,20 @@ static void onNetworkConnected()
// Startup WiFi
bool initWifi(bool forceSoftAP)
{
- if (forceSoftAP) {
- DEBUG_MSG("WiFi ... Forced AP Mode\n");
- } else if (radioConfig.preferences.wifi_ap_mode) {
- DEBUG_MSG("WiFi ... AP Mode\n");
- } else {
- DEBUG_MSG("WiFi ... Client Mode\n");
- }
-
forcedSoftAP = forceSoftAP;
if ((radioConfig.has_preferences && radioConfig.preferences.wifi_ssid[0]) || forceSoftAP) {
const char *wifiName = radioConfig.preferences.wifi_ssid;
const char *wifiPsw = radioConfig.preferences.wifi_password;
+ if (forceSoftAP) {
+ DEBUG_MSG("WiFi ... Forced AP Mode\n");
+ } else if (radioConfig.preferences.wifi_ap_mode) {
+ DEBUG_MSG("WiFi ... AP Mode\n");
+ } else {
+ DEBUG_MSG("WiFi ... Client Mode\n");
+ }
+
createSSLCert();
if (!*wifiPsw) // Treat empty password as no password
diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 6235f37f7..26ef2a3e7 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -95,7 +95,7 @@ void MQTT::reconnect()
}
pubSub.setServer(serverAddr, serverPort);
- DEBUG_MSG("Connecting to MQTT server %s, port: %d, username: %s, password %s\n", serverAddr, serverPort, mqttUsername, mqttPassword);
+ DEBUG_MSG("Connecting to MQTT server %s, port: %d, username: %s, password: %s\n", serverAddr, serverPort, mqttUsername, mqttPassword);
auto myStatus = (statusTopic + owner.id);
bool connected = pubSub.connect(owner.id, mqttUsername, mqttPassword, myStatus.c_str(), 1, true, "offline");
if (connected) {
diff --git a/src/plugins/PositionPlugin.cpp b/src/plugins/PositionPlugin.cpp
index e10f05d1e..0a3a412d7 100644
--- a/src/plugins/PositionPlugin.cpp
+++ b/src/plugins/PositionPlugin.cpp
@@ -127,7 +127,7 @@ int32_t PositionPlugin::runOnce()
{
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
- radioConfig.preferences.position_broadcast_smart = true;
+ //radioConfig.preferences.position_broadcast_smart = true;
// We limit our GPS broadcasts to a max rate
uint32_t now = millis();
diff --git a/src/plugins/esp32/StoreForwardPlugin.cpp b/src/plugins/esp32/StoreForwardPlugin.cpp
index 10dedda41..f613fc7c0 100644
--- a/src/plugins/esp32/StoreForwardPlugin.cpp
+++ b/src/plugins/esp32/StoreForwardPlugin.cpp
@@ -3,6 +3,7 @@
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
+#include "airtime.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include "mesh/generated/storeforward.pb.h"
@@ -22,20 +23,29 @@ int32_t StoreForwardPlugin::runOnce()
if (radioConfig.preferences.is_router) {
+ // Send out the message queue.
if (this->busy) {
- // Send out the message queue.
+
- // DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index);
- storeForwardPlugin->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
+ // Only send packets if the channel is less than 25% utilized.
+ if (airTime->channelUtilizationPercent() < 25) {
- if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
- strcpy(this->routerMessage, "** S&F - Done");
- storeForwardPlugin->sendMessage(this->busyTo, this->routerMessage);
- // DEBUG_MSG("--- --- --- In busy loop - Done \n");
- this->packetHistoryTXQueue_index = 0;
- this->busy = false;
+ // DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index);
+ storeForwardPlugin->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
+
+ if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
+ strcpy(this->routerMessage, "** S&F - Done");
+ storeForwardPlugin->sendMessage(this->busyTo, this->routerMessage);
+
+ // DEBUG_MSG("--- --- --- In busy loop - Done \n");
+ this->packetHistoryTXQueue_index = 0;
+ this->busy = false;
+ } else {
+ this->packetHistoryTXQueue_index++;
+ }
+
} else {
- this->packetHistoryTXQueue_index++;
+ DEBUG_MSG("Channel utilization is too high. Skipping this opportunity to send and will retry later.\n");
}
}
DEBUG_MSG("SF myNodeInfo.bitrate = %f bytes / sec\n", myNodeInfo.bitrate);
@@ -414,9 +424,6 @@ StoreForwardPlugin::StoreForwardPlugin()
// Popupate PSRAM with our data structures.
this->populatePSRAM();
- // this->packetTimeMax = 2000;
- // DEBUG_MSG("SF Time to Transmit maxPacketSize (%d bytes) %d ms\n", maxPacketSize, this->packetTimeMax);
-
} else {
DEBUG_MSG("Device has less than 1M of PSRAM free. Aborting startup.\n");
DEBUG_MSG("Store & Forward Plugin - Aborting Startup.\n");
diff --git a/src/sleep.cpp b/src/sleep.cpp
index 558dbf1d4..998645dcd 100644
--- a/src/sleep.cpp
+++ b/src/sleep.cpp
@@ -65,7 +65,11 @@ void setCPUFast(bool on)
return;
}
- setCpuFrequencyMhz(on ? 240 : 80);
+ // The Heltec LORA32 V1 runs at 26 MHz base frequency and doesn't react well to switching to 80 MHz...
+ #ifndef ARDUINO_HELTEC_WIFI_LORA_32
+ setCpuFrequencyMhz(on ? 240 : 80);
+ #endif
+
#endif
}
diff --git a/version.properties b/version.properties
index 13eff275e..7c9e94a60 100644
--- a/version.properties
+++ b/version.properties
@@ -1,4 +1,4 @@
[VERSION]
major = 1
minor = 2
-build = 49
+build = 50