Merge remote-tracking branch 'origin/develop'

This commit is contained in:
Ben Meadors
2026-03-06 10:48:51 -06:00
60 changed files with 1443 additions and 164 deletions
+2
View File
@@ -202,6 +202,8 @@
#define HW_VENDOR meshtastic_HardwareModel_M5STACK_C6L
#elif defined(HELTEC_WIRELESS_TRACKER_V2)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_WIRELESS_TRACKER_V2
#elif defined(M5STACK_CARDPUTER_ADV)
#define HW_VENDOR meshtastic_HardwareModel_M5STACK_CARDPUTER_ADV
#else
#define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW
#endif
+5 -2
View File
@@ -33,7 +33,10 @@ bool AsyncUDP::writeTo(const uint8_t *data, size_t len, IPAddress ip, uint16_t p
if (!udp.beginPacket(ip, port))
return false;
udp.write(data, len);
return udp.endPacket();
isSending = true;
bool ok = udp.endPacket();
isSending = false;
return ok;
}
void AsyncUDP::close()
@@ -70,7 +73,7 @@ const uint8_t *AsyncUDPPacket::data()
int32_t AsyncUDP::runOnce()
{
if (_onPacket && udp.parsePacket() > 0) {
if (_onPacket && !isSending && udp.parsePacket() > 0) {
AsyncUDPPacket packet(udp);
_onPacket(packet);
}
+1
View File
@@ -31,6 +31,7 @@ class AsyncUDP : public Print, private concurrency::OSThread
private:
EthernetUDP udp;
uint16_t localPort;
volatile bool isSending = false;
std::function<void(AsyncUDPPacket)> _onPacket;
virtual int32_t runOnce() override;
};
+77 -9
View File
@@ -19,6 +19,7 @@
#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <stdexcept>
#include <sys/ioctl.h>
#include <unistd.h>
@@ -277,6 +278,24 @@ void portduinoSetup()
std::cout << "autoconf: Found Pi HAT+ " << hat_vendor << " " << autoconf_product << " at /proc/device-tree/hat"
<< std::endl;
// check for custom data fields
int i = 0;
while (access(("/proc/device-tree/hat/custom_" + std::to_string(i)).c_str(), R_OK) == 0) {
std::ifstream customFieldFile(("/proc/device-tree/hat/custom_" + std::to_string(i)).c_str());
if (customFieldFile.is_open()) {
std::string customFieldName;
std::string customFieldValue;
getline(customFieldFile, customFieldName, ' ');
getline(customFieldFile, customFieldValue, ' ');
customFieldFile.close();
printf("autoconf: Found hat+ custom field %s: %s\n", customFieldName.c_str(), customFieldValue.c_str());
portduino_config.hat_plus_custom_fields[customFieldName] = customFieldValue;
}
i++;
}
// potential TODO: Validate that this is a real UUID
std::ifstream hatUUID("/proc/device-tree/hat/uuid");
char uuid[38] = {0};
@@ -496,14 +515,19 @@ void portduinoSetup()
randomSeed(time(NULL));
std::string defaultGpioChipName = gpioChipName + std::to_string(portduino_config.lora_default_gpiochip);
std::set<int> used_pins;
for (const auto *i : portduino_config.all_pins) {
if (i->enabled && i->pin > max_GPIO)
if (i->enabled && i->pin > max_GPIO) {
max_GPIO = i->pin;
}
}
for (auto i : portduino_config.extra_pins) {
if (i.enabled && i.pin > max_GPIO)
if (i.enabled && i.pin > max_GPIO) {
max_GPIO = i.pin;
}
}
gpioInit(max_GPIO + 1); // Done here so we can inform Portduino how many GPIOs we need.
@@ -517,12 +541,18 @@ void portduinoSetup()
continue;
}
if (i->enabled) {
if (initGPIOPin(i->pin, gpioChipName + std::to_string(i->gpiochip), i->line) != ERRNO_OK) {
printf("Error setting pin number %d. It may not exist, or may already be in use.\n", i->line);
exit(EXIT_FAILURE);
if (used_pins.find(i->pin) != used_pins.end()) {
printf("Pin %d is in use for multiple purposes\n", i->pin);
} else {
if (initGPIOPin(i->pin, gpioChipName + std::to_string(i->gpiochip), i->line) != ERRNO_OK) {
printf("Error setting pin number %d. It may not exist, or may already be in use.\n", i->line);
exit(EXIT_FAILURE);
}
used_pins.insert(i->pin);
}
}
}
printf("Initializing extra pins\n");
for (auto i : portduino_config.extra_pins) {
// In the case of a ch341 Lora device, we don't want to touch the system GPIO lines for Lora
// Those GPIO are handled in our usermode driver instead.
@@ -530,9 +560,14 @@ void portduinoSetup()
continue;
}
if (i.enabled) {
if (initGPIOPin(i.pin, gpioChipName + std::to_string(i.gpiochip), i.line) != ERRNO_OK) {
printf("Error setting pin number %d. It may not exist, or may already be in use.\n", i.line);
exit(EXIT_FAILURE);
if (used_pins.find(i.pin) != used_pins.end()) {
printf("Pin %d is in use for multiple purposes\n", i.pin);
} else {
if (initGPIOPin(i.pin, gpioChipName + std::to_string(i.gpiochip), i.line) != ERRNO_OK) {
printf("Error setting pin number %d. It may not exist, or may already be in use.\n", i.line);
exit(EXIT_FAILURE);
}
used_pins.insert(i.pin);
}
}
}
@@ -556,6 +591,25 @@ void portduinoSetup()
// disable bias once finished
pinMode(portduino_config.lora_pa_detect_pin.pin, INPUT);
} else if (portduino_config.hat_plus_custom_fields.find("io_slot1") != portduino_config.hat_plus_custom_fields.end()) {
printf("Hat+ io_slot1 is %s\n", portduino_config.hat_plus_custom_fields["io_slot1"].c_str());
if (portduino_config.hat_plus_custom_fields["io_slot1"] != "RAK13302") {
std::cout << "Hat+ io_slot1 is not RAK13302, skipping PA curve" << std::endl;
portduino_config.num_pa_points = 1;
portduino_config.tx_gain_lora[0] = 0;
}
}
for (auto i : portduino_config.extra_pins) {
// In the case of a ch341 Lora device, we don't want to touch the system GPIO lines for Lora
// Those GPIO are handled in our usermode driver instead.
if (i.config_section == "Lora" && portduino_config.lora_spi_dev == "ch341") {
continue;
}
if (i.enabled && i.default_high) {
pinMode(i.pin, OUTPUT);
digitalWrite(i.pin, HIGH);
}
}
// Only initialize the radio pins when dealing with real, kernel controlled SPI hardware
@@ -576,7 +630,9 @@ void portduinoSetup()
}
} else if (portduino_config.JSONFilename != "") {
try {
JSONFile.open(portduino_config.JSONFilename, std::ios::out | std::ios::app);
if (portduino_config.JSONFileRotate == 0) {
JSONFile.open(portduino_config.JSONFilename, std::ios::out | std::ios::app);
}
} catch (std::ofstream::failure &e) {
std::cout << "*** JSONFile Exception " << e.what() << std::endl;
exit(EXIT_FAILURE);
@@ -633,6 +689,7 @@ bool loadConfig(const char *configPath)
}
portduino_config.traceFilename = yamlConfig["Logging"]["TraceFile"].as<std::string>("");
portduino_config.JSONFilename = yamlConfig["Logging"]["JSONFile"].as<std::string>("");
portduino_config.JSONFileRotate = yamlConfig["Logging"]["JSONFileRotate"].as<int>(0);
portduino_config.JSONFilter = (_meshtastic_PortNum)yamlConfig["Logging"]["JSONFilter"].as<int>(0);
if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "textmessage")
portduino_config.JSONFilter = meshtastic_PortNum_TEXT_MESSAGE_APP;
@@ -712,6 +769,17 @@ bool loadConfig(const char *configPath)
}
}
if (yamlConfig["Lora"]["Enable_Pins"]) {
for (auto extra_pin : yamlConfig["Lora"]["Enable_Pins"]) {
portduino_config.extra_pins.push_back(pinMapping());
portduino_config.extra_pins.back().config_section = "Lora";
portduino_config.extra_pins.back().config_name = "Enable_Pins";
portduino_config.extra_pins.back().enabled = true;
portduino_config.extra_pins.back().default_high = true;
readGPIOFromYaml(extra_pin, portduino_config.extra_pins.back());
}
}
portduino_config.spiSpeed = yamlConfig["Lora"]["spiSpeed"].as<int>(2000000);
portduino_config.lora_usb_serial_num = yamlConfig["Lora"]["USB_Serialnum"].as<std::string>("");
portduino_config.lora_usb_pid = yamlConfig["Lora"]["USB_PID"].as<int>(0x5512);
+7
View File
@@ -52,6 +52,7 @@ struct pinMapping {
int gpiochip;
int line;
bool enabled = false;
bool default_high = false;
};
extern std::ofstream traceFile;
@@ -164,6 +165,7 @@ extern struct portduino_config_struct {
bool ascii_logs_explicit = false;
std::string JSONFilename;
int JSONFileRotate = 0;
meshtastic_PortNum JSONFilter = (_meshtastic_PortNum)0;
// Webserver
@@ -195,6 +197,8 @@ extern struct portduino_config_struct {
int maxtophone = 100;
int MaxNodes = 200;
std::unordered_map<std::string, std::string> hat_plus_custom_fields;
pinMapping *all_pins[21] = {&lora_cs_pin,
&lora_irq_pin,
&lora_busy_pin,
@@ -469,6 +473,9 @@ extern struct portduino_config_struct {
out << YAML::Key << "TraceFile" << YAML::Value << traceFilename;
if (JSONFilename != "") {
out << YAML::Key << "JSONFile" << YAML::Value << JSONFilename;
if (JSONFileRotate != 0)
out << YAML::Key << "JSONFileRotate" << YAML::Value << JSONFileRotate;
if (JSONFilter == meshtastic_PortNum_TEXT_MESSAGE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "textmessage";
else if (JSONFilter == meshtastic_PortNum_TELEMETRY_APP)