From 658459aaf3a6142445cf7494df7fe2291fd8138e Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Fri, 20 Dec 2024 12:59:23 -0800 Subject: [PATCH 1/8] Use encoded ServiceEnvelope in mqttQueue (#5619) --- src/mqtt/MQTT.cpp | 267 ++++++++++++++++++++++------------------------ src/mqtt/MQTT.h | 6 +- 2 files changed, 133 insertions(+), 140 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 1f7a06787..e40578680 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -24,6 +24,7 @@ #include #include #include +#include MQTT *mqtt; @@ -31,10 +32,6 @@ namespace { constexpr int reconnectMax = 5; -static MemoryDynamic staticMqttPool; - -Allocator &mqttPool = staticMqttPool; - // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets static uint8_t bytes[meshtastic_MqttClientProxyMessage_size + 30]; // 12 for channel name and 16 for nodeid @@ -528,39 +525,37 @@ void MQTT::publishNodeInfo() } void MQTT::publishQueuedMessages() { - if (!mqttQueue.isEmpty()) { - LOG_DEBUG("Publish enqueued MQTT message"); - meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0); - size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); - std::string topic; - if (env->packet->pki_encrypted) { - topic = cryptTopic + "PKI/" + owner.id; - } else { - topic = cryptTopic + env->channel_id + "/" + owner.id; - } - LOG_INFO("publish %s, %u bytes from queue", topic.c_str(), numBytes); + if (mqttQueue.isEmpty()) + return; - publish(topic.c_str(), bytes, numBytes, false); + LOG_DEBUG("Publish enqueued MQTT message"); + const std::unique_ptr entry(mqttQueue.dequeuePtr(0)); + LOG_INFO("publish %s, %u bytes from queue", entry->topic.c_str(), entry->envBytes.size()); + publish(entry->topic.c_str(), entry->envBytes.data(), entry->envBytes.size(), false); #if !defined(ARCH_NRF52) || \ defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ### - if (moduleConfig.mqtt.json_enabled) { - // handle json topic - auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet); - if (jsonString.length() != 0) { - std::string topicJson; - if (env->packet->pki_encrypted) { - topicJson = jsonTopic + "PKI/" + owner.id; - } else { - topicJson = jsonTopic + env->channel_id + "/" + owner.id; - } - LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str()); - publish(topicJson.c_str(), jsonString.c_str(), false); - } - } -#endif // ARCH_NRF52 NRF52_USE_JSON - mqttPool.release(env); + if (!moduleConfig.mqtt.json_enabled) + return; + + // handle json topic + const DecodedServiceEnvelope env(entry->envBytes.data(), entry->envBytes.size()); + if (!env.validDecode || env.packet == NULL || env.channel_id == NULL) + return; + + auto jsonString = MeshPacketSerializer::JsonSerialize(env.packet); + if (jsonString.length() == 0) + return; + + std::string topicJson; + if (env.packet->pki_encrypted) { + topicJson = jsonTopic + "PKI/" + owner.id; + } else { + topicJson = jsonTopic + env.channel_id + "/" + owner.id; } + LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str()); + publish(topicJson.c_str(), jsonString.c_str(), false); +#endif // ARCH_NRF52 NRF52_USE_JSON } void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_MeshPacket &mp_decoded, ChannelIndex chIndex) @@ -599,59 +594,56 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me // Either encrypted packet (we couldn't decrypt) is marked as pki_encrypted, or we could decode the PKI encrypted packet bool isPKIEncrypted = mp_encrypted.pki_encrypted || mp_decoded.pki_encrypted; // If it was to a channel, check uplink enabled, else must be pki_encrypted - if ((ch.settings.uplink_enabled && !isPKIEncrypted) || isPKIEncrypted) { - const char *channelId = isPKIEncrypted ? "PKI" : channels.getGlobalId(chIndex); + if (!(ch.settings.uplink_enabled || isPKIEncrypted)) + return; + const char *channelId = isPKIEncrypted ? "PKI" : channels.getGlobalId(chIndex); - meshtastic_ServiceEnvelope *env = mqttPool.allocZeroed(); - env->channel_id = (char *)channelId; - env->gateway_id = owner.id; + LOG_DEBUG("MQTT onSend - Publish "); + const meshtastic_MeshPacket *p; + if (moduleConfig.mqtt.encryption_enabled) { + p = &mp_encrypted; + LOG_DEBUG("encrypted message"); + } else if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + p = &mp_decoded; + LOG_DEBUG("portnum %i message", mp_decoded.decoded.portnum); + } else { + LOG_DEBUG("nothing, pkt not decrypted"); + return; // Don't upload a still-encrypted PKI packet if not encryption_enabled + } - LOG_DEBUG("MQTT onSend - Publish "); - if (moduleConfig.mqtt.encryption_enabled) { - env->packet = (meshtastic_MeshPacket *)&mp_encrypted; - LOG_DEBUG("encrypted message"); - } else if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) { - env->packet = (meshtastic_MeshPacket *)&mp_decoded; - LOG_DEBUG("portnum %i message", env->packet->decoded.portnum); - } else { - LOG_DEBUG("nothing, pkt not decrypted"); - mqttPool.release(env); - return; // Don't upload a still-encrypted PKI packet if not encryption_enabled - } + const meshtastic_ServiceEnvelope env = { + .packet = const_cast(p), .channel_id = const_cast(channelId), .gateway_id = owner.id}; + size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, &env); + std::string topic = cryptTopic + channelId + "/" + owner.id; - if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) { - size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); - std::string topic = cryptTopic + channelId + "/" + owner.id; - LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes); - - publish(topic.c_str(), bytes, numBytes, false); + if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) { + LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes); + publish(topic.c_str(), bytes, numBytes, false); #if !defined(ARCH_NRF52) || \ defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ### - if (moduleConfig.mqtt.json_enabled) { - // handle json topic - auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded); - if (jsonString.length() != 0) { - std::string topicJson = jsonTopic + channelId + "/" + owner.id; - LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), - jsonString.c_str()); - publish(topicJson.c_str(), jsonString.c_str(), false); - } - } + if (!moduleConfig.mqtt.json_enabled) + return; + // handle json topic + auto jsonString = MeshPacketSerializer::JsonSerialize(&mp_decoded); + if (jsonString.length() == 0) + return; + std::string topicJson = jsonTopic + channelId + "/" + owner.id; + LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str()); + publish(topicJson.c_str(), jsonString.c_str(), false); #endif // ARCH_NRF52 NRF52_USE_JSON + } else { + LOG_INFO("MQTT not connected, queue packet"); + QueueEntry *entry; + if (mqttQueue.numFree() == 0) { + LOG_WARN("MQTT queue is full, discard oldest"); + entry = mqttQueue.dequeuePtr(0); } else { - LOG_INFO("MQTT not connected, queue packet"); - if (mqttQueue.numFree() == 0) { - LOG_WARN("MQTT queue is full, discard oldest"); - meshtastic_ServiceEnvelope *d = mqttQueue.dequeuePtr(0); - if (d) - mqttPool.release(d); - } - // make a copy of serviceEnvelope and queue it - meshtastic_ServiceEnvelope *copied = mqttPool.allocCopy(*env); - assert(mqttQueue.enqueue(copied, 0)); + entry = new QueueEntry; } - mqttPool.release(env); + entry->topic = std::move(topic); + entry->envBytes.assign(bytes, numBytes); + assert(mqttQueue.enqueue(entry, 0)); } } @@ -660,73 +652,70 @@ void MQTT::perhapsReportToMap() if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly())) return; - if (Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs)) { + if (Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs)) return; - } else { - if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) { - last_report_to_map = millis(); - if (map_position_precision == 0) - LOG_WARN("MQTT Map report enabled, but precision is 0"); - if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) - LOG_WARN("MQTT Map report enabled, but no position available"); - return; - } - // Allocate ServiceEnvelope and fill it - meshtastic_ServiceEnvelope *se = mqttPool.allocZeroed(); - se->channel_id = (char *)channels.getGlobalId(channels.getPrimaryIndex()); // Use primary channel as the channel_id - se->gateway_id = owner.id; - - // Allocate MeshPacket and fill it - meshtastic_MeshPacket *mp = packetPool.allocZeroed(); - mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag; - mp->from = nodeDB->getNodeNum(); - mp->to = NODENUM_BROADCAST; - mp->decoded.portnum = meshtastic_PortNum_MAP_REPORT_APP; - - // Fill MapReport message - meshtastic_MapReport mapReport = meshtastic_MapReport_init_default; - memcpy(mapReport.long_name, owner.long_name, sizeof(owner.long_name)); - memcpy(mapReport.short_name, owner.short_name, sizeof(owner.short_name)); - mapReport.role = config.device.role; - mapReport.hw_model = owner.hw_model; - strncpy(mapReport.firmware_version, optstr(APP_VERSION), sizeof(mapReport.firmware_version)); - mapReport.region = config.lora.region; - mapReport.modem_preset = config.lora.modem_preset; - mapReport.has_default_channel = channels.hasDefaultChannel(); - - // Set position with precision (same as in PositionModule) - if (map_position_precision < 32 && map_position_precision > 0) { - mapReport.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - map_position_precision)); - mapReport.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - map_position_precision)); - mapReport.latitude_i += (1 << (31 - map_position_precision)); - mapReport.longitude_i += (1 << (31 - map_position_precision)); - } else { - mapReport.latitude_i = localPosition.latitude_i; - mapReport.longitude_i = localPosition.longitude_i; - } - mapReport.altitude = localPosition.altitude; - mapReport.position_precision = map_position_precision; - - mapReport.num_online_local_nodes = nodeDB->getNumOnlineMeshNodes(true); - - // Encode MapReport message and set it to MeshPacket in ServiceEnvelope - mp->decoded.payload.size = pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), - &meshtastic_MapReport_msg, &mapReport); - se->packet = mp; - - size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, se); - - LOG_INFO("MQTT Publish map report to %s", mapTopic.c_str()); - publish(mapTopic.c_str(), bytes, numBytes, false); - - // Release the allocated memory for ServiceEnvelope and MeshPacket - mqttPool.release(se); - packetPool.release(mp); - - // Update the last report time + if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) { last_report_to_map = millis(); + if (map_position_precision == 0) + LOG_WARN("MQTT Map report enabled, but precision is 0"); + if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) + LOG_WARN("MQTT Map report enabled, but no position available"); + return; } + + // Allocate MeshPacket and fill it + meshtastic_MeshPacket *mp = packetPool.allocZeroed(); + mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag; + mp->from = nodeDB->getNodeNum(); + mp->to = NODENUM_BROADCAST; + mp->decoded.portnum = meshtastic_PortNum_MAP_REPORT_APP; + + // Fill MapReport message + meshtastic_MapReport mapReport = meshtastic_MapReport_init_default; + memcpy(mapReport.long_name, owner.long_name, sizeof(owner.long_name)); + memcpy(mapReport.short_name, owner.short_name, sizeof(owner.short_name)); + mapReport.role = config.device.role; + mapReport.hw_model = owner.hw_model; + strncpy(mapReport.firmware_version, optstr(APP_VERSION), sizeof(mapReport.firmware_version)); + mapReport.region = config.lora.region; + mapReport.modem_preset = config.lora.modem_preset; + mapReport.has_default_channel = channels.hasDefaultChannel(); + + // Set position with precision (same as in PositionModule) + if (map_position_precision < 32 && map_position_precision > 0) { + mapReport.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - map_position_precision)); + mapReport.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - map_position_precision)); + mapReport.latitude_i += (1 << (31 - map_position_precision)); + mapReport.longitude_i += (1 << (31 - map_position_precision)); + } else { + mapReport.latitude_i = localPosition.latitude_i; + mapReport.longitude_i = localPosition.longitude_i; + } + mapReport.altitude = localPosition.altitude; + mapReport.position_precision = map_position_precision; + + mapReport.num_online_local_nodes = nodeDB->getNumOnlineMeshNodes(true); + + // Encode MapReport message into the MeshPacket + mp->decoded.payload.size = + pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), &meshtastic_MapReport_msg, &mapReport); + + // Encode the MeshPacket into a binary ServiceEnvelope and publish + const meshtastic_ServiceEnvelope se = { + .packet = mp, + .channel_id = (char *)channels.getGlobalId(channels.getPrimaryIndex()), // Use primary channel as the channel_id + .gateway_id = owner.id}; + size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, &se); + + LOG_INFO("MQTT Publish map report to %s", mapTopic.c_str()); + publish(mapTopic.c_str(), bytes, numBytes, false); + + // Release the allocated memory for MeshPacket + packetPool.release(mp); + + // Update the last report time + last_report_to_map = millis(); } bool MQTT::isPrivateIpAddress(const char address[]) diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index dc82c1a74..9db54ea4b 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -78,7 +78,11 @@ class MQTT : private concurrency::OSThread void start() { setIntervalFromNow(0); }; protected: - PointerQueue mqttQueue; + struct QueueEntry { + std::string topic; + std::basic_string envBytes; // binary/pb_encode_to_bytes ServiceEnvelope + }; + PointerQueue mqttQueue; int reconnectCount = 0; From 960626e498395d641d98f1430d53327b1cbf69a7 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 20 Dec 2024 17:34:02 -0600 Subject: [PATCH 2/8] Ch341 (#5474) * Very hacky first attempt at usermod ech341 * Fixes and debug printfs * Move to library version of libpinedio-usb * Add spidev: ch341 option in meshtasticd config.yaml * Only check settingsStrings on native * Use new CH341 code * Bump ch341 lib * Cleanup USBHal * Add ch341 config.d files * Remove ch341quirk * Bump to most recent spi-userspace driver * Add handling for ch341 serial, pid, and vid * Minor fixes from pio check * Trunk * Add include for musl compliance * Point to upstream libch341 --- arch/portduino/portduino.ini | 3 +- bin/config-dist.yaml | 9 - bin/config.d/lora-meshstick-1262.yaml | 11 ++ bin/config.d/lora-pinedio-usb-sx1262.yaml | 5 + src/main.cpp | 31 ++-- src/mesh/RadioLibInterface.cpp | 26 +-- src/mesh/RadioLibInterface.h | 9 +- src/platform/portduino/PortduinoGlue.cpp | 133 +++++++++------ src/platform/portduino/PortduinoGlue.h | 9 +- src/platform/portduino/USBHal.h | 194 ++++++++++++++++++++++ 10 files changed, 326 insertions(+), 104 deletions(-) create mode 100644 bin/config.d/lora-meshstick-1262.yaml create mode 100644 bin/config.d/lora-pinedio-usb-sx1262.yaml create mode 100644 src/platform/portduino/USBHal.h diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index bbafef4da..34f0dd8c9 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -26,6 +26,7 @@ lib_deps = ${radiolib_base.lib_deps} rweather/Crypto@^0.4.0 https://github.com/lovyan03/LovyanGFX.git#1401c28a47646fe00538d487adcb2eb3c72de805 + https://github.com/pine64/libch341-spi-userspace#8695637adeabf5abf5601d8e82cb0ba19ce9ec46 build_flags = ${arduino_base.build_flags} @@ -36,4 +37,4 @@ build_flags = -lstdc++fs -lbluetooth -lgpiod - -lyaml-cpp + -lyaml-cpp \ No newline at end of file diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index ec262536f..49de1675b 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -12,13 +12,6 @@ Lora: # IRQ: 17 # Reset: 22 -# Module: sx1262 # pinedio -# CS: 0 -# IRQ: 10 -# Busy: 11 -# DIO2_AS_RF_SWITCH: true -# spidev: spidev0.1 - # Module: RF95 # Adafruit RFM9x # Reset: 25 # CS: 7 @@ -50,8 +43,6 @@ Lora: # TXen: x # TX and RX enable pins # RXen: x -# ch341_quirk: true # Uncomment this to use the chunked SPI transfer that seems to fix the ch341 - # spiSpeed: 2000000 ### Set gpio chip to use in /dev/. Defaults to 0. diff --git a/bin/config.d/lora-meshstick-1262.yaml b/bin/config.d/lora-meshstick-1262.yaml new file mode 100644 index 000000000..3f8d6c617 --- /dev/null +++ b/bin/config.d/lora-meshstick-1262.yaml @@ -0,0 +1,11 @@ +Lora: + Module: sx1262 + CS: 0 + IRQ: 6 + Reset: 2 + Busy: 4 + spidev: ch341 + DIO3_TCXO_VOLTAGE: true +# USB_Serialnum: 12345678 + USB_PID: 0x5512 + USB_VID: 0x1A86 diff --git a/bin/config.d/lora-pinedio-usb-sx1262.yaml b/bin/config.d/lora-pinedio-usb-sx1262.yaml new file mode 100644 index 000000000..6b8a9fc95 --- /dev/null +++ b/bin/config.d/lora-pinedio-usb-sx1262.yaml @@ -0,0 +1,5 @@ +Lora: + Module: sx1262 + CS: 0 + IRQ: 10 + spidev: ch341 \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index eb99f279a..0409636b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -90,6 +90,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr; #include "linux/LinuxHardwareI2C.h" #include "mesh/raspihttp/PiWebServer.h" #include "platform/portduino/PortduinoGlue.h" +#include "platform/portduino/USBHal.h" #include #include #include @@ -213,6 +214,9 @@ static OSThread *powerFSMthread; static OSThread *ambientLightingThread; RadioInterface *rIf = NULL; +#ifdef ARCH_PORTDUINO +RadioLibHal *RadioLibHAL = NULL; +#endif /** * Some platforms (nrf52) might provide an alterate version that suppresses calling delay from sleep. @@ -241,7 +245,7 @@ void setup() // GPIO10 manages all peripheral power supplies // Turn on peripheral power immediately after MUC starts. // If some boards are turned on late, ESP32 will reset due to low voltage. - // ESP32-C3(Keyboard) , MAX98357A(Audio Power Amplifier) , + // ESP32-C3(Keyboard) , MAX98357A(Audio Power Amplifier) , // TF Card , Display backlight(AW9364DNR) , AN48841B(Trackball) , ES7210(Decoder) pinMode(KB_POWERON, OUTPUT); digitalWrite(KB_POWERON, HIGH); @@ -420,7 +424,6 @@ void setup() digitalWrite(AQ_SET_PIN, HIGH); #endif - // Currently only the tbeam has a PMU // PMU initialization needs to be placed before i2c scanning power = new Power(); @@ -706,12 +709,16 @@ void setup() pinMode(LORA_CS, OUTPUT); digitalWrite(LORA_CS, HIGH); SPI1.begin(false); -#else // HW_SPI1_DEVICE +#else // HW_SPI1_DEVICE SPI.setSCK(LORA_SCK); SPI.setTX(LORA_MOSI); SPI.setRX(LORA_MISO); SPI.begin(false); -#endif // HW_SPI1_DEVICE +#endif // HW_SPI1_DEVICE +#elif ARCH_PORTDUINO + if (settingsStrings[spidev] != "ch341") { + SPI.begin(); + } #elif !defined(ARCH_ESP32) // ARCH_RP2040 SPI.begin(); #else @@ -817,8 +824,11 @@ void setup() if (settingsMap[use_sx1262]) { if (!rIf) { LOG_DEBUG("Activate sx1262 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = - new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC)); + if (settingsStrings[spidev] == "ch341") { + RadioLibHAL = ch341Hal; + } else { + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + } rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -832,8 +842,7 @@ void setup() } else if (settingsMap[use_rf95]) { if (!rIf) { LOG_DEBUG("Activate rf95 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = - new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC)); + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -848,7 +857,7 @@ void setup() } else if (settingsMap[use_sx1280]) { if (!rIf) { LOG_DEBUG("Activate sx1280 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); rIf = new SX1280Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -908,7 +917,7 @@ void setup() } else if (settingsMap[use_sx1268]) { if (!rIf) { LOG_DEBUG("Activate sx1268 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); rIf = new SX1268Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -1265,4 +1274,4 @@ void loop() mainDelay.delay(delayMsec); } } -#endif +#endif \ No newline at end of file diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 5f82a41ce..e416160eb 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -31,31 +31,7 @@ void LockingArduinoHal::spiEndTransaction() #if ARCH_PORTDUINO void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in) { - if (busy == RADIOLIB_NC) { - spi->transfer(out, in, len); - } else { - uint16_t offset = 0; - - while (len) { - uint8_t block_size = (len < 20 ? len : 20); - spi->transfer((out != NULL ? out + offset : NULL), (in != NULL ? in + offset : NULL), block_size); - if (block_size == len) - return; - - // ensure GPIO is low - - uint32_t start = millis(); - while (digitalRead(busy)) { - if (!Throttle::isWithinTimespanMs(start, 2000)) { - LOG_ERROR("GPIO mid-transfer timeout, is it connected?"); - return; - } - } - - offset += block_size; - len -= block_size; - } - } + spi->transfer(out, in, len); } #endif diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index a5c2e30dd..d6101ae37 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -22,18 +22,11 @@ class LockingArduinoHal : public ArduinoHal { public: - LockingArduinoHal(SPIClass &spi, SPISettings spiSettings, RADIOLIB_PIN_TYPE _busy = RADIOLIB_NC) - : ArduinoHal(spi, spiSettings) - { -#if ARCH_PORTDUINO - busy = _busy; -#endif - }; + LockingArduinoHal(SPIClass &spi, SPISettings spiSettings) : ArduinoHal(spi, spiSettings){}; void spiBeginTransaction() override; void spiEndTransaction() override; #if ARCH_PORTDUINO - RADIOLIB_PIN_TYPE busy; void spiTransfer(uint8_t *out, size_t len, uint8_t *in) override; #endif diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 750cc1630..82fd8de66 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -21,9 +21,12 @@ #include #include +#include "platform/portduino/USBHal.h" + std::map settingsMap; std::map settingsStrings; std::ofstream traceFile; +Ch341Hal *ch341Hal = nullptr; char *configPath = nullptr; char *optionMac = nullptr; @@ -104,7 +107,6 @@ void getMacAddr(uint8_t *dmac) struct hci_dev_info di; di.dev_id = 0; bdaddr_t bdaddr; - char addr[18]; int btsock; btsock = socket(AF_BLUETOOTH, SOCK_RAW, 1); if (btsock < 0) { // If anything fails, just return with the default value @@ -201,8 +203,36 @@ void portduinoSetup() } } } - + // if we're using a usermode driver, we need to initialize it here, to get a serial number back for mac address uint8_t dmac[6] = {0}; + if (settingsStrings[spidev] == "ch341") { + ch341Hal = new Ch341Hal(0); + if (settingsStrings[lora_usb_serial_num] != "") { + ch341Hal->serial = settingsStrings[lora_usb_serial_num]; + } + ch341Hal->vid = settingsMap[lora_usb_vid]; + ch341Hal->pid = settingsMap[lora_usb_pid]; + ch341Hal->init(); + if (!ch341Hal->isInit()) { + std::cout << "Could not initialize CH341 device!" << std::endl; + exit(EXIT_FAILURE); + } + char serial[9] = {0}; + ch341Hal->getSerialString(serial, 8); + std::cout << "Serial " << serial << std::endl; + if (strlen(serial) == 8 && settingsStrings[mac_address].length() < 12) { + uint8_t hash[32] = {0}; + memcpy(hash, serial, 8); + crypto->hash(hash, 8); + dmac[0] = (hash[0] << 4) | 2; + dmac[1] = hash[1]; + dmac[2] = hash[2]; + dmac[3] = hash[3]; + dmac[4] = hash[4]; + dmac[5] = hash[5]; + } + } + getMacAddr(dmac); if (dmac[0] == 0 && dmac[1] == 0 && dmac[2] == 0 && dmac[3] == 0 && dmac[4] == 0 && dmac[5] == 0) { std::cout << "*** Blank MAC Address not allowed!" << std::endl; @@ -225,47 +255,11 @@ void portduinoSetup() // 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; - } - } - if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) { - settingsMap[irq] = RADIOLIB_NC; - } - } - if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) { - settingsMap[busy] = RADIOLIB_NC; - } - } - if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) { - 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; } } - if (settingsMap.count(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) { - settingsMap[rxen] = RADIOLIB_NC; - } - } - if (settingsMap.count(txen) > 0 && settingsMap[txen] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[txen], gpioChipName) != ERRNO_OK) { - settingsMap[txen] = RADIOLIB_NC; - } - } - if (settingsMap[displayPanel] != no_screen) { if (settingsMap[displayCS] > 0) initGPIOPin(settingsMap[displayCS], gpioChipName); @@ -283,7 +277,43 @@ void portduinoSetup() initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName); } - if (settingsStrings[spidev] != "") { + // Only initialize the radio pins when dealing with real, kernel controlled SPI hardware + if (settingsStrings[spidev] != "" && settingsStrings[spidev] != "ch341") { + if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) { + settingsMap[cs] = RADIOLIB_NC; + } + } + if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) { + settingsMap[irq] = RADIOLIB_NC; + } + } + if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) { + settingsMap[busy] = RADIOLIB_NC; + } + } + if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) { + 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(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) { + settingsMap[rxen] = RADIOLIB_NC; + } + } + if (settingsMap.count(txen) > 0 && settingsMap[txen] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[txen], gpioChipName) != ERRNO_OK) { + settingsMap[txen] = RADIOLIB_NC; + } + } SPI.begin(settingsStrings[spidev].c_str()); } if (settingsStrings[traceFilename] != "") { @@ -378,17 +408,24 @@ bool loadConfig(const char *configPath) 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); + settingsStrings[lora_usb_serial_num] = yamlConfig["Lora"]["USB_Serialnum"].as(""); + settingsMap[lora_usb_pid] = yamlConfig["Lora"]["USB_PID"].as(0x5512); + settingsMap[lora_usb_vid] = yamlConfig["Lora"]["USB_VID"].as(0x1A86); - settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as("spidev0.0"); - if (settingsStrings[spidev].length() == 14) { - int x = settingsStrings[spidev].at(11) - '0'; - int y = settingsStrings[spidev].at(13) - '0'; - if (x >= 0 && x < 10 && y >= 0 && y < 10) { - settingsMap[spidev] = x + y << 4; - settingsMap[displayspidev] = settingsMap[spidev]; - settingsMap[touchscreenspidev] = settingsMap[spidev]; + settingsStrings[spidev] = yamlConfig["Lora"]["spidev"].as("spidev0.0"); + if (settingsStrings[spidev] != "ch341") { + settingsStrings[spidev] = "/dev/" + settingsStrings[spidev]; + if (settingsStrings[spidev].length() == 14) { + int x = settingsStrings[spidev].at(11) - '0'; + int y = settingsStrings[spidev].at(13) - '0'; + // Pretty sure this is always true + if (x >= 0 && x < 10 && y >= 0 && y < 10) { + // I believe this bit of weirdness is specifically for the new GUI + settingsMap[spidev] = x + y << 4; + settingsMap[displayspidev] = settingsMap[spidev]; + settingsMap[touchscreenspidev] = settingsMap[spidev]; + } } } } diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 01541eeed..9cf9b6678 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -2,6 +2,8 @@ #include #include +#include "platform/portduino/USBHal.h" + enum configNames { use_sx1262, cs, @@ -13,13 +15,15 @@ enum configNames { rxen, dio2_as_rf_switch, dio3_tcxo_voltage, - ch341Quirk, use_rf95, use_sx1280, use_lr1110, use_lr1120, use_lr1121, use_sx1268, + lora_usb_serial_num, + lora_usb_pid, + lora_usb_vid, user, gpiochip, spidev, @@ -69,8 +73,9 @@ enum { level_error, level_warn, level_info, level_debug, level_trace }; extern std::map settingsMap; extern std::map settingsStrings; extern std::ofstream traceFile; +extern Ch341Hal *ch341Hal; int initGPIOPin(int pinNum, std::string gpioChipname); bool loadConfig(const char *configPath); static bool ends_with(std::string_view str, std::string_view suffix); void getMacAddr(uint8_t *dmac); -bool MAC_from_string(std::string mac_str, uint8_t *dmac); \ No newline at end of file +bool MAC_from_string(std::string mac_str, uint8_t *dmac); diff --git a/src/platform/portduino/USBHal.h b/src/platform/portduino/USBHal.h new file mode 100644 index 000000000..2b0302ced --- /dev/null +++ b/src/platform/portduino/USBHal.h @@ -0,0 +1,194 @@ +#ifndef PI_HAL_LGPIO_H +#define PI_HAL_LGPIO_H + +// include RadioLib +#include "platform/portduino/PortduinoGlue.h" +#include +#include +#include +#include + +// include the library for Raspberry GPIO pins + +#define PI_RISING (PINEDIO_INT_MODE_RISING) +#define PI_FALLING (PINEDIO_INT_MODE_FALLING) +#define PI_INPUT (0) +#define PI_OUTPUT (1) +#define PI_LOW (0) +#define PI_HIGH (1) + +#define CH341_PIN_CS (101) +#define CH341_PIN_IRQ (0) + +// the HAL must inherit from the base RadioLibHal class +// and implement all of its virtual methods +class Ch341Hal : public RadioLibHal +{ + public: + // default constructor - initializes the base HAL and any needed private members + Ch341Hal(uint8_t spiChannel, uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0) + : RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING) + { + } + + void getSerialString(char *_serial, size_t len) + { + if (!pinedio_is_init) { + return; + } + strncpy(_serial, pinedio.serial_number, len); + } + + void init() override + { + // now the SPI + spiBegin(); + } + + void term() override + { + // stop the SPI + spiEnd(); + } + + // GPIO-related methods (pinMode, digitalWrite etc.) should check + // RADIOLIB_NC as an alias for non-connected pins + void pinMode(uint32_t pin, uint32_t mode) override + { + if (pin == RADIOLIB_NC) { + return; + } + pinedio_set_pin_mode(&pinedio, pin, mode); + } + + void digitalWrite(uint32_t pin, uint32_t value) override + { + if (pin == RADIOLIB_NC) { + return; + } + pinedio_digital_write(&pinedio, pin, value); + } + + uint32_t digitalRead(uint32_t pin) override + { + if (pin == RADIOLIB_NC) { + return 0; + } + return pinedio_digital_read(&pinedio, pin); + } + + void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override + { + if ((interruptNum == RADIOLIB_NC)) { + return; + } + // LOG_DEBUG("Attach interrupt to pin %d", interruptNum); + pinedio_attach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum, (pinedio_int_mode)mode, interruptCb); + } + + void detachInterrupt(uint32_t interruptNum) override + { + if ((interruptNum == RADIOLIB_NC)) { + return; + } + // LOG_DEBUG("Detach interrupt from pin %d", interruptNum); + + pinedio_deattach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum); + } + + void delay(unsigned long ms) override + { + if (ms == 0) { + sched_yield(); + return; + } + + usleep(ms * 1000); + } + + void delayMicroseconds(unsigned long us) override + { + if (us == 0) { + sched_yield(); + return; + } + usleep(us); + } + + void yield() override { sched_yield(); } + + unsigned long millis() override + { + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000ULL); + } + + unsigned long micros() override + { + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec * 1000000ULL) + tv.tv_usec; + } + + long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override + { + fprintf(stderr, "pulseIn for pin %u is not supported!\n", pin); + return 0; + } + + void spiBegin() + { + if (!pinedio_is_init) { + if (serial != "") { + strncpy(pinedio.serial_number, serial.c_str(), 8); + pinedio_set_option(&pinedio, PINEDIO_OPTION_SEARCH_SERIAL, 1); + } + pinedio_set_option(&pinedio, PINEDIO_OPTION_PID, pid); + pinedio_set_option(&pinedio, PINEDIO_OPTION_VID, vid); + int32_t ret = pinedio_init(&pinedio, NULL); + if (ret != 0) { + fprintf(stderr, "Could not open SPI: %d\n", ret); + } else { + pinedio_is_init = true; + // LOG_INFO("USB Serial: %s", pinedio.serial_number); + pinedio_set_option(&pinedio, PINEDIO_OPTION_AUTO_CS, 0); + pinedio_set_pin_mode(&pinedio, 3, true); + pinedio_set_pin_mode(&pinedio, 5, true); + } + } + } + + void spiBeginTransaction() {} + + void spiTransfer(uint8_t *out, size_t len, uint8_t *in) + { + int32_t result = pinedio_transceive(&this->pinedio, out, in, len); + if (result < 0) { + fprintf(stderr, "Could not perform SPI transfer: %d\n", result); + } + } + + void spiEndTransaction() {} + + void spiEnd() + { + if (pinedio_is_init) { + pinedio_deinit(&pinedio); + pinedio_is_init = false; + } + } + + bool isInit() { return pinedio_is_init; } + + std::string serial = ""; + uint32_t pid = 0x5512; + uint32_t vid = 0x1A86; + + private: + // the HAL can contain any additional private members + pinedio_inst pinedio = {0}; + bool pinedio_is_init = false; +}; + +#endif \ No newline at end of file From 58d80b8557a849b7d3331fb3318d521a0c6cbcab Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Fri, 20 Dec 2024 16:21:27 -0800 Subject: [PATCH 3/8] Use IPAddress.fromString for parsing private IPs (#5621) --- src/mqtt/MQTT.cpp | 103 +++++++++++++++------------------------------- src/mqtt/MQTT.h | 4 -- 2 files changed, 33 insertions(+), 74 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index e40578680..ac4e9e786 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -26,6 +26,14 @@ #include #include +#include +#if defined(ARCH_PORTDUINO) +#include +#elif !defined(ntohl) +#include +#define ntohl __ntohl +#endif + MQTT *mqtt; namespace @@ -196,6 +204,29 @@ inline void onReceiveJson(byte *payload, size_t length) LOG_DEBUG("JSON ignore downlink message with unsupported type"); } } + +/// Determines if the given IPAddress is a private IPv4 address, i.e. not routable on the public internet. +bool isPrivateIpAddress(const IPAddress &ip) +{ + constexpr struct { + uint32_t network; + uint32_t mask; + } privateCidrRanges[] = { + {.network = 192u << 24 | 168 << 16, .mask = 0xffff0000}, // 192.168.0.0/16 + {.network = 172u << 24 | 16 << 16, .mask = 0xfff00000}, // 172.16.0.0/12 + {.network = 169u << 24 | 254 << 16, .mask = 0xffff0000}, // 169.254.0.0/16 + {.network = 10u << 24, .mask = 0xff000000}, // 10.0.0.0/8 + {.network = 127u << 24 | 1, .mask = 0xffffffff}, // 127.0.0.1/32 + }; + const uint32_t addr = ntohl(ip); + for (const auto &cidrRange : privateCidrRanges) { + if (cidrRange.network == (addr & cidrRange.mask)) { + LOG_INFO("MQTT server on a private IP"); + return true; + } + } + return false; +} } // namespace void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length) @@ -270,10 +301,8 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE) moduleConfig.mqtt.map_report_settings.publish_interval_secs, default_map_publish_interval_secs); } - isMqttServerAddressPrivate = isPrivateIpAddress(moduleConfig.mqtt.address); - if (isMqttServerAddressPrivate) { - LOG_INFO("MQTT server on a private IP"); - } + IPAddress ip; + isMqttServerAddressPrivate = ip.fromString(moduleConfig.mqtt.address) && isPrivateIpAddress(ip); #if HAS_NETWORKING if (!moduleConfig.mqtt.proxy_to_client_enabled) @@ -717,69 +746,3 @@ void MQTT::perhapsReportToMap() // Update the last report time last_report_to_map = millis(); } - -bool MQTT::isPrivateIpAddress(const char address[]) -{ - // Min. length like 10.0.0.0 (8), max like 192.168.255.255:65535 (21) - size_t length = strlen(address); - if (length < 8 || length > 21) { - return false; - } - - // Ensure the address contains only digits and dots and maybe a colon. - // Some limited validation is done. - // Even if it's not a valid IP address, we will know it's not a domain. - bool hasColon = false; - int numDots = 0; - for (size_t i = 0; i < length; i++) { - if (!isdigit(address[i]) && address[i] != '.' && address[i] != ':') { - return false; - } - - // Dots can't be the first character, immediately follow another dot, - // occur more than 3 times, or occur after a colon. - if (address[i] == '.') { - if (++numDots > 3 || i == 0 || address[i - 1] == '.' || hasColon) { - return false; - } - } - // There can only be a single colon, and it can only occur after 3 dots - else if (address[i] == ':') { - if (hasColon || numDots < 3) { - return false; - } - - hasColon = true; - } - } - - // Final validation for IPv4 address and port format. - // Note that the values of octets haven't been tested, only the address format. - if (numDots != 3) { - return false; - } - - // Check the easy ones first. - if (strcmp(address, "127.0.0.1") == 0 || strncmp(address, "10.", 3) == 0 || strncmp(address, "192.168", 7) == 0 || - strncmp(address, "169.254", 7) == 0) { - return true; - } - - // See if it's definitely not a 172 address. - if (strncmp(address, "172", 3) != 0) { - return false; - } - - // We know it's a 172 address, now see if the second octet is 2 digits. - if (address[6] != '.') { - return false; - } - - // Copy the second octet into a secondary buffer we can null-terminate and parse. - char octet2[3]; - strncpy(octet2, address + 4, 2); - octet2[2] = 0; - - int octet2Num = atoi(octet2); - return octet2Num >= 16 && octet2Num <= 31; -} diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index 9db54ea4b..11621c55f 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -121,10 +121,6 @@ class MQTT : private concurrency::OSThread // Check if we should report unencrypted information about our node for consumption by a map void perhapsReportToMap(); - /// Determines if the given address is a private IPv4 address, i.e. not routable on the public internet. - /// These are the ranges: 127.0.0.1, 10.0.0.0-10.255.255.255, 172.16.0.0-172.31.255.255, 192.168.0.0-192.168.255.255. - bool isPrivateIpAddress(const char address[]); - /// Return 0 if sleep is okay, veto sleep if we are connected to pubsub server // int preflightSleepCb(void *unused = NULL) { return pubSub.connected() ? 1 : 0; } }; From 5fed679d331d7c40dc835c25cfd73bae34104c17 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Sat, 21 Dec 2024 12:24:29 +1100 Subject: [PATCH 4/8] Add detection code for INA226 (#5605) INA226 is a high accuracy current and voltage sensor. --- src/detect/ScanI2C.h | 5 +++-- src/detect/ScanI2CTwoWire.cpp | 12 ++++++++++-- src/main.cpp | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 7fe3aac89..2473a6573 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -63,7 +63,8 @@ class ScanI2C MAX30102, TPS65233, MPR121KB, - CGRADSENS + CGRADSENS, + INA226 } DeviceType; // typedef uint8_t DeviceAddress; @@ -127,4 +128,4 @@ class ScanI2C private: bool shouldSuppressScreen = false; -}; \ No newline at end of file +}; diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 551b87d27..79c0deccf 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -250,8 +250,16 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2); LOG_DEBUG("Register MFG_UID: 0x%x", registerValue); if (registerValue == 0x5449) { - logFoundDevice("INA260", (uint8_t)addr.address); - type = INA260; + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFF), 2); + LOG_DEBUG("Register DIE_UID: 0x%x", registerValue); + + if (registerValue == 0x2260) { + logFoundDevice("INA226", (uint8_t)addr.address); + type = INA226; + } else { + logFoundDevice("INA260", (uint8_t)addr.address); + type = INA260; + } } else { // Assume INA219 if INA260 ID is not found logFoundDevice("INA219", (uint8_t)addr.address); type = INA219; diff --git a/src/main.cpp b/src/main.cpp index 0409636b4..f4bb11535 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -579,6 +579,7 @@ void setup() scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_3XX, meshtastic_TelemetrySensorType_BMP3XX); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA226, meshtastic_TelemetrySensorType_INA226); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048); From 9a10907a2d79bee4c76b5618e177897bb4643b10 Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Fri, 20 Dec 2024 17:25:31 -0800 Subject: [PATCH 5/8] Check if MQTT remote IP is private (#5627) --- src/mqtt/MQTT.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index ac4e9e786..7b15e99f3 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -438,6 +438,9 @@ void MQTT::reconnect() enabled = true; // Start running background process again runASAP = true; reconnectCount = 0; +#if !defined(ARCH_PORTDUINO) + isMqttServerAddressPrivate = isPrivateIpAddress(mqttClient.remoteIP()); +#endif publishNodeInfo(); sendSubscriptions(); From df63423cdcc5b41e8a7b900a21807817c7dc488f Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Sat, 21 Dec 2024 12:26:23 +1100 Subject: [PATCH 6/8] Let RangeTest Module use Phone position if there's no GPS (#5623) As reported by @Fastomat, if a user had enabled "Share Phone Position" in the app, RangeTest did not use this position and recorded a 0,0 lat/lon. This change preferences GPS where avaialble, but otherwise uses the position stored for the node in NodeDB. fixes https://github.com/meshtastic/firmware/issues/5620 --- src/modules/RangeTestModule.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp index bf842ce55..c42839d97 100644 --- a/src/modules/RangeTestModule.cpp +++ b/src/modules/RangeTestModule.cpp @@ -155,8 +155,6 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket LOG_DEBUG("mp.from %d", mp.from); LOG_DEBUG("mp.rx_snr %f", mp.rx_snr); LOG_DEBUG("mp.hop_limit %d", mp.hop_limit); - // LOG_DEBUG("mp.decoded.position.latitude_i %d", mp.decoded.position.latitude_i); // Deprecated - // LOG_DEBUG("mp.decoded.position.longitude_i %d", mp.decoded.position.longitude_i); // Deprecated LOG_DEBUG("---- Node Information of Received Packet (mp.from):"); LOG_DEBUG("n->user.long_name %s", n->user.long_name); LOG_DEBUG("n->user.short_name %s", n->user.short_name); @@ -194,8 +192,6 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) LOG_DEBUG("mp.from %d", mp.from); LOG_DEBUG("mp.rx_snr %f", mp.rx_snr); LOG_DEBUG("mp.hop_limit %d", mp.hop_limit); - // LOG_DEBUG("mp.decoded.position.latitude_i %d", mp.decoded.position.latitude_i); // Deprecated - // LOG_DEBUG("mp.decoded.position.longitude_i %d", mp.decoded.position.longitude_i); // Deprecated LOG_DEBUG("---- Node Information of Received Packet (mp.from):"); LOG_DEBUG("n->user.long_name %s", n->user.long_name); LOG_DEBUG("n->user.short_name %s", n->user.short_name); @@ -265,13 +261,21 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) fileToAppend.printf("??:??:??,"); // Time } - fileToAppend.printf("%d,", getFrom(&mp)); // From - fileToAppend.printf("%s,", n->user.long_name); // Long Name - fileToAppend.printf("%f,", n->position.latitude_i * 1e-7); // Sender Lat - fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long - fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat - fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long - fileToAppend.printf("%d,", gpsStatus->getAltitude()); // RX Altitude + fileToAppend.printf("%d,", getFrom(&mp)); // From + fileToAppend.printf("%s,", n->user.long_name); // Long Name + fileToAppend.printf("%f,", n->position.latitude_i * 1e-7); // Sender Lat + fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long + if (gpsStatus->getIsConnected() || config.position.fixed_position) { + fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat + fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long + fileToAppend.printf("%d,", gpsStatus->getAltitude()); // RX Altitude + } else { + // When the phone API is in use, the node info will be updated with position + meshtastic_NodeInfoLite *us = nodeDB->getMeshNode(nodeDB->getNodeNum()); + fileToAppend.printf("%f,", us->position.latitude_i * 1e-7); // RX Lat + fileToAppend.printf("%f,", us->position.longitude_i * 1e-7); // RX Long + fileToAppend.printf("%d,", us->position.altitude); // RX Altitude + } fileToAppend.printf("%f,", mp.rx_snr); // RX SNR @@ -292,4 +296,4 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) #endif return 1; -} +} \ No newline at end of file From 398d29064e770fbf1e79eec26542ec9fe9677fa4 Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Fri, 20 Dec 2024 19:06:01 -0800 Subject: [PATCH 7/8] Separate host/port before checking for private IP (#5630) --- src/mqtt/MQTT.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 7b15e99f3..c7db4672a 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -227,6 +227,16 @@ bool isPrivateIpAddress(const IPAddress &ip) } return false; } + +std::pair parseHostAndPort(std::string address, uint16_t port = 0) +{ + const size_t delimIndex = address.find_first_of(':'); + if (delimIndex > 0) { + port = std::stoul(address.substr(delimIndex + 1, address.length())); + address.resize(delimIndex); + } + return std::make_pair(std::move(address), port); +} } // namespace void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length) @@ -302,7 +312,8 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE) } IPAddress ip; - isMqttServerAddressPrivate = ip.fromString(moduleConfig.mqtt.address) && isPrivateIpAddress(ip); + isMqttServerAddressPrivate = + ip.fromString(parseHostAndPort(moduleConfig.mqtt.address).first.c_str()) && isPrivateIpAddress(ip); #if HAS_NETWORKING if (!moduleConfig.mqtt.proxy_to_client_enabled) @@ -418,14 +429,9 @@ void MQTT::reconnect() pubSub.setClient(mqttClient); #endif - String server = String(serverAddr); - int delimIndex = server.indexOf(':'); - if (delimIndex > 0) { - String port = server.substring(delimIndex + 1, server.length()); - server[delimIndex] = 0; - serverPort = port.toInt(); - serverAddr = server.c_str(); - } + std::pair hostAndPort = parseHostAndPort(serverAddr, serverPort); + serverAddr = hostAndPort.first.c_str(); + serverPort = hostAndPort.second; pubSub.setServer(serverAddr, serverPort); pubSub.setBufferSize(512); From f39a9c5083e061e5919495df3814e058b13bd263 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 20 Dec 2024 21:42:54 -0600 Subject: [PATCH 8/8] Clean up some straggler NRF52 json (#5628) --- src/mqtt/MQTT.cpp | 8 +++++++- src/mqtt/MQTT.h | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index c7db4672a..74a3f357d 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -19,8 +19,10 @@ #include #endif #include "Default.h" +#if !defined(ARCH_NRF52) || NRF52_USE_JSON #include "serialization/JSON.h" #include "serialization/MeshPacketSerializer.h" +#endif #include #include #include @@ -119,6 +121,7 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length) router->enqueueReceivedMessage(p.release()); } +#if !defined(ARCH_NRF52) || NRF52_USE_JSON // returns true if this is a valid JSON envelope which we accept on downlink inline bool isValidJsonEnvelope(JSONObject &json) { @@ -204,6 +207,7 @@ inline void onReceiveJson(byte *payload, size_t length) LOG_DEBUG("JSON ignore downlink message with unsupported type"); } } +#endif /// Determines if the given IPAddress is a private IPv4 address, i.e. not routable on the public internet. bool isPrivateIpAddress(const IPAddress &ip) @@ -258,6 +262,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) // check if this is a json payload message by comparing the topic start if (moduleConfig.mqtt.json_enabled && (strncmp(topic, jsonTopic.c_str(), jsonTopic.length()) == 0)) { +#if !defined(ARCH_NRF52) || NRF52_USE_JSON // parse the channel name from the topic string // the topic has been checked above for having jsonTopic prefix, so just move past it char *channelName = topic + jsonTopic.length(); @@ -271,6 +276,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) return; } onReceiveJson(payload, length); +#endif return; } @@ -754,4 +760,4 @@ void MQTT::perhapsReportToMap() // Update the last report time last_report_to_map = millis(); -} +} \ No newline at end of file diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index 11621c55f..81892f6f3 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -5,7 +5,9 @@ #include "concurrency/OSThread.h" #include "mesh/Channels.h" #include "mesh/generated/meshtastic/mqtt.pb.h" +#if !defined(ARCH_NRF52) || NRF52_USE_JSON #include "serialization/JSON.h" +#endif #if HAS_WIFI #include #if !defined(ARCH_PORTDUINO) @@ -127,4 +129,4 @@ class MQTT : private concurrency::OSThread void mqttInit(); -extern MQTT *mqtt; +extern MQTT *mqtt; \ No newline at end of file