Merge branch 'meshtastic:master' into master

This commit is contained in:
Mictronics 2024-12-21 10:05:40 +01:00 committed by GitHub
commit ba53101d8c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 539 additions and 334 deletions

View File

@ -26,6 +26,7 @@ lib_deps =
${radiolib_base.lib_deps} ${radiolib_base.lib_deps}
rweather/Crypto@^0.4.0 rweather/Crypto@^0.4.0
https://github.com/lovyan03/LovyanGFX.git#1401c28a47646fe00538d487adcb2eb3c72de805 https://github.com/lovyan03/LovyanGFX.git#1401c28a47646fe00538d487adcb2eb3c72de805
https://github.com/pine64/libch341-spi-userspace#8695637adeabf5abf5601d8e82cb0ba19ce9ec46
build_flags = build_flags =
${arduino_base.build_flags} ${arduino_base.build_flags}

View File

@ -12,13 +12,6 @@ Lora:
# IRQ: 17 # IRQ: 17
# Reset: 22 # Reset: 22
# Module: sx1262 # pinedio
# CS: 0
# IRQ: 10
# Busy: 11
# DIO2_AS_RF_SWITCH: true
# spidev: spidev0.1
# Module: RF95 # Adafruit RFM9x # Module: RF95 # Adafruit RFM9x
# Reset: 25 # Reset: 25
# CS: 7 # CS: 7
@ -50,8 +43,6 @@ Lora:
# TXen: x # TX and RX enable pins # TXen: x # TX and RX enable pins
# RXen: x # RXen: x
# ch341_quirk: true # Uncomment this to use the chunked SPI transfer that seems to fix the ch341
# spiSpeed: 2000000 # spiSpeed: 2000000
### Set gpio chip to use in /dev/. Defaults to 0. ### Set gpio chip to use in /dev/. Defaults to 0.

View File

@ -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

View File

@ -0,0 +1,5 @@
Lora:
Module: sx1262
CS: 0
IRQ: 10
spidev: ch341

View File

@ -63,7 +63,8 @@ class ScanI2C
MAX30102, MAX30102,
TPS65233, TPS65233,
MPR121KB, MPR121KB,
CGRADSENS CGRADSENS,
INA226
} DeviceType; } DeviceType;
// typedef uint8_t DeviceAddress; // typedef uint8_t DeviceAddress;

View File

