Merge branch 'master' into tft-gui-work

This commit is contained in:
Manuel 2024-09-28 09:02:38 +02:00 committed by GitHub
commit fb02f87e6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 105 additions and 18 deletions

View File

@ -8,7 +8,7 @@ plugins:
uri: https://github.com/trunk-io/plugins uri: https://github.com/trunk-io/plugins
lint: lint:
enabled: enabled:
- trufflehog@3.82.4 - trufflehog@3.82.5
- yamllint@1.35.1 - yamllint@1.35.1
- bandit@1.7.10 - bandit@1.7.10
- checkov@3.2.255 - checkov@3.2.255

View File

@ -9,6 +9,7 @@ Lora:
# IRQ: 16 # IRQ: 16
# Busy: 20 # Busy: 20
# Reset: 18 # Reset: 18
# SX126X_ANT_SW: 6
# Module: sx1262 # Waveshare SX1302 LISTEN ONLY AT THIS TIME! # Module: sx1262 # Waveshare SX1302 LISTEN ONLY AT THIS TIME!
# CS: 7 # CS: 7

View File

@ -8,6 +8,7 @@
#include "MeshTypes.h" #include "MeshTypes.h"
#include "NodeStatus.h" #include "NodeStatus.h"
#include "configuration.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include "mesh/generated/meshtastic/mesh.pb.h" // For CriticalErrorCode #include "mesh/generated/meshtastic/mesh.pb.h" // For CriticalErrorCode

View File

@ -8,6 +8,7 @@
#include "FSCommon.h" #include "FSCommon.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PacketHistory.h"
#include "PhoneAPI.h" #include "PhoneAPI.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "RadioInterface.h" #include "RadioInterface.h"
@ -31,6 +32,7 @@
PhoneAPI::PhoneAPI() PhoneAPI::PhoneAPI()
{ {
lastContactMsec = millis(); lastContactMsec = millis();
std::fill(std::begin(recentToRadioPacketIds), std::end(recentToRadioPacketIds), 0);
} }
PhoneAPI::~PhoneAPI() 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 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(); lastContactMsec = millis();
// return (lastContactMsec != 0) &&
memset(&toRadioScratch, 0, sizeof(toRadioScratch)); memset(&toRadioScratch, 0, sizeof(toRadioScratch));
if (pb_decode_from_bytes(buf, bufLength, &meshtastic_ToRadio_msg, &toRadioScratch)) { if (pb_decode_from_bytes(buf, bufLength, &meshtastic_ToRadio_msg, &toRadioScratch)) {
switch (toRadioScratch.which_payload_variant) { switch (toRadioScratch.which_payload_variant) {
@ -572,12 +572,35 @@ void PhoneAPI::sendNotification(meshtastic_LogRecord_Level level, uint32_t reply
service->sendClientNotification(cn); 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 * 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) bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p)
{ {
printPacket("PACKET FROM PHONE", &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] && if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] &&
Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], THIRTY_SECONDS_MS)) { Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], THIRTY_SECONDS_MS)) {
LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum); 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] && } else if (p.decoded.portnum == meshtastic_PortNum_POSITION_APP && lastPortNumToRadio[p.decoded.portnum] &&
Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], FIVE_SECONDS_MS)) { Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], FIVE_SECONDS_MS)) {
LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum); 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; return false;
} }
lastPortNumToRadio[p.decoded.portnum] = millis(); lastPortNumToRadio[p.decoded.portnum] = millis();

View File

