mirror of
https://github.com/meshtastic/firmware.git
synced 2025-09-13 14:18:19 +00:00
Merge branch 'meshtastic:master' into master
This commit is contained in:
commit
ba53101d8c
@ -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}
|
||||||
@ -36,4 +37,4 @@ build_flags =
|
|||||||
-lstdc++fs
|
-lstdc++fs
|
||||||
-lbluetooth
|
-lbluetooth
|
||||||
-lgpiod
|
-lgpiod
|
||||||
-lyaml-cpp
|
-lyaml-cpp
|
@ -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.
|
||||||
|
11
bin/config.d/lora-meshstick-1262.yaml
Normal file
11
bin/config.d/lora-meshstick-1262.yaml
Normal 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
|
5
bin/config.d/lora-pinedio-usb-sx1262.yaml
Normal file
5
bin/config.d/lora-pinedio-usb-sx1262.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Lora:
|
||||||
|
Module: sx1262
|
||||||
|
CS: 0
|
||||||
|
IRQ: 10
|
||||||
|
spidev: ch341
|
@ -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;
|
||||||
@ -127,4 +128,4 @@ class ScanI2C
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool shouldSuppressScreen = false;
|
bool shouldSuppressScreen = false;
|
||||||
};
|
};
|
||||||
|
@ -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) {
|
||||||
logFoundDevice("INA260", (uint8_t)addr.address);
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFF), 2);
|
||||||
type = INA260;
|
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
|
} 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;
|
||||||
|
32
src/main.cpp
32
src/main.cpp
@ -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.
|
||||||
@ -241,7 +245,7 @@ void setup()
|
|||||||
// GPIO10 manages all peripheral power supplies
|
// GPIO10 manages all peripheral power supplies
|
||||||
// Turn on peripheral power immediately after MUC starts.
|
// Turn on peripheral power immediately after MUC starts.
|
||||||
// If some boards are turned on late, ESP32 will reset due to low voltage.
|
// 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)
|
// TF Card , Display backlight(AW9364DNR) , AN48841B(Trackball) , ES7210(Decoder)
|
||||||
pinMode(KB_POWERON, OUTPUT);
|
pinMode(KB_POWERON, OUTPUT);
|
||||||
digitalWrite(KB_POWERON, HIGH);
|
digitalWrite(KB_POWERON, HIGH);
|
||||||
@ -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);
|
||||||
@ -706,12 +710,16 @@ void setup()
|
|||||||
pinMode(LORA_CS, OUTPUT);
|
pinMode(LORA_CS, OUTPUT);
|
||||||
digitalWrite(LORA_CS, HIGH);
|
digitalWrite(LORA_CS, HIGH);
|
||||||
SPI1.begin(false);
|
SPI1.begin(false);
|
||||||
#else // HW_SPI1_DEVICE
|
#else // HW_SPI1_DEVICE
|
||||||
SPI.setSCK(LORA_SCK);
|
SPI.setSCK(LORA_SCK);
|
||||||
SPI.setTX(LORA_MOSI);
|
SPI.setTX(LORA_MOSI);
|
||||||
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()) {
|
||||||
@ -1265,4 +1275,4 @@ void loop()
|
|||||||
mainDelay.delay(delayMsec);
|
mainDelay.delay(delayMsec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
@ -265,13 +261,21 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
|
|||||||
fileToAppend.printf("??:??:??,"); // Time
|
fileToAppend.printf("??:??:??,"); // Time
|
||||||
}
|
}
|
||||||
|
|
||||||
fileToAppend.printf("%d,", getFrom(&mp)); // From
|
fileToAppend.printf("%d,", getFrom(&mp)); // From
|
||||||
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
|
||||||
fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat
|
if (gpsStatus->getIsConnected() || config.position.fixed_position) {
|
||||||
fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
|
fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat
|
||||||
fileToAppend.printf("%d,", gpsStatus->getAltitude()); // RX Altitude
|
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
|
fileToAppend.printf("%f,", mp.rx_snr); // RX SNR
|
||||||
|
|
||||||
@ -292,4 +296,4 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
@ -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)
|
||||||
// handle json topic
|
return;
|
||||||
auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet);
|
|
||||||
if (jsonString.length() != 0) {
|
// handle json topic
|
||||||
std::string topicJson;
|
const DecodedServiceEnvelope env(entry->envBytes.data(), entry->envBytes.size());
|
||||||
if (env->packet->pki_encrypted) {
|
if (!env.validDecode || env.packet == NULL || env.channel_id == NULL)
|
||||||
topicJson = jsonTopic + "PKI/" + owner.id;
|
return;
|
||||||
} else {
|
|
||||||
topicJson = jsonTopic + env->channel_id + "/" + owner.id;
|
auto jsonString = MeshPacketSerializer::JsonSerialize(env.packet);
|
||||||
}
|
if (jsonString.length() == 0)
|
||||||
LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
|
return;
|
||||||
publish(topicJson.c_str(), jsonString.c_str(), false);
|
|
||||||
}
|
std::string topicJson;
|
||||||
}
|
if (env.packet->pki_encrypted) {
|
||||||
#endif // ARCH_NRF52 NRF52_USE_JSON
|
topicJson = jsonTopic + "PKI/" + owner.id;
|
||||||
mqttPool.release(env);
|
} 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)
|
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))
|
||||||
const char *channelId = isPKIEncrypted ? "PKI" : channels.getGlobalId(chIndex);
|
return;
|
||||||
|
const char *channelId = isPKIEncrypted ? "PKI" : channels.getGlobalId(chIndex);
|
||||||
|
|
||||||
meshtastic_ServiceEnvelope *env = mqttPool.allocZeroed();
|
LOG_DEBUG("MQTT onSend - Publish ");
|
||||||
env->channel_id = (char *)channelId;
|
const meshtastic_MeshPacket *p;
|
||||||
env->gateway_id = owner.id;
|
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 ");
|
const meshtastic_ServiceEnvelope env = {
|
||||||
if (moduleConfig.mqtt.encryption_enabled) {
|
.packet = const_cast<meshtastic_MeshPacket *>(p), .channel_id = const_cast<char *>(channelId), .gateway_id = owner.id};
|
||||||
env->packet = (meshtastic_MeshPacket *)&mp_encrypted;
|
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, &env);
|
||||||
LOG_DEBUG("encrypted message");
|
std::string topic = cryptTopic + channelId + "/" + owner.id;
|
||||||
} 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
|
|
||||||
}
|
|
||||||
|
|
||||||
if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) {
|
if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) {
|
||||||
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
|
LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes);
|
||||||
std::string topic = cryptTopic + channelId + "/" + owner.id;
|
publish(topic.c_str(), bytes, numBytes, false);
|
||||||
LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes);
|
|
||||||
|
|
||||||
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)
|
||||||
// handle json topic
|
return;
|
||||||
auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded);
|
// handle json topic
|
||||||
if (jsonString.length() != 0) {
|
auto jsonString = MeshPacketSerializer::JsonSerialize(&mp_decoded);
|
||||||
std::string topicJson = jsonTopic + channelId + "/" + owner.id;
|
if (jsonString.length() == 0)
|
||||||
LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(),
|
return;
|
||||||
jsonString.c_str());
|
std::string topicJson = jsonTopic + channelId + "/" + owner.id;
|
||||||
publish(topicJson.c_str(), jsonString.c_str(), false);
|
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
|
#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 {
|
} else {
|
||||||
LOG_INFO("MQTT not connected, queue packet");
|
entry = new QueueEntry;
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
mqttPool.release(env);
|
entry->topic = std::move(topic);
|
||||||
|
entry->envBytes.assign(bytes, numBytes);
|
||||||
|
assert(mqttQueue.enqueue(entry, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,137 +696,68 @@ 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)) {
|
|
||||||
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
|
if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) {
|
||||||
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
|
|
||||||
last_report_to_map = millis();
|
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)
|
||||||
bool MQTT::isPrivateIpAddress(const char address[])
|
LOG_WARN("MQTT Map report enabled, but no position available");
|
||||||
{
|
return;
|
||||||
// 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.
|
// Allocate MeshPacket and fill it
|
||||||
// Some limited validation is done.
|
meshtastic_MeshPacket *mp = packetPool.allocZeroed();
|
||||||
// Even if it's not a valid IP address, we will know it's not a domain.
|
mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag;
|
||||||
bool hasColon = false;
|
mp->from = nodeDB->getNodeNum();
|
||||||
int numDots = 0;
|
mp->to = NODENUM_BROADCAST;
|
||||||
for (size_t i = 0; i < length; i++) {
|
mp->decoded.portnum = meshtastic_PortNum_MAP_REPORT_APP;
|
||||||
if (!isdigit(address[i]) && address[i] != '.' && address[i] != ':') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dots can't be the first character, immediately follow another dot,
|
// Fill MapReport message
|
||||||
// occur more than 3 times, or occur after a colon.
|
meshtastic_MapReport mapReport = meshtastic_MapReport_init_default;
|
||||||
if (address[i] == '.') {
|
memcpy(mapReport.long_name, owner.long_name, sizeof(owner.long_name));
|
||||||
if (++numDots > 3 || i == 0 || address[i - 1] == '.' || hasColon) {
|
memcpy(mapReport.short_name, owner.short_name, sizeof(owner.short_name));
|
||||||
return false;
|
mapReport.role = config.device.role;
|
||||||
}
|
mapReport.hw_model = owner.hw_model;
|
||||||
}
|
strncpy(mapReport.firmware_version, optstr(APP_VERSION), sizeof(mapReport.firmware_version));
|
||||||
// There can only be a single colon, and it can only occur after 3 dots
|
mapReport.region = config.lora.region;
|
||||||
else if (address[i] == ':') {
|
mapReport.modem_preset = config.lora.modem_preset;
|
||||||
if (hasColon || numDots < 3) {
|
mapReport.has_default_channel = channels.hasDefaultChannel();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasColon = true;
|
// 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;
|
||||||
|
|
||||||
// Final validation for IPv4 address and port format.
|
mapReport.num_online_local_nodes = nodeDB->getNumOnlineMeshNodes(true);
|
||||||
// Note that the values of octets haven't been tested, only the address format.
|
|
||||||
if (numDots != 3) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the easy ones first.
|
// Encode MapReport message into the MeshPacket
|
||||||
if (strcmp(address, "127.0.0.1") == 0 || strncmp(address, "10.", 3) == 0 || strncmp(address, "192.168", 7) == 0 ||
|
mp->decoded.payload.size =
|
||||||
strncmp(address, "169.254", 7) == 0) {
|
pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), &meshtastic_MapReport_msg, &mapReport);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if it's definitely not a 172 address.
|
// Encode the MeshPacket into a binary ServiceEnvelope and publish
|
||||||
if (strncmp(address, "172", 3) != 0) {
|
const meshtastic_ServiceEnvelope se = {
|
||||||
return false;
|
.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);
|
||||||
|
|
||||||
// We know it's a 172 address, now see if the second octet is 2 digits.
|
LOG_INFO("MQTT Publish map report to %s", mapTopic.c_str());
|
||||||
if (address[6] != '.') {
|
publish(mapTopic.c_str(), bytes, numBytes, false);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the second octet into a secondary buffer we can null-terminate and parse.
|
// Release the allocated memory for MeshPacket
|
||||||
char octet2[3];
|
packetPool.release(mp);
|
||||||
strncpy(octet2, address + 4, 2);
|
|
||||||
octet2[2] = 0;
|
|
||||||
|
|
||||||
int octet2Num = atoi(octet2);
|
// Update the last report time
|
||||||
return octet2Num >= 16 && octet2Num <= 31;
|
last_report_to_map = millis();
|
||||||
}
|
}
|
@ -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,14 +123,10 @@ 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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
void mqttInit();
|
void mqttInit();
|
||||||
|
|
||||||
extern MQTT *mqtt;
|
extern MQTT *mqtt;
|
@ -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,47 +255,11 @@ 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(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 (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) {
|
||||||
if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) {
|
if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) {
|
||||||
settingsMap[user] = RADIOLIB_NC;
|
settingsMap[user] = RADIOLIB_NC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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[displayPanel] != no_screen) {
|
||||||
if (settingsMap[displayCS] > 0)
|
if (settingsMap[displayCS] > 0)
|
||||||
initGPIOPin(settingsMap[displayCS], gpioChipName);
|
initGPIOPin(settingsMap[displayCS], gpioChipName);
|
||||||
@ -283,7 +277,43 @@ void portduinoSetup()
|
|||||||
initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName);
|
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());
|
SPI.begin(settingsStrings[spidev].c_str());
|
||||||
}
|
}
|
||||||
if (settingsStrings[traceFilename] != "") {
|
if (settingsStrings[traceFilename] != "") {
|
||||||
@ -378,17 +408,24 @@ 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].length() == 14) {
|
if (settingsStrings[spidev] != "ch341") {
|
||||||
int x = settingsStrings[spidev].at(11) - '0';
|
settingsStrings[spidev] = "/dev/" + settingsStrings[spidev];
|
||||||
int y = settingsStrings[spidev].at(13) - '0';
|
if (settingsStrings[spidev].length() == 14) {
|
||||||
if (x >= 0 && x < 10 && y >= 0 && y < 10) {
|
int x = settingsStrings[spidev].at(11) - '0';
|
||||||
settingsMap[spidev] = x + y << 4;
|
int y = settingsStrings[spidev].at(13) - '0';
|
||||||
settingsMap[displayspidev] = settingsMap[spidev];
|
// Pretty sure this is always true
|
||||||
settingsMap[touchscreenspidev] = settingsMap[spidev];
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,8 +73,9 @@ 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);
|
||||||
void getMacAddr(uint8_t *dmac);
|
void getMacAddr(uint8_t *dmac);
|
||||||
bool MAC_from_string(std::string mac_str, uint8_t *dmac);
|
bool MAC_from_string(std::string mac_str, uint8_t *dmac);
|
||||||
|
194
src/platform/portduino/USBHal.h
Normal file
194
src/platform/portduino/USBHal.h
Normal 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
|
Loading…
Reference in New Issue
Block a user