@ -250,8 +250,16 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2); registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2);
LOG_DEBUG("Register MFG_UID: 0x%x", registerValue); LOG_DEBUG("Register MFG_UID: 0x%x", registerValue);
if (registerValue == 0x5449) { if (registerValue == 0x5449) {
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); logFoundDevice("INA260", (uint8_t)addr.address);
type = INA260; type = INA260;
}
} else { // Assume INA219 if INA260 ID is not found } else { // Assume INA219 if INA260 ID is not found
logFoundDevice("INA219", (uint8_t)addr.address); logFoundDevice("INA219", (uint8_t)addr.address);
type = INA219; type = INA219;

View File

@ -90,6 +90,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
#include "linux/LinuxHardwareI2C.h" #include "linux/LinuxHardwareI2C.h"
#include "mesh/raspihttp/PiWebServer.h" #include "mesh/raspihttp/PiWebServer.h"
#include "platform/portduino/PortduinoGlue.h" #include "platform/portduino/PortduinoGlue.h"
#include "platform/portduino/USBHal.h"
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <string> #include <string>
@ -213,6 +214,9 @@ static OSThread *powerFSMthread;
static OSThread *ambientLightingThread; static OSThread *ambientLightingThread;
RadioInterface *rIf = NULL; RadioInterface *rIf = NULL;
#ifdef ARCH_PORTDUINO
RadioLibHal *RadioLibHAL = NULL;
#endif
/** /**
* Some platforms (nrf52) might provide an alterate version that suppresses calling delay from sleep. * Some platforms (nrf52) might provide an alterate version that suppresses calling delay from sleep.
@ -420,7 +424,6 @@ void setup()
digitalWrite(AQ_SET_PIN, HIGH); digitalWrite(AQ_SET_PIN, HIGH);
#endif #endif
// Currently only the tbeam has a PMU // Currently only the tbeam has a PMU
// PMU initialization needs to be placed before i2c scanning // PMU initialization needs to be placed before i2c scanning
power = new Power(); power = new Power();
@ -576,6 +579,7 @@ void setup()
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_3XX, meshtastic_TelemetrySensorType_BMP3XX); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_3XX, meshtastic_TelemetrySensorType_BMP3XX);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260); 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::INA219, meshtastic_TelemetrySensorType_INA219);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048);
@ -712,6 +716,10 @@ void setup()
SPI.setRX(LORA_MISO); SPI.setRX(LORA_MISO);
SPI.begin(false); 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 #elif !defined(ARCH_ESP32) // ARCH_RP2040
SPI.begin(); SPI.begin();
#else #else
@ -817,8 +825,11 @@ void setup()
if (settingsMap[use_sx1262]) { if (settingsMap[use_sx1262]) {
if (!rIf) { if (!rIf) {
LOG_DEBUG("Activate sx1262 radio on SPI port %s", settingsStrings[spidev].c_str()); LOG_DEBUG("Activate sx1262 radio on SPI port %s", settingsStrings[spidev].c_str());
LockingArduinoHal *RadioLibHAL = if (settingsStrings[spidev] == "ch341") {
new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC)); RadioLibHAL = ch341Hal;
} else {
RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
}
rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]); settingsMap[busy]);
if (!rIf->init()) { if (!rIf->init()) {
@ -832,8 +843,7 @@ void setup()
} else if (settingsMap[use_rf95]) { } else if (settingsMap[use_rf95]) {
if (!rIf) { if (!rIf) {
LOG_DEBUG("Activate rf95 radio on SPI port %s", settingsStrings[spidev].c_str()); LOG_DEBUG("Activate rf95 radio on SPI port %s", settingsStrings[spidev].c_str());
LockingArduinoHal *RadioLibHAL = RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC));
rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]); settingsMap[busy]);
if (!rIf->init()) { if (!rIf->init()) {
@ -848,7 +858,7 @@ void setup()
} else if (settingsMap[use_sx1280]) { } else if (settingsMap[use_sx1280]) {
if (!rIf) { if (!rIf) {
LOG_DEBUG("Activate sx1280 radio on SPI port %s", settingsStrings[spidev].c_str()); 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], rIf = new SX1280Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]); settingsMap[busy]);
if (!rIf->init()) { if (!rIf->init()) {
@ -908,7 +918,7 @@ void setup()
} else if (settingsMap[use_sx1268]) { } else if (settingsMap[use_sx1268]) {
if (!rIf) { if (!rIf) {
LOG_DEBUG("Activate sx1268 radio on SPI port %s", settingsStrings[spidev].c_str()); 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], rIf = new SX1268Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]); settingsMap[busy]);
if (!rIf->init()) { if (!rIf->init()) {

View File

@ -31,31 +31,7 @@ void LockingArduinoHal::spiEndTransaction()
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in) void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in)
{ {
if (busy == RADIOLIB_NC) {
spi->transfer(out, in, len); 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;
}
}
} }
#endif #endif

View File

@ -22,18 +22,11 @@
class LockingArduinoHal : public ArduinoHal class LockingArduinoHal : public ArduinoHal
{ {
public: public:
LockingArduinoHal(SPIClass &spi, SPISettings spiSettings, RADIOLIB_PIN_TYPE _busy = RADIOLIB_NC) LockingArduinoHal(SPIClass &spi, SPISettings spiSettings) : ArduinoHal(spi, spiSettings){};
: ArduinoHal(spi, spiSettings)
{
#if ARCH_PORTDUINO
busy = _busy;
#endif
};
void spiBeginTransaction() override; void spiBeginTransaction() override;
void spiEndTransaction() override; void spiEndTransaction() override;
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
RADIOLIB_PIN_TYPE busy;
void spiTransfer(uint8_t *out, size_t len, uint8_t *in) override; void spiTransfer(uint8_t *out, size_t len, uint8_t *in) override;
#endif #endif

View File

@ -155,8 +155,6 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket
LOG_DEBUG("mp.from %d", mp.from); LOG_DEBUG("mp.from %d", mp.from);
LOG_DEBUG("mp.rx_snr %f", mp.rx_snr); LOG_DEBUG("mp.rx_snr %f", mp.rx_snr);
LOG_DEBUG("mp.hop_limit %d", mp.hop_limit); 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("---- Node Information of Received Packet (mp.from):");
LOG_DEBUG("n->user.long_name %s", n->user.long_name); LOG_DEBUG("n->user.long_name %s", n->user.long_name);
LOG_DEBUG("n->user.short_name %s", n->user.short_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.from %d", mp.from);
LOG_DEBUG("mp.rx_snr %f", mp.rx_snr); LOG_DEBUG("mp.rx_snr %f", mp.rx_snr);
LOG_DEBUG("mp.hop_limit %d", mp.hop_limit); 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("---- Node Information of Received Packet (mp.from):");
LOG_DEBUG("n->user.long_name %s", n->user.long_name); LOG_DEBUG("n->user.long_name %s", n->user.long_name);
LOG_DEBUG("n->user.short_name %s", n->user.short_name); LOG_DEBUG("n->user.short_name %s", n->user.short_name);
@ -269,9 +265,17 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
fileToAppend.printf("%s,", n->user.long_name); // Long Name 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.latitude_i * 1e-7); // Sender Lat
fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long 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->getLatitude() * 1e-7); // RX Lat
fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
fileToAppend.printf("%d,", gpsStatus->getAltitude()); // RX Altitude 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 fileToAppend.printf("%f,", mp.rx_snr); // RX SNR

View File

@ -19,11 +19,22 @@
#include <WiFi.h> #include <WiFi.h>
#endif #endif
#include "Default.h" #include "Default.h"
#if !defined(ARCH_NRF52) || NRF52_USE_JSON
#include "serialization/JSON.h" #include "serialization/JSON.h"
#include "serialization/MeshPacketSerializer.h" #include "serialization/MeshPacketSerializer.h"
#endif
#include <Throttle.h> #include <Throttle.h>
#include <assert.h> #include <assert.h>
#include <pb_decode.h> #include <pb_decode.h>
#include <utility>
#include <IPAddress.h>
#if defined(ARCH_PORTDUINO)
#include <netinet/in.h>
#elif !defined(ntohl)
#include <machine/endian.h>
#define ntohl __ntohl
#endif
MQTT *mqtt; MQTT *mqtt;
@ -31,10 +42,6 @@ namespace
{ {
constexpr int reconnectMax = 5; constexpr int reconnectMax = 5;
static MemoryDynamic<meshtastic_ServiceEnvelope> staticMqttPool;
Allocator<meshtastic_ServiceEnvelope> &mqttPool = staticMqttPool;
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets // 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 static uint8_t bytes[meshtastic_MqttClientProxyMessage_size + 30]; // 12 for channel name and 16 for nodeid
@ -114,6 +121,7 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
router->enqueueReceivedMessage(p.release()); 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 // returns true if this is a valid JSON envelope which we accept on downlink
inline bool isValidJsonEnvelope(JSONObject &json) inline bool isValidJsonEnvelope(JSONObject &json)
{ {
@ -199,6 +207,40 @@ inline void onReceiveJson(byte *payload, size_t length)
LOG_DEBUG("JSON ignore downlink message with unsupported type"); 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)
{
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;
}
std::pair<std::string, uint16_t> 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 } // namespace
void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length) void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length)
@ -220,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 // 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 (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 // parse the channel name from the topic string
// the topic has been checked above for having jsonTopic prefix, so just move past it // the topic has been checked above for having jsonTopic prefix, so just move past it
char *channelName = topic + jsonTopic.length(); char *channelName = topic + jsonTopic.length();
@ -233,6 +276,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length)
return; return;
} }
onReceiveJson(payload, length); onReceiveJson(payload, length);
#endif
return; return;
} }
@ -273,10 +317,9 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
moduleConfig.mqtt.map_report_settings.publish_interval_secs, default_map_publish_interval_secs); moduleConfig.mqtt.map_report_settings.publish_interval_secs, default_map_publish_interval_secs);
} }
isMqttServerAddressPrivate = isPrivateIpAddress(moduleConfig.mqtt.address); IPAddress ip;
if (isMqttServerAddressPrivate) { isMqttServerAddressPrivate =
LOG_INFO("MQTT server on a private IP"); ip.fromString(parseHostAndPort(moduleConfig.mqtt.address).first.c_str()) && isPrivateIpAddress(ip);
}
#if HAS_NETWORKING #if HAS_NETWORKING
if (!moduleConfig.mqtt.proxy_to_client_enabled) if (!moduleConfig.mqtt.proxy_to_client_enabled)
@ -392,14 +435,9 @@ void MQTT::reconnect()
pubSub.setClient(mqttClient); pubSub.setClient(mqttClient);
#endif #endif
String server = String(serverAddr); std::pair<std::string, uint16_t> hostAndPort = parseHostAndPort(serverAddr, serverPort);
int delimIndex = server.indexOf(':'); serverAddr = hostAndPort.first.c_str();
if (delimIndex > 0) { serverPort = hostAndPort.second;
String port = server.substring(delimIndex + 1, server.length());
server[delimIndex] = 0;
serverPort = port.toInt();
serverAddr = server.c_str();
}
pubSub.setServer(serverAddr, serverPort); pubSub.setServer(serverAddr, serverPort);
pubSub.setBufferSize(512); pubSub.setBufferSize(512);
@ -412,6 +450,9 @@ void MQTT::reconnect()
enabled = true; // Start running background process again enabled = true; // Start running background process again
runASAP = true; runASAP = true;
reconnectCount = 0; reconnectCount = 0;
#if !defined(ARCH_PORTDUINO)
isMqttServerAddressPrivate = isPrivateIpAddress(mqttClient.remoteIP());
#endif
publishNodeInfo(); publishNodeInfo();
sendSubscriptions(); sendSubscriptions();
@ -528,39 +569,37 @@ void MQTT::publishNodeInfo()
} }
void MQTT::publishQueuedMessages() void MQTT::publishQueuedMessages()
{ {
if (!mqttQueue.isEmpty()) { if (mqttQueue.isEmpty())
LOG_DEBUG("Publish enqueued MQTT message"); return;
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);
publish(topic.c_str(), bytes, numBytes, false); LOG_DEBUG("Publish enqueued MQTT message");
const std::unique_ptr<QueueEntry> 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) || \ #if !defined(ARCH_NRF52) || \
defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ### defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ###
if (moduleConfig.mqtt.json_enabled) { if (!moduleConfig.mqtt.json_enabled)
return;
// handle json topic // handle json topic
auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet); const DecodedServiceEnvelope env(entry->envBytes.data(), entry->envBytes.size());
if (jsonString.length() != 0) { 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; std::string topicJson;
if (env->packet->pki_encrypted) { if (env.packet->pki_encrypted) {
topicJson = jsonTopic + "PKI/" + owner.id; topicJson = jsonTopic + "PKI/" + owner.id;
} else { } else {
topicJson = jsonTopic + env->channel_id + "/" + owner.id; 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()); 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); publish(topicJson.c_str(), jsonString.c_str(), false);
}
}
#endif // ARCH_NRF52 NRF52_USE_JSON #endif // ARCH_NRF52 NRF52_USE_JSON
mqttPool.release(env);
}
} }
void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_MeshPacket &mp_decoded, ChannelIndex chIndex) void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_MeshPacket &mp_decoded, ChannelIndex chIndex)
@ -599,59 +638,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 // 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; 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 it was to a channel, check uplink enabled, else must be pki_encrypted
if ((ch.settings.uplink_enabled && !isPKIEncrypted) || isPKIEncrypted) { if (!(ch.settings.uplink_enabled || isPKIEncrypted))
return;
const char *channelId = isPKIEncrypted ? "PKI" : channels.getGlobalId(chIndex); 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 "); LOG_DEBUG("MQTT onSend - Publish ");
const meshtastic_MeshPacket *p;
if (moduleConfig.mqtt.encryption_enabled) { if (moduleConfig.mqtt.encryption_enabled) {
env->packet = (meshtastic_MeshPacket *)&mp_encrypted; p = &mp_encrypted;
LOG_DEBUG("encrypted message"); LOG_DEBUG("encrypted message");
} else if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) { } else if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
env->packet = (meshtastic_MeshPacket *)&mp_decoded; p = &mp_decoded;
LOG_DEBUG("portnum %i message", env->packet->decoded.portnum); LOG_DEBUG("portnum %i message", mp_decoded.decoded.portnum);
} else { } else {
LOG_DEBUG("nothing, pkt not decrypted"); LOG_DEBUG("nothing, pkt not decrypted");
mqttPool.release(env);
return; // Don't upload a still-encrypted PKI packet if not encryption_enabled return; // Don't upload a still-encrypted PKI packet if not encryption_enabled
} }
if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) { const meshtastic_ServiceEnvelope env = {
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); .packet = const_cast<meshtastic_MeshPacket *>(p), .channel_id = const_cast<char *>(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; std::string topic = cryptTopic + channelId + "/" + owner.id;
LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes);
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); publish(topic.c_str(), bytes, numBytes, false);
#if !defined(ARCH_NRF52) || \ #if !defined(ARCH_NRF52) || \
defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ### defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ###
if (moduleConfig.mqtt.json_enabled) { if (!moduleConfig.mqtt.json_enabled)
return;
// handle json topic // handle json topic
auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded); auto jsonString = MeshPacketSerializer::JsonSerialize(&mp_decoded);
if (jsonString.length() != 0) { if (jsonString.length() == 0)
return;
std::string topicJson = jsonTopic + channelId + "/" + owner.id; std::string topicJson = jsonTopic + channelId + "/" + owner.id;
LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
jsonString.c_str());
publish(topicJson.c_str(), jsonString.c_str(), false); publish(topicJson.c_str(), jsonString.c_str(), false);
}
}
#endif // ARCH_NRF52 NRF52_USE_JSON #endif // ARCH_NRF52 NRF52_USE_JSON
} else { } else {
LOG_INFO("MQTT not connected, queue packet"); LOG_INFO("MQTT not connected, queue packet");
QueueEntry *entry;
if (mqttQueue.numFree() == 0) { if (mqttQueue.numFree() == 0) {
LOG_WARN("MQTT queue is full, discard oldest"); LOG_WARN("MQTT queue is full, discard oldest");
meshtastic_ServiceEnvelope *d = mqttQueue.dequeuePtr(0); entry = mqttQueue.dequeuePtr(0);
if (d) } else {
mqttPool.release(d); entry = new QueueEntry;
} }
// make a copy of serviceEnvelope and queue it entry->topic = std::move(topic);
meshtastic_ServiceEnvelope *copied = mqttPool.allocCopy(*env); entry->envBytes.assign(bytes, numBytes);
assert(mqttQueue.enqueue(copied, 0)); assert(mqttQueue.enqueue(entry, 0));
}
mqttPool.release(env);
} }
} }
@ -660,9 +696,9 @@ void MQTT::perhapsReportToMap()
if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly())) if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly()))
return; return;
if (Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs)) { if (Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs))
return; return;
} else {
if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) { if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) {
last_report_to_map = millis(); last_report_to_map = millis();
if (map_position_precision == 0) if (map_position_precision == 0)
@ -672,11 +708,6 @@ void MQTT::perhapsReportToMap()
return; 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 // Allocate MeshPacket and fill it
meshtastic_MeshPacket *mp = packetPool.allocZeroed(); meshtastic_MeshPacket *mp = packetPool.allocZeroed();
mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag; mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag;
@ -710,87 +741,23 @@ void MQTT::perhapsReportToMap()
mapReport.num_online_local_nodes = nodeDB->getNumOnlineMeshNodes(true); mapReport.num_online_local_nodes = nodeDB->getNumOnlineMeshNodes(true);
// Encode MapReport message and set it to MeshPacket in ServiceEnvelope // Encode MapReport message into the MeshPacket
mp->decoded.payload.size = pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), mp->decoded.payload.size =
&meshtastic_MapReport_msg, &mapReport); 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); // 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()); LOG_INFO("MQTT Publish map report to %s", mapTopic.c_str());
publish(mapTopic.c_str(), bytes, numBytes, false); publish(mapTopic.c_str(), bytes, numBytes, false);
// Release the allocated memory for ServiceEnvelope and MeshPacket // Release the allocated memory for MeshPacket
mqttPool.release(se);
packetPool.release(mp); packetPool.release(mp);
// Update the last report time // Update the last report time
last_report_to_map = millis(); 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;
} }

View File

@ -5,7 +5,9 @@
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "mesh/Channels.h" #include "mesh/Channels.h"
#include "mesh/generated/meshtastic/mqtt.pb.h" #include "mesh/generated/meshtastic/mqtt.pb.h"
#if !defined(ARCH_NRF52) || NRF52_USE_JSON
#include "serialization/JSON.h" #include "serialization/JSON.h"
#endif
#if HAS_WIFI #if HAS_WIFI
#include <WiFiClient.h> #include <WiFiClient.h>
#if !defined(ARCH_PORTDUINO) #if !defined(ARCH_PORTDUINO)
@ -78,7 +80,11 @@ class MQTT : private concurrency::OSThread
void start() { setIntervalFromNow(0); }; void start() { setIntervalFromNow(0); };
protected: protected:
PointerQueue<meshtastic_ServiceEnvelope> mqttQueue; struct QueueEntry {
std::string topic;
std::basic_string<uint8_t> envBytes; // binary/pb_encode_to_bytes ServiceEnvelope
};
PointerQueue<QueueEntry> mqttQueue;
int reconnectCount = 0; int reconnectCount = 0;
@ -117,10 +123,6 @@ class MQTT : private concurrency::OSThread
// Check if we should report unencrypted information about our node for consumption by a map // Check if we should report unencrypted information about our node for consumption by a map
void perhapsReportToMap(); 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 /// 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; } // int preflightSleepCb(void *unused = NULL) { return pubSub.connected() ? 1 : 0; }
}; };

View File

@ -21,9 +21,12 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#include "platform/portduino/USBHal.h"
std::map<configNames, int> settingsMap; std::map<configNames, int> settingsMap;
std::map<configNames, std::string> settingsStrings; std::map<configNames, std::string> settingsStrings;
std::ofstream traceFile; std::ofstream traceFile;
Ch341Hal *ch341Hal = nullptr;
char *configPath = nullptr; char *configPath = nullptr;
char *optionMac = nullptr; char *optionMac = nullptr;
@ -104,7 +107,6 @@ void getMacAddr(uint8_t *dmac)
struct hci_dev_info di; struct hci_dev_info di;
di.dev_id = 0; di.dev_id = 0;
bdaddr_t bdaddr; bdaddr_t bdaddr;
char addr[18];
int btsock; int btsock;
btsock = socket(AF_BLUETOOTH, SOCK_RAW, 1); btsock = socket(AF_BLUETOOTH, SOCK_RAW, 1);
if (btsock < 0) { // If anything fails, just return with the default value 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}; 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); getMacAddr(dmac);
if (dmac[0] == 0 && dmac[1] == 0 && dmac[2] == 0 && dmac[3] == 0 && dmac[4] == 0 && dmac[5] == 0) { 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; std::cout << "*** Blank MAC Address not allowed!" << std::endl;
@ -225,6 +255,30 @@ void portduinoSetup()
// 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: Can we do this in the for loop above?
// TODO: If one of these fails, we should log and terminate // TODO: If one of these fails, we should log and terminate
if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) {
settingsMap[user] = RADIOLIB_NC;
}
}
if (settingsMap[displayPanel] != no_screen) {
if (settingsMap[displayCS] > 0)
initGPIOPin(settingsMap[displayCS], gpioChipName);
if (settingsMap[displayDC] > 0)
initGPIOPin(settingsMap[displayDC], gpioChipName);
if (settingsMap[displayBacklight] > 0)
initGPIOPin(settingsMap[displayBacklight], gpioChipName);
if (settingsMap[displayReset] > 0)
initGPIOPin(settingsMap[displayReset], gpioChipName);
}
if (settingsMap[touchscreenModule] != no_touchscreen) {
if (settingsMap[touchscreenCS] > 0)
initGPIOPin(settingsMap[touchscreenCS], gpioChipName);
if (settingsMap[touchscreenIRQ] > 0)
initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName);
}
// 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 (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;
@ -250,11 +304,6 @@ void portduinoSetup()
settingsMap[sx126x_ant_sw] = RADIOLIB_NC; 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 (settingsMap.count(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) { if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) {
settingsMap[rxen] = RADIOLIB_NC; settingsMap[rxen] = RADIOLIB_NC;
@ -265,25 +314,6 @@ void portduinoSetup()
settingsMap[txen] = RADIOLIB_NC; settingsMap[txen] = RADIOLIB_NC;
} }
} }
if (settingsMap[displayPanel] != no_screen) {
if (settingsMap[displayCS] > 0)
initGPIOPin(settingsMap[displayCS], gpioChipName);
if (settingsMap[displayDC] > 0)
initGPIOPin(settingsMap[displayDC], gpioChipName);
if (settingsMap[displayBacklight] > 0)
initGPIOPin(settingsMap[displayBacklight], gpioChipName);
if (settingsMap[displayReset] > 0)
initGPIOPin(settingsMap[displayReset], gpioChipName);
}
if (settingsMap[touchscreenModule] != no_touchscreen) {
if (settingsMap[touchscreenCS] > 0)
initGPIOPin(settingsMap[touchscreenCS], gpioChipName);
if (settingsMap[touchscreenIRQ] > 0)
initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName);
}
if (settingsStrings[spidev] != "") {
SPI.begin(settingsStrings[spidev].c_str()); SPI.begin(settingsStrings[spidev].c_str());
} }
if (settingsStrings[traceFilename] != "") { if (settingsStrings[traceFilename] != "") {
@ -378,20 +408,27 @@ bool loadConfig(const char *configPath)
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[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[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as<int>(2000000); settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as<int>(2000000);
settingsStrings[lora_usb_serial_num] = yamlConfig["Lora"]["USB_Serialnum"].as<std::string>("");
settingsMap[lora_usb_pid] = yamlConfig["Lora"]["USB_PID"].as<int>(0x5512);
settingsMap[lora_usb_vid] = yamlConfig["Lora"]["USB_VID"].as<int>(0x1A86);
settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as<std::string>("spidev0.0"); settingsStrings[spidev] = yamlConfig["Lora"]["spidev"].as<std::string>("spidev0.0");
if (settingsStrings[spidev] != "ch341") {
settingsStrings[spidev] = "/dev/" + settingsStrings[spidev];
if (settingsStrings[spidev].length() == 14) { if (settingsStrings[spidev].length() == 14) {
int x = settingsStrings[spidev].at(11) - '0'; int x = settingsStrings[spidev].at(11) - '0';
int y = settingsStrings[spidev].at(13) - '0'; int y = settingsStrings[spidev].at(13) - '0';
// Pretty sure this is always true
if (x >= 0 && x < 10 && y >= 0 && y < 10) { 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[spidev] = x + y << 4;
settingsMap[displayspidev] = settingsMap[spidev]; settingsMap[displayspidev] = settingsMap[spidev];
settingsMap[touchscreenspidev] = settingsMap[spidev]; settingsMap[touchscreenspidev] = settingsMap[spidev];
} }
} }
} }
}
if (yamlConfig["GPIO"]) { if (yamlConfig["GPIO"]) {
settingsMap[user] = yamlConfig["GPIO"]["User"].as<int>(RADIOLIB_NC); settingsMap[user] = yamlConfig["GPIO"]["User"].as<int>(RADIOLIB_NC);
} }

View File

@ -2,6 +2,8 @@
#include <fstream> #include <fstream>
#include <map> #include <map>
#include "platform/portduino/USBHal.h"
enum configNames { enum configNames {
use_sx1262, use_sx1262,
cs, cs,
@ -13,13 +15,15 @@ enum configNames {
rxen, rxen,
dio2_as_rf_switch, dio2_as_rf_switch,
dio3_tcxo_voltage, dio3_tcxo_voltage,
ch341Quirk,
use_rf95, use_rf95,
use_sx1280, use_sx1280,
use_lr1110, use_lr1110,
use_lr1120, use_lr1120,
use_lr1121, use_lr1121,
use_sx1268, use_sx1268,
lora_usb_serial_num,
lora_usb_pid,
lora_usb_vid,
user, user,
gpiochip, gpiochip,
spidev, spidev,
@ -69,6 +73,7 @@ enum { level_error, level_warn, level_info, level_debug, level_trace };
extern std::map<configNames, int> settingsMap; extern std::map<configNames, int> settingsMap;
extern std::map<configNames, std::string> settingsStrings; extern std::map<configNames, std::string> settingsStrings;
extern std::ofstream traceFile; extern std::ofstream traceFile;
extern Ch341Hal *ch341Hal;
int initGPIOPin(int pinNum, std::string gpioChipname); int initGPIOPin(int pinNum, std::string gpioChipname);
bool loadConfig(const char *configPath); bool loadConfig(const char *configPath);
static bool ends_with(std::string_view str, std::string_view suffix); static bool ends_with(std::string_view str, std::string_view suffix);

View File

@ -0,0 +1,194 @@
#ifndef PI_HAL_LGPIO_H
#define PI_HAL_LGPIO_H
// include RadioLib
#include "platform/portduino/PortduinoGlue.h"
#include <RadioLib.h>
#include <csignal>
#include <libpinedio-usb.h>
#include <unistd.h>
// 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