@ -52,6 +52,7 @@ class PhoneAPI
// Hashmap of timestamps for last time we received a packet on the API per portnum // Hashmap of timestamps for last time we received a packet on the API per portnum
std::unordered_map<meshtastic_PortNum, uint32_t> lastPortNumToRadio; std::unordered_map<meshtastic_PortNum, uint32_t> lastPortNumToRadio;
uint32_t recentToRadioPacketIds[20]; // Last 20 ToRadio MeshPacket IDs we have seen
/** /**
* Each packet sent to the phone has an incrementing count * Each packet sent to the phone has an incrementing count
@ -159,6 +160,8 @@ class PhoneAPI
void releaseClientNotification(); void releaseClientNotification();
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 * 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 * @return true true if a packet was queued for sending

View File

@ -52,6 +52,10 @@ template <typename T> bool SX126xInterface<T>::init()
float tcxoVoltage = 0; float tcxoVoltage = 0;
if (settingsMap[dio3_tcxo_voltage]) if (settingsMap[dio3_tcxo_voltage])
tcxoVoltage = 1.8; 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 // 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) #elif !defined(SX126X_DIO3_TCXO_VOLTAGE)
float tcxoVoltage = float tcxoVoltage =

View File

@ -15,26 +15,44 @@
// a max of one change per 30 seconds // a max of one change per 30 seconds
#define WATCH_INTERVAL_MSEC (30 * 1000) #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 /// 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++) { for (uint64_t i = 0; i < NUM_GPIOS; i++) {
if (mask & (1ULL << i)) { if (mask & (1ULL << i)) {
pinMode(i, mode); if (pinAccessAllowed(maskAvailable, i)) {
pinMode(i, mode);
}
} }
} }
} }
/// Read all the pins mentioned in a mask /// 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; uint64_t res = 0;
pinModes(mask, INPUT_PULLUP); pinModes(mask, INPUT_PULLUP, maskAvailable);
for (uint64_t i = 0; i < NUM_GPIOS; i++) { for (uint64_t i = 0; i < NUM_GPIOS; i++) {
uint64_t m = 1ULL << i; uint64_t m = 1ULL << i;
if (mask & m) { if (mask & m && pinAccessAllowed(maskAvailable, i)) {
if (digitalRead(i)) { if (digitalRead(i)) {
res |= m; res |= m;
} }
@ -50,6 +68,11 @@ RemoteHardwareModule::RemoteHardwareModule()
{ {
// restrict to the gpio channel for rx // restrict to the gpio channel for rx
boundChannel = Channels::gpioChannel; 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) 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 // Print notification to LCD screen
screen->print("Write GPIOs\n"); 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++) { for (uint8_t i = 0; i < NUM_GPIOS; i++) {
uint64_t mask = 1ULL << 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); digitalWrite(i, (p.gpio_value & mask) ? 1 : 0);
} }
} }
@ -79,7 +102,7 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r
if (screen) if (screen)
screen->print("Read GPIOs\n"); screen->print("Read GPIOs\n");
uint64_t res = digitalReads(p.gpio_mask); uint64_t res = digitalReads(p.gpio_mask, availablePins);
// Send the reply // Send the reply
meshtastic_HardwareMessage r = meshtastic_HardwareMessage_init_default; meshtastic_HardwareMessage r = meshtastic_HardwareMessage_init_default;
@ -121,7 +144,7 @@ int32_t RemoteHardwareModule::runOnce()
if (moduleConfig.remote_hardware.enabled && watchGpios) { if (moduleConfig.remote_hardware.enabled && watchGpios) {
if (!Throttle::isWithinTimespanMs(lastWatchMsec, WATCH_INTERVAL_MSEC)) { if (!Throttle::isWithinTimespanMs(lastWatchMsec, WATCH_INTERVAL_MSEC)) {
uint64_t curVal = digitalReads(watchGpios); uint64_t curVal = digitalReads(watchGpios, availablePins);
lastWatchMsec = millis(); lastWatchMsec = millis();
if (curVal != previousWatch) { if (curVal != previousWatch) {

View File

@ -17,6 +17,9 @@ class RemoteHardwareModule : public ProtobufModule<meshtastic_HardwareMessage>,
/// The timestamp of our last watch event (we throttle watches to 1 change every 30 seconds) /// The timestamp of our last watch event (we throttle watches to 1 change every 30 seconds)
uint32_t lastWatchMsec = 0; 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: public:
/** Constructor /** Constructor
* name is for debugging output * name is for debugging output

View File

@ -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) * 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 class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
{ {
virtual void onWrite(NimBLECharacteristic *pCharacteristic) virtual void onWrite(NimBLECharacteristic *pCharacteristic)
@ -51,7 +54,13 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
LOG_INFO("To Radio onwrite\n"); LOG_INFO("To Radio onwrite\n");
auto val = pCharacteristic->getValue(); 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");
}
} }
}; };

View File

@ -141,10 +141,19 @@ void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_e
} }
authorizeRead(conn_hdl); 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) void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len)
{ {
LOG_INFO("toRadioWriteCb data %p, len %u\n", data, 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) void setupMeshService(void)

View File

@ -80,6 +80,7 @@ void portduinoSetup()
irq, irq,
busy, busy,
reset, reset,
sx126x_ant_sw,
txen, txen,
rxen, rxen,
displayDC, displayDC,
@ -181,6 +182,7 @@ void portduinoSetup()
settingsMap[reset] = yamlConfig["Lora"]["Reset"].as<int>(RADIOLIB_NC); settingsMap[reset] = yamlConfig["Lora"]["Reset"].as<int>(RADIOLIB_NC);
settingsMap[txen] = yamlConfig["Lora"]["TXen"].as<int>(RADIOLIB_NC); settingsMap[txen] = yamlConfig["Lora"]["TXen"].as<int>(RADIOLIB_NC);
settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as<int>(RADIOLIB_NC); settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as<int>(RADIOLIB_NC);
settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as<int>(RADIOLIB_NC);
settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as<int>(0); settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as<int>(0);
settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as<bool>(false); settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as<bool>(false);
settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as<int>(2000000); settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as<int>(2000000);
@ -309,6 +311,8 @@ void portduinoSetup()
gpioInit(max_GPIO + 1); // Done here so we can inform Portduino how many GPIOs we need. 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 // 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 (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) { if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) {
settingsMap[cs] = RADIOLIB_NC; settingsMap[cs] = RADIOLIB_NC;
@ -329,6 +333,11 @@ void portduinoSetup()
settingsMap[reset] = RADIOLIB_NC; 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 (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) { if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) {
settingsMap[user] = RADIOLIB_NC; settingsMap[user] = RADIOLIB_NC;

View File

@ -8,6 +8,7 @@ enum configNames {
irq, irq,
busy, busy,
reset, reset,
sx126x_ant_sw,
txen, txen,
rxen, rxen,
dio2_as_rf_switch, dio2_as_rf_switch,