diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml
index 87ba8cf85..da619fe9f 100644
--- a/.github/workflows/main_matrix.yml
+++ b/.github/workflows/main_matrix.yml
@@ -32,7 +32,11 @@ jobs:
name: Checkout base
- id: jsonStep
run: |
- TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} quick)
+ if [[ "${{ github.ref }}" == "refs/heads/master" ]]; then
+ TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
+ else
+ TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} quick)
+ fi
echo "$TARGETS"
echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
outputs:
diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml
index b19f96bb5..79cd91af5 100644
--- a/.trunk/trunk.yaml
+++ b/.trunk/trunk.yaml
@@ -8,7 +8,7 @@ plugins:
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- - trufflehog@3.82.4
+ - trufflehog@3.82.5
- yamllint@1.35.1
- bandit@1.7.10
- checkov@3.2.255
diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml
index 5590ed3b0..3a470b7bb 100644
--- a/bin/config-dist.yaml
+++ b/bin/config-dist.yaml
@@ -9,6 +9,7 @@ Lora:
# IRQ: 16
# Busy: 20
# Reset: 18
+# SX126X_ANT_SW: 6
# Module: sx1262 # Waveshare SX1302 LISTEN ONLY AT THIS TIME!
# CS: 7
@@ -153,4 +154,4 @@ Webserver:
General:
MaxNodes: 200
- MaxMessageQueue: 100
+ MaxMessageQueue: 100
\ No newline at end of file
diff --git a/platformio.ini b/platformio.ini
index 603db4944..f528a6fde 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -81,6 +81,7 @@ build_flags = -Wno-missing-field-initializers
-DRADIOLIB_EXCLUDE_APRS
-DRADIOLIB_EXCLUDE_LORAWAN
-DMESHTASTIC_EXCLUDE_DROPZONE=1
+ -DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1
-DBUILD_EPOCH=$UNIX_TIME
;-D OLED_PL
diff --git a/src/Power.cpp b/src/Power.cpp
index c71d17586..6ed937648 100644
--- a/src/Power.cpp
+++ b/src/Power.cpp
@@ -1068,10 +1068,9 @@ bool Power::axpChipInit()
#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
/**
- * Wrapper class for an I2C MAX17048 Lipo battery sensor. If there is no
- * I2C sensor present, the class falls back to analog battery sensing
+ * Wrapper class for an I2C MAX17048 Lipo battery sensor.
*/
-class LipoBatteryLevel : public AnalogBatteryLevel
+class LipoBatteryLevel : public HasBatteryLevel
{
private:
MAX17048Singleton *max17048 = nullptr;
@@ -1096,52 +1095,27 @@ class LipoBatteryLevel : public AnalogBatteryLevel
/**
* Battery state of charge, from 0 to 100 or -1 for unknown
*/
- virtual int getBatteryPercent() override
- {
- if (!max17048->isInitialised())
- return AnalogBatteryLevel::getBatteryPercent();
- return max17048->getBusBatteryPercent();
- }
+ virtual int getBatteryPercent() override { return max17048->getBusBatteryPercent(); }
/**
* The raw voltage of the battery in millivolts, or NAN if unknown
*/
- virtual uint16_t getBattVoltage() override
- {
- if (!max17048->isInitialised())
- return AnalogBatteryLevel::getBattVoltage();
- return max17048->getBusVoltageMv();
- }
+ virtual uint16_t getBattVoltage() override { return max17048->getBusVoltageMv(); }
/**
* return true if there is a battery installed in this unit
*/
- virtual bool isBatteryConnect() override
- {
- if (!max17048->isInitialised())
- return AnalogBatteryLevel::isBatteryConnect();
- return max17048->isBatteryConnected();
- }
+ virtual bool isBatteryConnect() override { return max17048->isBatteryConnected(); }
/**
* return true if there is an external power source detected
*/
- virtual bool isVbusIn() override
- {
- if (!max17048->isInitialised())
- return AnalogBatteryLevel::isVbusIn();
- return max17048->isExternallyPowered();
- }
+ virtual bool isVbusIn() override { return max17048->isExternallyPowered(); }
/**
* return true if the battery is currently charging
*/
- virtual bool isCharging() override
- {
- if (!max17048->isInitialised())
- return AnalogBatteryLevel::isCharging();
- return max17048->isBatteryCharging();
- }
+ virtual bool isCharging() override { return max17048->isBatteryCharging(); }
};
LipoBatteryLevel lipoLevel;
@@ -1153,6 +1127,8 @@ bool Power::lipoInit()
{
bool result = lipoLevel.runOnce();
LOG_DEBUG("Power::lipoInit lipo sensor is %s\n", result ? "ready" : "not ready yet");
+ if (!result)
+ return false;
batteryLevel = &lipoLevel;
return true;
}
diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp
index 66859ce95..b6d2776bc 100644
--- a/src/gps/GPS.cpp
+++ b/src/gps/GPS.cpp
@@ -917,20 +917,22 @@ void GPS::down()
softsleepSupported = true;
#endif
- // How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP?
- // Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050
- // https://www.desmos.com/calculator/6gvjghoumr
- // This is not particularly accurate, but probably an impromevement over a single, fixed threshold
- uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22));
- LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep\n", hardsleepThreshold / 1000);
-
- // If update interval too short: softsleep (if supported by hardware)
- if (softsleepSupported && updateInterval < hardsleepThreshold)
- setPowerState(GPS_SOFTSLEEP, sleepTime);
+ if (softsleepSupported) {
+ // How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP?
+ // Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050
+ // https://www.desmos.com/calculator/6gvjghoumr
+ // This is not particularly accurate, but probably an impromevement over a single, fixed threshold
+ uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22));
+ LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep\n", hardsleepThreshold / 1000);
+ // If update interval too short: softsleep (if supported by hardware)
+ if (updateInterval < hardsleepThreshold) {
+ setPowerState(GPS_SOFTSLEEP, sleepTime);
+ return;
+ }
+ }
// If update interval long enough (or softsleep unsupported): hardsleep instead
- else
- setPowerState(GPS_HARDSLEEP, sleepTime);
+ setPowerState(GPS_HARDSLEEP, sleepTime);
}
}
diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index 531ee1b56..4f6a5a45f 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -58,10 +58,11 @@ along with this program. If not, see .
#ifdef ARCH_ESP32
#include "esp_task_wdt.h"
-#include "modules/esp32/StoreForwardModule.h"
+#include "modules/StoreForwardModule.h"
#endif
#if ARCH_PORTDUINO
+#include "modules/StoreForwardModule.h"
#include "platform/portduino/PortduinoGlue.h"
#endif
@@ -2822,4 +2823,4 @@ int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
} // namespace graphics
#else
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
-#endif // HAS_SCREEN
+#endif // HAS_SCREEN
\ No newline at end of file
diff --git a/src/memGet.cpp b/src/memGet.cpp
index e982ef7ee..ef1102f1e 100644
--- a/src/memGet.cpp
+++ b/src/memGet.cpp
@@ -57,6 +57,8 @@ uint32_t MemGet::getFreePsram()
{
#ifdef ARCH_ESP32
return ESP.getFreePsram();
+#elif defined(ARCH_PORTDUINO)
+ return 4194252;
#else
return 0;
#endif
@@ -71,6 +73,8 @@ uint32_t MemGet::getPsramSize()
{
#ifdef ARCH_ESP32
return ESP.getPsramSize();
+#elif defined(ARCH_PORTDUINO)
+ return 4194252;
#else
return 0;
#endif
diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h
index e78c2be2c..b91fedada 100644
--- a/src/mesh/MeshService.h
+++ b/src/mesh/MeshService.h
@@ -13,9 +13,9 @@
#if defined(ARCH_PORTDUINO) && !HAS_RADIO
#include "../platform/portduino/SimRadio.h"
#endif
-#ifdef ARCH_ESP32
+#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
-#include "modules/esp32/StoreForwardModule.h"
+#include "modules/StoreForwardModule.h"
#endif
#endif
diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 91963d51f..7760ae0e1 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -32,12 +32,13 @@
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
-#include "modules/esp32/StoreForwardModule.h"
+#include "modules/StoreForwardModule.h"
#include
#include
#endif
#ifdef ARCH_PORTDUINO
+#include "modules/StoreForwardModule.h"
#include "platform/portduino/PortduinoGlue.h"
#endif
diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h
index c3ebf3c6e..1be61759a 100644
--- a/src/mesh/NodeDB.h
+++ b/src/mesh/NodeDB.h
@@ -8,6 +8,7 @@
#include "MeshTypes.h"
#include "NodeStatus.h"
+#include "configuration.h"
#include "mesh-pb-constants.h"
#include "mesh/generated/meshtastic/mesh.pb.h" // For CriticalErrorCode
diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp
index 103572990..ecc5effe9 100644
--- a/src/mesh/PhoneAPI.cpp
+++ b/src/mesh/PhoneAPI.cpp
@@ -8,6 +8,7 @@
#include "FSCommon.h"
#include "MeshService.h"
#include "NodeDB.h"
+#include "PacketHistory.h"
#include "PhoneAPI.h"
#include "PowerFSM.h"
#include "RadioInterface.h"
@@ -31,6 +32,7 @@
PhoneAPI::PhoneAPI()
{
lastContactMsec = millis();
+ std::fill(std::begin(recentToRadioPacketIds), std::end(recentToRadioPacketIds), 0);
}
PhoneAPI::~PhoneAPI()
@@ -109,8 +111,6 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep
lastContactMsec = millis();
- // return (lastContactMsec != 0) &&
-
memset(&toRadioScratch, 0, sizeof(toRadioScratch));
if (pb_decode_from_bytes(buf, bufLength, &meshtastic_ToRadio_msg, &toRadioScratch)) {
switch (toRadioScratch.which_payload_variant) {
@@ -572,12 +572,35 @@ void PhoneAPI::sendNotification(meshtastic_LogRecord_Level level, uint32_t reply
service->sendClientNotification(cn);
}
+bool PhoneAPI::wasSeenRecently(uint32_t id)
+{
+ for (int i = 0; i < 20; i++) {
+ if (recentToRadioPacketIds[i] == id) {
+ return true;
+ }
+ if (recentToRadioPacketIds[i] == 0) {
+ recentToRadioPacketIds[i] = id;
+ return false;
+ }
+ }
+ // If the array is full, shift all elements to the left and add the new id at the end
+ memmove(recentToRadioPacketIds, recentToRadioPacketIds + 1, (19) * sizeof(uint32_t));
+ recentToRadioPacketIds[19] = id;
+ return false;
+}
+
/**
* Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool
*/
bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p)
{
printPacket("PACKET FROM PHONE", &p);
+
+ if (p.id > 0 && wasSeenRecently(p.id)) {
+ LOG_DEBUG("Ignoring packet from phone, already seen recently\n");
+ return false;
+ }
+
if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] &&
Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], THIRTY_SECONDS_MS)) {
LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum);
@@ -586,7 +609,8 @@ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p)
} else if (p.decoded.portnum == meshtastic_PortNum_POSITION_APP && lastPortNumToRadio[p.decoded.portnum] &&
Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], FIVE_SECONDS_MS)) {
LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum);
- sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "Position can only be sent once every 5 seconds");
+ // FIXME: Figure out why this continues to happen
+ // sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "Position can only be sent once every 5 seconds");
return false;
}
lastPortNumToRadio[p.decoded.portnum] = millis();
diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h
index cf6f55416..3247fee5c 100644
--- a/src/mesh/PhoneAPI.h
+++ b/src/mesh/PhoneAPI.h
@@ -52,6 +52,7 @@ class PhoneAPI
// Hashmap of timestamps for last time we received a packet on the API per portnum
std::unordered_map lastPortNumToRadio;
+ uint32_t recentToRadioPacketIds[20]; // Last 20 ToRadio MeshPacket IDs we have seen
/**
* Each packet sent to the phone has an incrementing count
@@ -159,6 +160,8 @@ class PhoneAPI
/// begin a new connection
void handleStartConfig();
+ bool wasSeenRecently(uint32_t packetId);
+
/**
* Handle a packet that the phone wants us to send. We can write to it but can not keep a reference to it
* @return true true if a packet was queued for sending
diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp
index ad1ceeeeb..30024daf0 100644
--- a/src/mesh/SX126xInterface.cpp
+++ b/src/mesh/SX126xInterface.cpp
@@ -52,6 +52,10 @@ template bool SX126xInterface::init()
float tcxoVoltage = 0;
if (settingsMap[dio3_tcxo_voltage])
tcxoVoltage = 1.8;
+ if (settingsMap[sx126x_ant_sw] != RADIOLIB_NC) {
+ digitalWrite(settingsMap[sx126x_ant_sw], HIGH);
+ pinMode(settingsMap[sx126x_ant_sw], OUTPUT);
+ }
// FIXME: correct logic to default to not using TCXO if no voltage is specified for SX126X_DIO3_TCXO_VOLTAGE
#elif !defined(SX126X_DIO3_TCXO_VOLTAGE)
float tcxoVoltage =
@@ -99,21 +103,19 @@ template bool SX126xInterface::init()
LOG_DEBUG("Current limit set to %f\n", currentLimit);
LOG_DEBUG("Current limit set result %d\n", res);
-#ifdef SX126X_DIO2_AS_RF_SWITCH
- LOG_DEBUG("Setting DIO2 as RF switch\n");
- bool dio2AsRfSwitch = true;
-#elif defined(ARCH_PORTDUINO)
- bool dio2AsRfSwitch = false;
- if (settingsMap[dio2_as_rf_switch]) {
- LOG_DEBUG("Setting DIO2 as RF switch\n");
- dio2AsRfSwitch = true;
- }
-#else
- LOG_DEBUG("Setting DIO2 as not RF switch\n");
- bool dio2AsRfSwitch = false;
-#endif
if (res == RADIOLIB_ERR_NONE) {
+#ifdef SX126X_DIO2_AS_RF_SWITCH
+ bool dio2AsRfSwitch = true;
+#elif defined(ARCH_PORTDUINO)
+ bool dio2AsRfSwitch = false;
+ if (settingsMap[dio2_as_rf_switch]) {
+ dio2AsRfSwitch = true;
+ }
+#else
+ bool dio2AsRfSwitch = false;
+#endif
res = lora.setDio2AsRfSwitch(dio2AsRfSwitch);
+ LOG_DEBUG("Set DIO2 as %sRF switch, result: %d\n", dio2AsRfSwitch ? "" : "not ", res);
}
// If a pin isn't defined, we set it to RADIOLIB_NC, it is safe to always do external RF switching with RADIOLIB_NC as it has
diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp
index eca0e8ac9..eedb3a37d 100644
--- a/src/modules/Modules.cpp
+++ b/src/modules/Modules.cpp
@@ -47,6 +47,9 @@
#endif
#if ARCH_PORTDUINO
#include "input/LinuxInputImpl.h"
+#if !MESHTASTIC_EXCLUDE_STOREFORWARD
+#include "modules/StoreForwardModule.h"
+#endif
#endif
#if HAS_TELEMETRY
#include "modules/Telemetry/DeviceTelemetry.h"
@@ -67,7 +70,7 @@
#include "modules/esp32/PaxcounterModule.h"
#endif
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
-#include "modules/esp32/StoreForwardModule.h"
+#include "modules/StoreForwardModule.h"
#endif
#endif
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)
diff --git a/src/modules/RemoteHardwareModule.cpp b/src/modules/RemoteHardwareModule.cpp
index f6b8b2e90..43612e450 100644
--- a/src/modules/RemoteHardwareModule.cpp
+++ b/src/modules/RemoteHardwareModule.cpp
@@ -15,26 +15,44 @@
// a max of one change per 30 seconds
#define WATCH_INTERVAL_MSEC (30 * 1000)
+// Tests for access to read from or write to a specified GPIO pin
+static bool pinAccessAllowed(uint64_t mask, uint8_t pin)
+{
+ // If undefined pin access is allowed, don't check the pin and just return true
+ if (moduleConfig.remote_hardware.allow_undefined_pin_access) {
+ return true;
+ }
+
+ // Test to see if the pin is in the list of allowed pins and return true if found
+ if (mask & (1ULL << pin)) {
+ return true;
+ }
+
+ return false;
+}
+
/// Set pin modes for every set bit in a mask
-static void pinModes(uint64_t mask, uint8_t mode)
+static void pinModes(uint64_t mask, uint8_t mode, uint64_t maskAvailable)
{
for (uint64_t i = 0; i < NUM_GPIOS; i++) {
if (mask & (1ULL << i)) {
- pinMode(i, mode);
+ if (pinAccessAllowed(maskAvailable, i)) {
+ pinMode(i, mode);
+ }
}
}
}
/// Read all the pins mentioned in a mask
-static uint64_t digitalReads(uint64_t mask)
+static uint64_t digitalReads(uint64_t mask, uint64_t maskAvailable)
{
uint64_t res = 0;
- pinModes(mask, INPUT_PULLUP);
+ pinModes(mask, INPUT_PULLUP, maskAvailable);
for (uint64_t i = 0; i < NUM_GPIOS; i++) {
uint64_t m = 1ULL << i;
- if (mask & m) {
+ if (mask & m && pinAccessAllowed(maskAvailable, i)) {
if (digitalRead(i)) {
res |= m;
}
@@ -50,6 +68,11 @@ RemoteHardwareModule::RemoteHardwareModule()
{
// restrict to the gpio channel for rx
boundChannel = Channels::gpioChannel;
+
+ // Pull available pin allowlist from config and build a bitmask out of it for fast comparisons later
+ for (uint8_t i = 0; i < 4; i++) {
+ availablePins += 1ULL << moduleConfig.remote_hardware.available_pins[i].gpio_pin;
+ }
}
bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req, meshtastic_HardwareMessage *pptr)
@@ -63,10 +86,10 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r
// Print notification to LCD screen
screen->print("Write GPIOs\n");
- pinModes(p.gpio_mask, OUTPUT);
+ pinModes(p.gpio_mask, OUTPUT, availablePins);
for (uint8_t i = 0; i < NUM_GPIOS; i++) {
uint64_t mask = 1ULL << i;
- if (p.gpio_mask & mask) {
+ if (p.gpio_mask & mask && pinAccessAllowed(availablePins, i)) {
digitalWrite(i, (p.gpio_value & mask) ? 1 : 0);
}
}
@@ -79,7 +102,7 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r
if (screen)
screen->print("Read GPIOs\n");
- uint64_t res = digitalReads(p.gpio_mask);
+ uint64_t res = digitalReads(p.gpio_mask, availablePins);
// Send the reply
meshtastic_HardwareMessage r = meshtastic_HardwareMessage_init_default;
@@ -121,7 +144,7 @@ int32_t RemoteHardwareModule::runOnce()
if (moduleConfig.remote_hardware.enabled && watchGpios) {
if (!Throttle::isWithinTimespanMs(lastWatchMsec, WATCH_INTERVAL_MSEC)) {
- uint64_t curVal = digitalReads(watchGpios);
+ uint64_t curVal = digitalReads(watchGpios, availablePins);
lastWatchMsec = millis();
if (curVal != previousWatch) {
diff --git a/src/modules/RemoteHardwareModule.h b/src/modules/RemoteHardwareModule.h
index dd39f5b69..4dc31d405 100644
--- a/src/modules/RemoteHardwareModule.h
+++ b/src/modules/RemoteHardwareModule.h
@@ -17,6 +17,9 @@ class RemoteHardwareModule : public ProtobufModule,
/// The timestamp of our last watch event (we throttle watches to 1 change every 30 seconds)
uint32_t lastWatchMsec = 0;
+ /// A bitmask of GPIOs that are exposed to the mesh if undefined access is not enabled
+ uint64_t availablePins = 0;
+
public:
/** Constructor
* name is for debugging output
diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/StoreForwardModule.cpp
similarity index 98%
rename from src/modules/esp32/StoreForwardModule.cpp
rename to src/modules/StoreForwardModule.cpp
index 51ec2a942..29fbd8d92 100644
--- a/src/modules/esp32/StoreForwardModule.cpp
+++ b/src/modules/StoreForwardModule.cpp
@@ -35,7 +35,7 @@ uint32_t heartbeatInterval = 60; // Default to 60 seconds, adjust as needed
int32_t StoreForwardModule::runOnce()
{
-#ifdef ARCH_ESP32
+#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
if (moduleConfig.store_forward.enabled && is_server) {
// Send out the message queue.
if (this->busy) {
@@ -82,8 +82,12 @@ void StoreForwardModule::populatePSRAM()
uint32_t numberOfPackets =
(this->records ? this->records : (((memGet.getFreePsram() / 3) * 2) / sizeof(PacketHistoryStruct)));
this->records = numberOfPackets;
-
+#if defined(ARCH_ESP32)
this->packetHistory = static_cast(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
+#elif defined(ARCH_PORTDUINO)
+ this->packetHistory = static_cast(calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
+
+#endif
LOG_DEBUG("*** After PSRAM initialization: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(),
memGet.getFreePsram(), memGet.getPsramSize());
@@ -376,7 +380,7 @@ void StoreForwardModule::statsSend(uint32_t to)
*/
ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &mp)
{
-#ifdef ARCH_ESP32
+#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
if (moduleConfig.store_forward.enabled) {
if ((mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) && is_server) {
@@ -559,7 +563,7 @@ StoreForwardModule::StoreForwardModule()
ProtobufModule("StoreForward", meshtastic_PortNum_STORE_FORWARD_APP, &meshtastic_StoreAndForward_msg)
{
-#ifdef ARCH_ESP32
+#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
isPromiscuous = true; // Brown chicken brown cow
diff --git a/src/modules/esp32/StoreForwardModule.h b/src/modules/StoreForwardModule.h
similarity index 100%
rename from src/modules/esp32/StoreForwardModule.h
rename to src/modules/StoreForwardModule.h
diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp
index 03fa80415..eedfe1a71 100644
--- a/src/nimble/NimbleBluetooth.cpp
+++ b/src/nimble/NimbleBluetooth.cpp
@@ -44,6 +44,9 @@ static BluetoothPhoneAPI *bluetoothPhoneAPI;
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
*/
+// Last ToRadio value received from the phone
+static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE];
+
class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
{
virtual void onWrite(NimBLECharacteristic *pCharacteristic)
@@ -51,7 +54,13 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
LOG_INFO("To Radio onwrite\n");
auto val = pCharacteristic->getValue();
- bluetoothPhoneAPI->handleToRadio(val.data(), val.length());
+ if (memcmp(lastToRadio, val.data(), val.length()) != 0) {
+ LOG_DEBUG("New ToRadio packet\n");
+ memcpy(lastToRadio, val.data(), val.length());
+ bluetoothPhoneAPI->handleToRadio(val.data(), val.length());
+ } else {
+ LOG_DEBUG("Dropping duplicate ToRadio packet we just saw\n");
+ }
}
};
diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp
index ec3ff3e8d..fbc0728d7 100644
--- a/src/platform/nrf52/NRF52Bluetooth.cpp
+++ b/src/platform/nrf52/NRF52Bluetooth.cpp
@@ -141,10 +141,19 @@ void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_e
}
authorizeRead(conn_hdl);
}
+// Last ToRadio value received from the phone
+static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE];
+
void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len)
{
LOG_INFO("toRadioWriteCb data %p, len %u\n", data, len);
- bluetoothPhoneAPI->handleToRadio(data, len);
+ if (memcmp(lastToRadio, data, len) != 0) {
+ LOG_DEBUG("New ToRadio packet\n");
+ memcpy(lastToRadio, data, len);
+ bluetoothPhoneAPI->handleToRadio(data, len);
+ } else {
+ LOG_DEBUG("Dropping duplicate ToRadio packet we just saw\n");
+ }
}
void setupMeshService(void)
diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index dc143c661..44f4d5227 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -80,6 +80,7 @@ void portduinoSetup()
irq,
busy,
reset,
+ sx126x_ant_sw,
txen,
rxen,
displayDC,
@@ -180,6 +181,7 @@ void portduinoSetup()
settingsMap[reset] = yamlConfig["Lora"]["Reset"].as(RADIOLIB_NC);
settingsMap[txen] = yamlConfig["Lora"]["TXen"].as(RADIOLIB_NC);
settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as(RADIOLIB_NC);
+ settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as(RADIOLIB_NC);
settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0);
settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as(false);
settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as(2000000);
@@ -305,6 +307,8 @@ void portduinoSetup()
gpioInit(max_GPIO + 1); // Done here so we can inform Portduino how many GPIOs we need.
// Need to bind all the configured GPIO pins so they're not simulated
+ // TODO: Can we do this in the for loop above?
+ // TODO: If one of these fails, we should log and terminate
if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) {
settingsMap[cs] = RADIOLIB_NC;
@@ -325,6 +329,11 @@ void portduinoSetup()
settingsMap[reset] = RADIOLIB_NC;
}
}
+ if (settingsMap.count(sx126x_ant_sw) > 0 && settingsMap[sx126x_ant_sw] != RADIOLIB_NC) {
+ if (initGPIOPin(settingsMap[sx126x_ant_sw], gpioChipName) != ERRNO_OK) {
+ settingsMap[sx126x_ant_sw] = RADIOLIB_NC;
+ }
+ }
if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) {
settingsMap[user] = RADIOLIB_NC;
@@ -391,4 +400,4 @@ int initGPIOPin(int pinNum, const std::string gpioChipName)
#else
return ERRNO_OK;
#endif
-}
+}
\ No newline at end of file
diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h
index 3fee1db40..59956cb59 100644
--- a/src/platform/portduino/PortduinoGlue.h
+++ b/src/platform/portduino/PortduinoGlue.h
@@ -8,6 +8,7 @@ enum configNames {
irq,
busy,
reset,
+ sx126x_ant_sw,
txen,
rxen,
dio2_as_rf_switch,
@@ -63,4 +64,4 @@ enum { level_error, level_warn, level_info, level_debug, level_trace };
extern std::map settingsMap;
extern std::map settingsStrings;
extern std::ofstream traceFile;
-int initGPIOPin(int pinNum, std::string gpioChipname);
+int initGPIOPin(int pinNum, std::string gpioChipname);
\ No newline at end of file