From 70296b47bc9b4a026f3aceb73d8afb8448249160 Mon Sep 17 00:00:00 2001 From: Patrick Siegl <3261314+psiegl@users.noreply.github.com> Date: Sun, 12 Jan 2025 20:40:25 +0100 Subject: [PATCH 01/19] Multi gpiochip support for native environment (#5743) * For each GPIO PIN, allow to specify gpiochip and line * Added support for LLCC68 in native env. * Removed one if by employing && * Fix for log, as std::string and not const char* * Remove CH341 flag, enabling it for all LoRa chips * Provide a default example --------- Co-authored-by: Ben Meadors Co-authored-by: Jonathan Bennett --- bin/config-dist.yaml | 45 +++++- src/main.cpp | 141 ++++++------------- src/mesh/RF95Interface.cpp | 22 +-- src/mesh/SX126xInterface.cpp | 12 +- src/mesh/SX128xInterface.cpp | 42 +++--- src/platform/portduino/PortduinoGlue.cpp | 167 ++++++++++++----------- src/platform/portduino/PortduinoGlue.h | 37 +++-- 7 files changed, 237 insertions(+), 229 deletions(-) diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index e68b01ba3..c8f181308 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -23,6 +23,47 @@ Lora: # Busy: 20 # Reset: 18 +### The Radxa Zero 3E/W employs multiple gpio chips. +### Each gpio pin must be unique, but can be assigned to a specific gpio chip and line. +### In case solely a no. is given, the default gpio chip and pin == line will be employed. +### +# Module: sx1262 # Radxa Zero 3E/W + Ebyte E22-900M30S +# DIO2_AS_RF_SWITCH: true +# DIO3_TCXO_VOLTAGE: 1.8 +# CS: # NSS PIN_24 -> chip 4, line 22 +# pin: 24 +# gpiochip: 4 +# line: 22 +# SCK: # SCK PIN_23 -> chip 4, line 18 +# pin: 23 +# gpiochip: 4 +# line: 18 +# Busy: # BUSY PIN_29 -> chip 3!, line 11 +# pin: 29 +# gpiochip: 3 +# line: 11 +# MOSI: # MOSI PIN_19 -> chip 4, line 19 +# pin: 19 +# gpiochip: 4 +# line: 19 +# MISO: # MISO PIN_21 -> chip 4, line 21 +# pin: 21 +# gpiochip: 4 +# line: 21 +# Reset: # NRST PIN_27 -> chip 4, line 10 +# pin: 27 +# gpiochip: 4 +# line: 10 +# IRQ: # DIO1 PIN_28 -> chip 4, line 11 +# pin: 28 +# gpiochip: 4 +# line: 11 +# RXen: # RXEN PIN_22 -> chip 3!, line 17 +# pin: 22 +# gpiochip: 3 +# line: 17 +# TXen: RADIOLIB_NC # TXEN no PIN, no line, fallback to default gpio chip + # Module: sx1268 # SX1268-based modules, tested with Ebyte E22 400M33S # CS: 21 # IRQ: 16 @@ -39,7 +80,7 @@ Lora: # spiSpeed: 2000000 -### Set gpio chip to use in /dev/. Defaults to 0. +### Set default/fallback gpio chip to use in /dev/. Defaults to 0. ### Notably the Raspberry Pi 5 puts the GPIO header on gpiochip4 # gpiochip: 4 @@ -147,4 +188,4 @@ General: MaxMessageQueue: 100 ConfigDirectory: /etc/meshtasticd/config.d/ # MACAddress: AA:BB:CC:DD:EE:FF -# MACAddressSource: eth0 \ No newline at end of file +# MACAddressSource: eth0 diff --git a/src/main.cpp b/src/main.cpp index 4a642ef6d..338fca5c1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -826,116 +826,61 @@ void setup() #endif #ifdef ARCH_PORTDUINO - if (settingsMap[use_sx1262]) { - if (!rIf) { - LOG_DEBUG("Activate sx1262 radio on SPI port %s", settingsStrings[spidev].c_str()); + const struct { configNames cfgName; + std::string strName; + } loraModules[] = { + { use_rf95, "RF95" }, + { use_sx1262, "sx1262" }, + { use_sx1268, "sx1268" }, + { use_sx1280, "sx1280" }, + { use_lr1110, "lr1110" }, + { use_lr1120, "lr1120" }, + { use_lr1121, "lr1121" }, + { use_llcc68, "LLCC68" } + }; + // as one can't use a function pointer to the class constructor: + auto loraModuleInterface = [](configNames cfgName, LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) { + switch (cfgName) { + case use_rf95: + return (RadioInterface*)new RF95Interface(hal, cs, irq, rst, busy); + case use_sx1262: + return (RadioInterface*)new SX1262Interface(hal, cs, irq, rst, busy); + case use_sx1268: + return (RadioInterface*)new SX1268Interface(hal, cs, irq, rst, busy); + case use_sx1280: + return (RadioInterface*)new SX1280Interface(hal, cs, irq, rst, busy); + case use_lr1110: + return (RadioInterface*)new LR1110Interface(hal, cs, irq, rst, busy); + case use_lr1120: + return (RadioInterface*)new LR1120Interface(hal, cs, irq, rst, busy); + case use_lr1121: + return (RadioInterface*)new LR1121Interface(hal, cs, irq, rst, busy); + case use_llcc68: + return (RadioInterface*)new LLCC68Interface(hal, cs, irq, rst, busy); + default: + assert(0); // shouldn't happen + return (RadioInterface*)nullptr; + } + }; + for (auto& loraModule : loraModules) { + if (settingsMap[loraModule.cfgName] && !rIf) { + LOG_DEBUG("Activate %s radio on SPI port %s", loraModule.strName.c_str(), settingsStrings[spidev].c_str()); if (settingsStrings[spidev] == "ch341") { RadioLibHAL = ch341Hal; } else { RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); } - rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); + rIf = loraModuleInterface(loraModule.cfgName, (LockingArduinoHal *)RadioLibHAL, settingsMap[cs_pin], settingsMap[irq_pin], settingsMap[reset_pin], settingsMap[busy_pin]); if (!rIf->init()) { - LOG_WARN("No SX1262 radio"); - delete rIf; - exit(EXIT_FAILURE); - } else { - LOG_INFO("SX1262 init success"); - } - } - } else if (settingsMap[use_rf95]) { - if (!rIf) { - LOG_DEBUG("Activate rf95 radio on SPI port %s", settingsStrings[spidev].c_str()); - RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No RF95 radio"); + LOG_WARN("No %s radio", loraModule.strName.c_str()); delete rIf; rIf = NULL; exit(EXIT_FAILURE); } else { - LOG_INFO("RF95 init success"); - } - } - } else if (settingsMap[use_sx1280]) { - if (!rIf) { - LOG_DEBUG("Activate sx1280 radio on SPI port %s", settingsStrings[spidev].c_str()); - RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new SX1280Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No SX1280 radio"); - delete rIf; - rIf = NULL; - exit(EXIT_FAILURE); - } else { - LOG_INFO("SX1280 init success"); - } - } - } else if (settingsMap[use_lr1110]) { - if (!rIf) { - LOG_DEBUG("Activate lr1110 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new LR1110Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No LR1110 radio"); - delete rIf; - rIf = NULL; - exit(EXIT_FAILURE); - } else { - LOG_INFO("LR1110 init success"); - } - } - } else if (settingsMap[use_lr1120]) { - if (!rIf) { - LOG_DEBUG("Activate lr1120 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new LR1120Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No LR1120 radio"); - delete rIf; - rIf = NULL; - exit(EXIT_FAILURE); - } else { - LOG_INFO("LR1120 init success"); - } - } - } else if (settingsMap[use_lr1121]) { - if (!rIf) { - LOG_DEBUG("Activate lr1121 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new LR1121Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No LR1121 radio"); - delete rIf; - rIf = NULL; - exit(EXIT_FAILURE); - } else { - LOG_INFO("LR1121 init success"); - } - } - } else if (settingsMap[use_sx1268]) { - if (!rIf) { - LOG_DEBUG("Activate sx1268 radio on SPI port %s", settingsStrings[spidev].c_str()); - RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new SX1268Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No SX1268 radio"); - delete rIf; - rIf = NULL; - exit(EXIT_FAILURE); - } else { - LOG_INFO("SX1268 init success"); + LOG_INFO("%s init success", loraModule.strName.c_str()); } } } - #elif defined(HW_SPI1_DEVICE) LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI1, spiSettings); #else // HW_SPI1_DEVICE @@ -1280,4 +1225,4 @@ void loop() mainDelay.delay(delayMsec); } } -#endif \ No newline at end of file +#endif diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index 9ef045099..d4d9ad23c 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -91,16 +91,16 @@ void RF95Interface::setTransmitEnable(bool txon) #ifdef RF95_TXEN digitalWrite(RF95_TXEN, txon ? 1 : 0); #elif ARCH_PORTDUINO - if (settingsMap[txen] != RADIOLIB_NC) { - digitalWrite(settingsMap[txen], txon ? 1 : 0); + if (settingsMap[txen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[txen_pin], txon ? 1 : 0); } #endif #ifdef RF95_RXEN digitalWrite(RF95_RXEN, txon ? 0 : 1); #elif ARCH_PORTDUINO - if (settingsMap[rxen] != RADIOLIB_NC) { - digitalWrite(settingsMap[rxen], txon ? 0 : 1); + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[rxen_pin], txon ? 0 : 1); } #endif } @@ -164,13 +164,13 @@ bool RF95Interface::init() digitalWrite(RF95_RXEN, 1); #endif #if ARCH_PORTDUINO - if (settingsMap[txen] != RADIOLIB_NC) { - pinMode(settingsMap[txen], OUTPUT); - digitalWrite(settingsMap[txen], 0); + if (settingsMap[txen_pin] != RADIOLIB_NC) { + pinMode(settingsMap[txen_pin], OUTPUT); + digitalWrite(settingsMap[txen_pin], 0); } - if (settingsMap[rxen] != RADIOLIB_NC) { - pinMode(settingsMap[rxen], OUTPUT); - digitalWrite(settingsMap[rxen], 0); + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + pinMode(settingsMap[rxen_pin], OUTPUT); + digitalWrite(settingsMap[rxen_pin], 0); } #endif setTransmitEnable(false); @@ -337,4 +337,4 @@ bool RF95Interface::sleep() return true; } -#endif \ No newline at end of file +#endif diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index ed0267c5b..b13bb6faf 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -51,9 +51,9 @@ template bool SX126xInterface::init() #if ARCH_PORTDUINO float tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; - if (settingsMap[sx126x_ant_sw] != RADIOLIB_NC) { - digitalWrite(settingsMap[sx126x_ant_sw], HIGH); - pinMode(settingsMap[sx126x_ant_sw], OUTPUT); + if (settingsMap[sx126x_ant_sw_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[sx126x_ant_sw_pin], HIGH); + pinMode(settingsMap[sx126x_ant_sw_pin], OUTPUT); } // FIXME: correct logic to default to not using TCXO if no voltage is specified for SX126X_DIO3_TCXO_VOLTAGE #elif !defined(SX126X_DIO3_TCXO_VOLTAGE) @@ -121,8 +121,8 @@ template bool SX126xInterface::init() // no effect #if ARCH_PORTDUINO if (res == RADIOLIB_ERR_NONE) { - LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen], settingsMap[txen]); - lora.setRfSwitchPins(settingsMap[rxen], settingsMap[txen]); + LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen_pin], settingsMap[txen_pin]); + lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]); } #else #ifndef SX126X_RXEN @@ -341,4 +341,4 @@ template bool SX126xInterface::sleep() return true; } -#endif \ No newline at end of file +#endif diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index 013164bca..ee3408456 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -38,13 +38,13 @@ template bool SX128xInterface::init() #endif #if ARCH_PORTDUINO - if (settingsMap[rxen] != RADIOLIB_NC) { - pinMode(settingsMap[rxen], OUTPUT); - digitalWrite(settingsMap[rxen], LOW); // Set low before becoming an output + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + pinMode(settingsMap[rxen_pin], OUTPUT); + digitalWrite(settingsMap[rxen_pin], LOW); // Set low before becoming an output } - if (settingsMap[txen] != RADIOLIB_NC) { - pinMode(settingsMap[txen], OUTPUT); - digitalWrite(settingsMap[txen], LOW); // Set low before becoming an output + if (settingsMap[txen_pin] != RADIOLIB_NC) { + pinMode(settingsMap[txen_pin], OUTPUT); + digitalWrite(settingsMap[txen_pin], LOW); // Set low before becoming an output } #else #if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode @@ -93,8 +93,8 @@ template bool SX128xInterface::init() lora.setRfSwitchPins(SX128X_RXEN, SX128X_TXEN); } #elif ARCH_PORTDUINO - if (res == RADIOLIB_ERR_NONE && settingsMap[rxen] != RADIOLIB_NC && settingsMap[txen] != RADIOLIB_NC) { - lora.setRfSwitchPins(settingsMap[rxen], settingsMap[txen]); + if (res == RADIOLIB_ERR_NONE && settingsMap[rxen_pin] != RADIOLIB_NC && settingsMap[txen_pin] != RADIOLIB_NC) { + lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]); } #endif @@ -174,11 +174,11 @@ template void SX128xInterface::setStandby() LOG_ERROR("SX128x standby %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); #if ARCH_PORTDUINO - if (settingsMap[rxen] != RADIOLIB_NC) { - digitalWrite(settingsMap[rxen], LOW); + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[rxen_pin], LOW); } - if (settingsMap[txen] != RADIOLIB_NC) { - digitalWrite(settingsMap[txen], LOW); + if (settingsMap[txen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[txen_pin], LOW); } #else #if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn off RX and TX power @@ -210,11 +210,11 @@ template void SX128xInterface::addReceiveMetadata(meshtastic_Mes template void SX128xInterface::configHardwareForSend() { #if ARCH_PORTDUINO - if (settingsMap[txen] != RADIOLIB_NC) { - digitalWrite(settingsMap[txen], HIGH); + if (settingsMap[txen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[txen_pin], HIGH); } - if (settingsMap[rxen] != RADIOLIB_NC) { - digitalWrite(settingsMap[rxen], LOW); + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[rxen_pin], LOW); } #else @@ -241,11 +241,11 @@ template void SX128xInterface::startReceive() setStandby(); #if ARCH_PORTDUINO - if (settingsMap[rxen] != RADIOLIB_NC) { - digitalWrite(settingsMap[rxen], HIGH); + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[rxen_pin], HIGH); } - if (settingsMap[txen] != RADIOLIB_NC) { - digitalWrite(settingsMap[txen], LOW); + if (settingsMap[txen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[txen_pin], LOW); } #else @@ -315,4 +315,4 @@ template bool SX128xInterface::sleep() return true; } -#endif \ No newline at end of file +#endif diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index b042510f5..e75112235 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -134,13 +134,13 @@ void portduinoSetup() { printf("Set up Meshtastic on Portduino...\n"); int max_GPIO = 0; - const configNames GPIO_lines[] = {cs, - irq, - busy, - reset, - sx126x_ant_sw, - txen, - rxen, + const configNames GPIO_lines[] = {cs_pin, + irq_pin, + busy_pin, + reset_pin, + sx126x_ant_sw_pin, + txen_pin, + rxen_pin, displayDC, displayCS, displayBacklight, @@ -247,7 +247,7 @@ void portduinoSetup() // Rather important to set this, if not running simulated. randomSeed(time(NULL)); - gpioChipName += std::to_string(settingsMap[gpiochip]); + std::string defaultGpioChipName = gpioChipName + std::to_string(settingsMap[default_gpiochip]); for (configNames i : GPIO_lines) { if (settingsMap.count(i) && settingsMap[i] > max_GPIO) @@ -260,62 +260,46 @@ void portduinoSetup() // TODO: Can we do this in the for loop above? // 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) { + if (initGPIOPin(settingsMap[user], defaultGpioChipName, settingsMap[user]) != ERRNO_OK) { settingsMap[user] = RADIOLIB_NC; } } if (settingsMap[displayPanel] != no_screen) { if (settingsMap[displayCS] > 0) - initGPIOPin(settingsMap[displayCS], gpioChipName); + initGPIOPin(settingsMap[displayCS], defaultGpioChipName, settingsMap[displayCS]); if (settingsMap[displayDC] > 0) - initGPIOPin(settingsMap[displayDC], gpioChipName); + initGPIOPin(settingsMap[displayDC], defaultGpioChipName, settingsMap[displayDC]); if (settingsMap[displayBacklight] > 0) - initGPIOPin(settingsMap[displayBacklight], gpioChipName); + initGPIOPin(settingsMap[displayBacklight], defaultGpioChipName, settingsMap[displayBacklight]); if (settingsMap[displayReset] > 0) - initGPIOPin(settingsMap[displayReset], gpioChipName); + initGPIOPin(settingsMap[displayReset], defaultGpioChipName, settingsMap[displayReset]); } if (settingsMap[touchscreenModule] != no_touchscreen) { if (settingsMap[touchscreenCS] > 0) - initGPIOPin(settingsMap[touchscreenCS], gpioChipName); + initGPIOPin(settingsMap[touchscreenCS], defaultGpioChipName, settingsMap[touchscreenCS]); if (settingsMap[touchscreenIRQ] > 0) - initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName); + initGPIOPin(settingsMap[touchscreenIRQ], defaultGpioChipName, settingsMap[touchscreenIRQ]); } // 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; + const struct { configNames pin; configNames gpiochip; configNames line; } pinMappings[] = { + { cs_pin, cs_gpiochip, cs_line }, + { irq_pin, irq_gpiochip, irq_line }, + { busy_pin, busy_gpiochip, busy_line }, + { reset_pin, reset_gpiochip, reset_line }, + { rxen_pin, rxen_gpiochip, rxen_line }, + { txen_pin, txen_gpiochip, txen_line }, + { sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line } + }; + for (auto& pinMap : pinMappings) { + auto setMapIter = settingsMap.find(pinMap.pin); + if (setMapIter != settingsMap.end() && setMapIter->second != RADIOLIB_NC) { + if (initGPIOPin(setMapIter->second, gpioChipName + std::to_string(settingsMap[pinMap.gpiochip]), settingsMap[pinMap.line] ) != ERRNO_OK) { + settingsMap[pinMap.pin] = RADIOLIB_NC; + settingsMap[pinMap.gpiochip] = RADIOLIB_NC; + settingsMap[pinMap.line] = RADIOLIB_NC; + } } } SPI.begin(settingsStrings[spidev].c_str()); @@ -332,13 +316,13 @@ void portduinoSetup() return; } -int initGPIOPin(int pinNum, const std::string gpioChipName) +int initGPIOPin(int pinNum, const std::string gpioChipName, int line) { #ifdef PORTDUINO_LINUX_HARDWARE std::string gpio_name = "GPIO" + std::to_string(pinNum); try { GPIOPin *csPin; - csPin = new LinuxGPIOPin(pinNum, gpioChipName.c_str(), pinNum, gpio_name.c_str()); + csPin = new LinuxGPIOPin(pinNum, gpioChipName.c_str(), line, gpio_name.c_str()); csPin->setSilent(); gpioBind(csPin); return ERRNO_OK; @@ -376,42 +360,65 @@ bool loadConfig(const char *configPath) } } if (yamlConfig["Lora"]) { - settingsMap[use_sx1262] = false; - settingsMap[use_rf95] = false; - settingsMap[use_sx1280] = false; - settingsMap[use_lr1110] = false; - settingsMap[use_lr1120] = false; - settingsMap[use_lr1121] = false; - settingsMap[use_sx1268] = false; - - if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1262") { - settingsMap[use_sx1262] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "RF95") { - settingsMap[use_rf95] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1280") { - settingsMap[use_sx1280] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "lr1110") { - settingsMap[use_lr1110] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "lr1120") { - settingsMap[use_lr1120] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "lr1121") { - settingsMap[use_lr1121] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1268") { - settingsMap[use_sx1268] = true; + const struct { configNames cfgName; std::string strName; } loraModules[] = { + { use_rf95, "RF95" }, + { use_sx1262, "sx1262" }, + { use_sx1268, "sx1268" }, + { use_sx1280, "sx1280" }, + { use_lr1110, "lr1110" }, + { use_lr1120, "lr1120" }, + { use_lr1121, "lr1121" }, + { use_llcc68, "LLCC68" } + }; + for (auto& loraModule : loraModules) { + settingsMap[loraModule.cfgName] = false; } + if (yamlConfig["Lora"]["Module"]) { + for (auto& loraModule : loraModules) { + if (yamlConfig["Lora"]["Module"].as("") == loraModule.strName) { + settingsMap[loraModule.cfgName] = true; + break; + } + } + } + settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as(false); settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(0) * 1000; if (settingsMap[dio3_tcxo_voltage] == 0 && yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(false)) { settingsMap[dio3_tcxo_voltage] = 1800; // default millivolts for "true" } - settingsMap[cs] = yamlConfig["Lora"]["CS"].as(RADIOLIB_NC); - settingsMap[irq] = yamlConfig["Lora"]["IRQ"].as(RADIOLIB_NC); - settingsMap[busy] = yamlConfig["Lora"]["Busy"].as(RADIOLIB_NC); - settingsMap[reset] = yamlConfig["Lora"]["Reset"].as(RADIOLIB_NC); - settingsMap[txen] = yamlConfig["Lora"]["TXen"].as(RADIOLIB_NC); - settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as(RADIOLIB_NC); - settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as(RADIOLIB_NC); - settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0); + + // backwards API compatibility and to globally set gpiochip once + int defaultGpioChip = settingsMap[default_gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0); + + const struct { configNames pin; + configNames gpiochip; + configNames line; + std::string strName; } pinMappings[] = { + { cs_pin, cs_gpiochip, cs_line, "CS" }, + { irq_pin, irq_gpiochip, irq_line, "IRQ" }, + { busy_pin, busy_gpiochip, busy_line, "Busy" }, + { reset_pin, reset_gpiochip, reset_line, "Reset" }, + { txen_pin, txen_gpiochip, txen_line, "TXen" }, + { rxen_pin, rxen_gpiochip, rxen_line, "RXen" }, + { sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line, "SX126X_ANT_SW" }, + }; + for (auto& pinMap : pinMappings) { + if (yamlConfig["Lora"][pinMap.strName].IsMap() + && (yamlConfig["Lora"][pinMap.strName]["pin"] + || yamlConfig["Lora"][pinMap.strName]["line"] + || yamlConfig["Lora"][pinMap.strName]["gpiochip"])) { + settingsMap[pinMap.pin] = yamlConfig["Lora"][pinMap.strName]["pin"].as(RADIOLIB_NC); + settingsMap[pinMap.line] = yamlConfig["Lora"][pinMap.strName]["line"].as(settingsMap[pinMap.pin]); + settingsMap[pinMap.gpiochip] = yamlConfig["Lora"][pinMap.strName]["gpiochip"].as(defaultGpioChip); + } + else { // backwards API compatibility + settingsMap[pinMap.pin] = yamlConfig["Lora"][pinMap.strName].as(RADIOLIB_NC); + settingsMap[pinMap.line] = settingsMap[pinMap.pin]; + settingsMap[pinMap.gpiochip] = defaultGpioChip; + } + } + settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as(2000000); settingsStrings[lora_usb_serial_num] = yamlConfig["Lora"]["USB_Serialnum"].as(""); settingsMap[lora_usb_pid] = yamlConfig["Lora"]["USB_PID"].as(0x5512); diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 5bc07df6a..d1e91956d 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -5,27 +5,42 @@ #include "platform/portduino/USBHal.h" enum configNames { - use_sx1262, - cs, - irq, - busy, - reset, - sx126x_ant_sw, - txen, - rxen, + default_gpiochip, + cs_pin, + cs_line, + cs_gpiochip, + irq_pin, + irq_line, + irq_gpiochip, + busy_pin, + busy_line, + busy_gpiochip, + reset_pin, + reset_line, + reset_gpiochip, + txen_pin, + txen_line, + txen_gpiochip, + rxen_pin, + rxen_line, + rxen_gpiochip, + sx126x_ant_sw_pin, + sx126x_ant_sw_line, + sx126x_ant_sw_gpiochip, dio2_as_rf_switch, dio3_tcxo_voltage, use_rf95, + use_sx1262, + use_sx1268, use_sx1280, use_lr1110, use_lr1120, use_lr1121, - use_sx1268, + use_llcc68, lora_usb_serial_num, lora_usb_pid, lora_usb_vid, user, - gpiochip, spidev, spiSpeed, i2cdev, @@ -75,7 +90,7 @@ extern std::map settingsMap; extern std::map settingsStrings; extern std::ofstream traceFile; extern Ch341Hal *ch341Hal; -int initGPIOPin(int pinNum, std::string gpioChipname); +int initGPIOPin(int pinNum, std::string gpioChipname, int line); bool loadConfig(const char *configPath); static bool ends_with(std::string_view str, std::string_view suffix); void getMacAddr(uint8_t *dmac); From 6b1c01ce02e57788873a9f36197e46885b8f11fc Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 12 Jan 2025 15:10:50 -0600 Subject: [PATCH 02/19] Trunk n stuff (#5833) * Trunk * Allow new gpio syntax with defaults * Exit on pin init failure --- src/main.cpp | 67 +++++++------- src/mesh/SX126xInterface.cpp | 3 +- src/platform/portduino/PortduinoGlue.cpp | 106 ++++++++++------------- 3 files changed, 78 insertions(+), 98 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 338fca5c1..fc9d24e37 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -826,43 +826,37 @@ void setup() #endif #ifdef ARCH_PORTDUINO - const struct { configNames cfgName; - std::string strName; - } loraModules[] = { - { use_rf95, "RF95" }, - { use_sx1262, "sx1262" }, - { use_sx1268, "sx1268" }, - { use_sx1280, "sx1280" }, - { use_lr1110, "lr1110" }, - { use_lr1120, "lr1120" }, - { use_lr1121, "lr1121" }, - { use_llcc68, "LLCC68" } - }; + const struct { + configNames cfgName; + std::string strName; + } loraModules[] = {{use_rf95, "RF95"}, {use_sx1262, "sx1262"}, {use_sx1268, "sx1268"}, {use_sx1280, "sx1280"}, + {use_lr1110, "lr1110"}, {use_lr1120, "lr1120"}, {use_lr1121, "lr1121"}, {use_llcc68, "LLCC68"}}; // as one can't use a function pointer to the class constructor: - auto loraModuleInterface = [](configNames cfgName, LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) { - switch (cfgName) { - case use_rf95: - return (RadioInterface*)new RF95Interface(hal, cs, irq, rst, busy); - case use_sx1262: - return (RadioInterface*)new SX1262Interface(hal, cs, irq, rst, busy); - case use_sx1268: - return (RadioInterface*)new SX1268Interface(hal, cs, irq, rst, busy); - case use_sx1280: - return (RadioInterface*)new SX1280Interface(hal, cs, irq, rst, busy); - case use_lr1110: - return (RadioInterface*)new LR1110Interface(hal, cs, irq, rst, busy); - case use_lr1120: - return (RadioInterface*)new LR1120Interface(hal, cs, irq, rst, busy); - case use_lr1121: - return (RadioInterface*)new LR1121Interface(hal, cs, irq, rst, busy); - case use_llcc68: - return (RadioInterface*)new LLCC68Interface(hal, cs, irq, rst, busy); - default: - assert(0); // shouldn't happen - return (RadioInterface*)nullptr; - } + auto loraModuleInterface = [](configNames cfgName, LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, + RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) { + switch (cfgName) { + case use_rf95: + return (RadioInterface *)new RF95Interface(hal, cs, irq, rst, busy); + case use_sx1262: + return (RadioInterface *)new SX1262Interface(hal, cs, irq, rst, busy); + case use_sx1268: + return (RadioInterface *)new SX1268Interface(hal, cs, irq, rst, busy); + case use_sx1280: + return (RadioInterface *)new SX1280Interface(hal, cs, irq, rst, busy); + case use_lr1110: + return (RadioInterface *)new LR1110Interface(hal, cs, irq, rst, busy); + case use_lr1120: + return (RadioInterface *)new LR1120Interface(hal, cs, irq, rst, busy); + case use_lr1121: + return (RadioInterface *)new LR1121Interface(hal, cs, irq, rst, busy); + case use_llcc68: + return (RadioInterface *)new LLCC68Interface(hal, cs, irq, rst, busy); + default: + assert(0); // shouldn't happen + return (RadioInterface *)nullptr; + } }; - for (auto& loraModule : loraModules) { + for (auto &loraModule : loraModules) { if (settingsMap[loraModule.cfgName] && !rIf) { LOG_DEBUG("Activate %s radio on SPI port %s", loraModule.strName.c_str(), settingsStrings[spidev].c_str()); if (settingsStrings[spidev] == "ch341") { @@ -870,7 +864,8 @@ void setup() } else { RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); } - rIf = loraModuleInterface(loraModule.cfgName, (LockingArduinoHal *)RadioLibHAL, settingsMap[cs_pin], settingsMap[irq_pin], settingsMap[reset_pin], settingsMap[busy_pin]); + rIf = loraModuleInterface(loraModule.cfgName, (LockingArduinoHal *)RadioLibHAL, settingsMap[cs_pin], + settingsMap[irq_pin], settingsMap[reset_pin], settingsMap[busy_pin]); if (!rIf->init()) { LOG_WARN("No %s radio", loraModule.strName.c_str()); delete rIf; diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index b13bb6faf..8a7bc7670 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -121,7 +121,8 @@ template bool SX126xInterface::init() // no effect #if ARCH_PORTDUINO if (res == RADIOLIB_ERR_NONE) { - LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen_pin], settingsMap[txen_pin]); + LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen_pin], + settingsMap[txen_pin]); lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]); } #else diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index e75112235..ce2418e86 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -134,21 +134,10 @@ void portduinoSetup() { printf("Set up Meshtastic on Portduino...\n"); int max_GPIO = 0; - const configNames GPIO_lines[] = {cs_pin, - irq_pin, - busy_pin, - reset_pin, - sx126x_ant_sw_pin, - txen_pin, - rxen_pin, - displayDC, - displayCS, - displayBacklight, - displayBacklightPWMChannel, - displayReset, - touchscreenCS, - touchscreenIRQ, - user}; + const configNames GPIO_lines[] = { + cs_pin, irq_pin, busy_pin, reset_pin, sx126x_ant_sw_pin, txen_pin, + rxen_pin, displayDC, displayCS, displayBacklight, displayBacklightPWMChannel, displayReset, + touchscreenCS, touchscreenIRQ, user}; std::string gpioChipName = "gpiochip"; settingsStrings[i2cdev] = ""; @@ -257,7 +246,6 @@ void portduinoSetup() gpioInit(max_GPIO + 1); // Done here so we can inform Portduino how many GPIOs we need. // Need to bind all the configured GPIO pins so they're not simulated - // TODO: Can we do this in the for loop above? // TODO: If one of these fails, we should log and terminate if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) { if (initGPIOPin(settingsMap[user], defaultGpioChipName, settingsMap[user]) != ERRNO_OK) { @@ -283,22 +271,25 @@ void portduinoSetup() // Only initialize the radio pins when dealing with real, kernel controlled SPI hardware if (settingsStrings[spidev] != "" && settingsStrings[spidev] != "ch341") { - const struct { configNames pin; configNames gpiochip; configNames line; } pinMappings[] = { - { cs_pin, cs_gpiochip, cs_line }, - { irq_pin, irq_gpiochip, irq_line }, - { busy_pin, busy_gpiochip, busy_line }, - { reset_pin, reset_gpiochip, reset_line }, - { rxen_pin, rxen_gpiochip, rxen_line }, - { txen_pin, txen_gpiochip, txen_line }, - { sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line } - }; - for (auto& pinMap : pinMappings) { + const struct { + configNames pin; + configNames gpiochip; + configNames line; + } pinMappings[] = {{cs_pin, cs_gpiochip, cs_line}, + {irq_pin, irq_gpiochip, irq_line}, + {busy_pin, busy_gpiochip, busy_line}, + {reset_pin, reset_gpiochip, reset_line}, + {rxen_pin, rxen_gpiochip, rxen_line}, + {txen_pin, txen_gpiochip, txen_line}, + {sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line}}; + for (auto &pinMap : pinMappings) { auto setMapIter = settingsMap.find(pinMap.pin); if (setMapIter != settingsMap.end() && setMapIter->second != RADIOLIB_NC) { - if (initGPIOPin(setMapIter->second, gpioChipName + std::to_string(settingsMap[pinMap.gpiochip]), settingsMap[pinMap.line] ) != ERRNO_OK) { - settingsMap[pinMap.pin] = RADIOLIB_NC; - settingsMap[pinMap.gpiochip] = RADIOLIB_NC; - settingsMap[pinMap.line] = RADIOLIB_NC; + if (initGPIOPin(setMapIter->second, gpioChipName + std::to_string(settingsMap[pinMap.gpiochip]), + settingsMap[pinMap.line]) != ERRNO_OK) { + printf("Error setting pin number %d. It may not exist, or may already be in use.\n", + settingsMap[pinMap.line]); + exit(EXIT_FAILURE); } } } @@ -360,21 +351,16 @@ bool loadConfig(const char *configPath) } } if (yamlConfig["Lora"]) { - const struct { configNames cfgName; std::string strName; } loraModules[] = { - { use_rf95, "RF95" }, - { use_sx1262, "sx1262" }, - { use_sx1268, "sx1268" }, - { use_sx1280, "sx1280" }, - { use_lr1110, "lr1110" }, - { use_lr1120, "lr1120" }, - { use_lr1121, "lr1121" }, - { use_llcc68, "LLCC68" } - }; - for (auto& loraModule : loraModules) { + const struct { + configNames cfgName; + std::string strName; + } loraModules[] = {{use_rf95, "RF95"}, {use_sx1262, "sx1262"}, {use_sx1268, "sx1268"}, {use_sx1280, "sx1280"}, + {use_lr1110, "lr1110"}, {use_lr1120, "lr1120"}, {use_lr1121, "lr1121"}, {use_llcc68, "LLCC68"}}; + for (auto &loraModule : loraModules) { settingsMap[loraModule.cfgName] = false; } if (yamlConfig["Lora"]["Module"]) { - for (auto& loraModule : loraModules) { + for (auto &loraModule : loraModules) { if (yamlConfig["Lora"]["Module"].as("") == loraModule.strName) { settingsMap[loraModule.cfgName] = true; break; @@ -391,28 +377,26 @@ bool loadConfig(const char *configPath) // backwards API compatibility and to globally set gpiochip once int defaultGpioChip = settingsMap[default_gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0); - const struct { configNames pin; - configNames gpiochip; - configNames line; - std::string strName; } pinMappings[] = { - { cs_pin, cs_gpiochip, cs_line, "CS" }, - { irq_pin, irq_gpiochip, irq_line, "IRQ" }, - { busy_pin, busy_gpiochip, busy_line, "Busy" }, - { reset_pin, reset_gpiochip, reset_line, "Reset" }, - { txen_pin, txen_gpiochip, txen_line, "TXen" }, - { rxen_pin, rxen_gpiochip, rxen_line, "RXen" }, - { sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line, "SX126X_ANT_SW" }, + const struct { + configNames pin; + configNames gpiochip; + configNames line; + std::string strName; + } pinMappings[] = { + {cs_pin, cs_gpiochip, cs_line, "CS"}, + {irq_pin, irq_gpiochip, irq_line, "IRQ"}, + {busy_pin, busy_gpiochip, busy_line, "Busy"}, + {reset_pin, reset_gpiochip, reset_line, "Reset"}, + {txen_pin, txen_gpiochip, txen_line, "TXen"}, + {rxen_pin, rxen_gpiochip, rxen_line, "RXen"}, + {sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line, "SX126X_ANT_SW"}, }; - for (auto& pinMap : pinMappings) { - if (yamlConfig["Lora"][pinMap.strName].IsMap() - && (yamlConfig["Lora"][pinMap.strName]["pin"] - || yamlConfig["Lora"][pinMap.strName]["line"] - || yamlConfig["Lora"][pinMap.strName]["gpiochip"])) { + for (auto &pinMap : pinMappings) { + if (yamlConfig["Lora"][pinMap.strName].IsMap()) { settingsMap[pinMap.pin] = yamlConfig["Lora"][pinMap.strName]["pin"].as(RADIOLIB_NC); settingsMap[pinMap.line] = yamlConfig["Lora"][pinMap.strName]["line"].as(settingsMap[pinMap.pin]); settingsMap[pinMap.gpiochip] = yamlConfig["Lora"][pinMap.strName]["gpiochip"].as(defaultGpioChip); - } - else { // backwards API compatibility + } else { // backwards API compatibility settingsMap[pinMap.pin] = yamlConfig["Lora"][pinMap.strName].as(RADIOLIB_NC); settingsMap[pinMap.line] = settingsMap[pinMap.pin]; settingsMap[pinMap.gpiochip] = defaultGpioChip; @@ -584,4 +568,4 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac) } else { return false; } -} +} \ No newline at end of file From e0f97c930648e8adf71af3a279c8ee79da1f2dca Mon Sep 17 00:00:00 2001 From: Austin Date: Sun, 12 Jan 2025 23:24:05 -0500 Subject: [PATCH 03/19] rpkg Fedora packaging (#5735) --- .github/workflows/hook_copr.yml | 73 +++++++++++++++ ...ghtly_debian.yml => nightly_packaging.yml} | 12 ++- .github/workflows/release_channels.yml | 7 ++ bin/rpkg.macros | 12 +++ meshtasticd.spec.rpkg | 91 +++++++++++++++++++ rpkg.conf | 2 + 6 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/hook_copr.yml rename .github/workflows/{nightly_debian.yml => nightly_packaging.yml} (75%) create mode 100644 bin/rpkg.macros create mode 100644 meshtasticd.spec.rpkg create mode 100644 rpkg.conf diff --git a/.github/workflows/hook_copr.yml b/.github/workflows/hook_copr.yml new file mode 100644 index 000000000..c7b6b8d79 --- /dev/null +++ b/.github/workflows/hook_copr.yml @@ -0,0 +1,73 @@ +name: Trigger COPR build + +on: + workflow_call: + secrets: + COPR_HOOK_DAILY: + COPR_HOOK_ALPHA: + COPR_HOOK_BETA: + inputs: + copr_project: + description: COPR project to target + required: true + type: string + +permissions: read-all + +jobs: + build-copr-hook: + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + ref: ${{ github.ref }} + repository: ${{ github.repository }} + + - name: Install Python dependencies + run: | + pip install requests + + - name: Trigger COPR build + shell: python + run: | + import requests + + project_name = "${{ inputs.copr_project }}" + if project_name == "daily": + hook_secret = "${{ secrets.COPR_HOOK_DAILY }}" + project_id = 160277 + elif project_name == "alpha": + hook_secret = "${{ secrets.COPR_HOOK_ALPHA }}" + project_id = 160278 + elif project_name == "beta": + hook_secret = "${{ secrets.COPR_HOOK_BETA }}" + project_id = 160279 + else: + raise ValueError(f"Unknown COPR project: {project_name}") + + webhook_url = f"https://copr.fedorainfracloud.org/webhooks/github/{project_id}/{hook_secret}/meshtasticd/" + copr_payload = { + "event": "push", + "payload": { + "ref": "${{ github.ref }}", + "after": "${{ github.sha }}", + "repository": { + "id": "${{ github.repository_id }}", + "full_name": "${{ github.repository }}", + "git_url": "${{ github.repositoryUrl }}", + "owner": { + "name": "${{ github.repository_owner }}" + } + }, + "pusher": { + "name": "${{ github.actor }}" + }, + "sender": { + "login": "github-actions[bot]" + } + } + } + r = requests.post(webhook_url, json=copr_payload) + r.raise_for_status() diff --git a/.github/workflows/nightly_debian.yml b/.github/workflows/nightly_packaging.yml similarity index 75% rename from .github/workflows/nightly_debian.yml rename to .github/workflows/nightly_packaging.yml index aa001854e..e477fe194 100644 --- a/.github/workflows/nightly_debian.yml +++ b/.github/workflows/nightly_packaging.yml @@ -1,4 +1,4 @@ -name: Nightly Debian Packaging +name: Nightly Packaging on: schedule: - cron: 0 9 * * * @@ -8,10 +8,12 @@ on: - master paths: - debian/** - - .github/workflows/nightly_debian.yml + - "*.rpkg" + - .github/workflows/nightly_packaging.yml - .github/workflows/build_debian_src.yml - .github/workflows/package_ppa.yml - .github/workflows/package_obs.yml + - .github/workflows/hook_copr.yml permissions: contents: write @@ -35,3 +37,9 @@ jobs: obs_project: home:meshtastic:daily series: unstable secrets: inherit + + hook-copr: + uses: ./.github/workflows/hook_copr.yml + with: + copr_project: daily + secrets: inherit diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index 34673f0c7..8891826d2 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -27,3 +27,10 @@ jobs: series: |- ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} secrets: inherit + + # hook-copr: + # uses: ./.github/workflows/hook_copr.yml + # with: + # copr_project: |- + # ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} + # secrets: inherit diff --git a/bin/rpkg.macros b/bin/rpkg.macros new file mode 100644 index 000000000..2bbb203de --- /dev/null +++ b/bin/rpkg.macros @@ -0,0 +1,12 @@ +function meshtastic_version { + meshtastic_version=$(python3 bin/buildinfo.py short) + echo -n "$meshtastic_version" +} +function git_commits_num { + total_commits=$(git rev-list --all --count) + echo -n "$total_commits" +} +function git_commit_sha { + commit_sha=$(git rev-parse --short HEAD) + echo -n "$commit_sha" +} \ No newline at end of file diff --git a/meshtasticd.spec.rpkg b/meshtasticd.spec.rpkg new file mode 100644 index 000000000..1819897b0 --- /dev/null +++ b/meshtasticd.spec.rpkg @@ -0,0 +1,91 @@ +# meshtasticd spec file for RPM-based distributions +# +# Build locally with: +# ``` +# sudo dnf install rpkg-util +# rpkg local +# ``` +# +# See: +# - https://docs.pagure.org/rpkg-util/v3/index.html +# - https://docs.fedoraproject.org/en-US/packaging-guidelines/Versioning/ + +Name: meshtasticd +# Version Ex: 2.5.19 +Version: {{{ meshtastic_version }}} +# Release Ex: 9127.daily.gitd7f5f620.fc41 +Release: {{{ git_commits_num }}}%{?copr_projectname:.%{copr_projectname}}.git{{{ git_commit_sha }}}%{?dist} +VCS: {{{ git_dir_vcs }}} +Summary: Meshtastic daemon for communicating with Meshtastic devices + +License: GPL-3.0 +URL: https://github.com/meshtastic/firmware +Source0: {{{ git_dir_pack }}} +Source1: https://github.com/meshtastic/web/releases/download/latest/build.tar + +BuildRequires: systemd-rpm-macros +BuildRequires: python3-devel +BuildRequires: platformio +BuildRequires: python3dist(protobuf) +BuildRequires: python3dist(grpcio[protobuf]) +BuildRequires: python3dist(grpcio-tools) +BuildRequires: git-core +BuildRequires: gcc-c++ +BuildRequires: pkgconfig(yaml-cpp) +BuildRequires: pkgconfig(libgpiod) +BuildRequires: pkgconfig(bluez) +BuildRequires: pkgconfig(libusb-1.0) +BuildRequires: libi2c-devel +# Web components: +BuildRequires: pkgconfig(openssl) +BuildRequires: pkgconfig(liborcania) +BuildRequires: pkgconfig(libyder) +BuildRequires: pkgconfig(libulfius) + +%description +Meshtastic daemon for controlling Meshtastic devices. Meshtastic is an off-grid +text communication platform that uses inexpensive LoRa radios. + +%prep +{{{ git_dir_setup_macro }}} +# Unpack the web files +mkdir -p web +tar -xf %{SOURCE1} -C web +gzip -dr web + +%build +# Use the “native” environment from platformio to build a Linux binary +platformio run -e native + +%install +mkdir -p %{buildroot}%{_sbindir} +install -m 0755 .pio/build/native/program %{buildroot}%{_sbindir}/meshtasticd + +mkdir -p %{buildroot}%{_sysconfdir}/meshtasticd +install -m 0644 bin/config-dist.yaml %{buildroot}%{_sysconfdir}/meshtasticd/config.yaml +mkdir -p %{buildroot}%{_sysconfdir}/meshtasticd/config.d +mkdir -p %{buildroot}%{_sysconfdir}/meshtasticd/available.d +cp -r bin/config.d/* %{buildroot}%{_sysconfdir}/meshtasticd/available.d + +install -D -m 0644 bin/meshtasticd.service %{buildroot}%{_unitdir}/meshtasticd.service + +# Install the web files under /usr/share/meshtasticd/web +mkdir -p %{buildroot}%{_datadir}/meshtasticd/web +cp -r web/* %{buildroot}%{_datadir}/meshtasticd/web + +%files +%license LICENSE +%doc README.md +%{_sbindir}/meshtasticd +%dir %{_sysconfdir}/meshtasticd +%dir %{_sysconfdir}/meshtasticd/config.d +%dir %{_sysconfdir}/meshtasticd/available.d +%config(noreplace) %{_sysconfdir}/meshtasticd/config.yaml +%config %{_sysconfdir}/meshtasticd/available.d/* +%{_unitdir}/meshtasticd.service +%dir %{_datadir}/meshtasticd +%dir %{_datadir}/meshtasticd/web +%{_datadir}/meshtasticd/web/* + +%changelog +%autochangelog \ No newline at end of file diff --git a/rpkg.conf b/rpkg.conf new file mode 100644 index 000000000..f574f9a13 --- /dev/null +++ b/rpkg.conf @@ -0,0 +1,2 @@ +[rpkg] +user_macros = "${git_props:root}/bin/rpkg.macros" From 4dc8d6e4006c485fa7d5fb7b658c8e5d5f23b49d Mon Sep 17 00:00:00 2001 From: Austin Date: Mon, 13 Jan 2025 00:20:22 -0500 Subject: [PATCH 04/19] Fix daily packaging perms (#5836) --- .../workflows/{nightly_packaging.yml => daily_packaging.yml} | 2 +- .github/workflows/hook_copr.yml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) rename .github/workflows/{nightly_packaging.yml => daily_packaging.yml} (97%) diff --git a/.github/workflows/nightly_packaging.yml b/.github/workflows/daily_packaging.yml similarity index 97% rename from .github/workflows/nightly_packaging.yml rename to .github/workflows/daily_packaging.yml index e477fe194..14daae74d 100644 --- a/.github/workflows/nightly_packaging.yml +++ b/.github/workflows/daily_packaging.yml @@ -1,4 +1,4 @@ -name: Nightly Packaging +name: Daily Packaging on: schedule: - cron: 0 9 * * * diff --git a/.github/workflows/hook_copr.yml b/.github/workflows/hook_copr.yml index c7b6b8d79..94d9d095f 100644 --- a/.github/workflows/hook_copr.yml +++ b/.github/workflows/hook_copr.yml @@ -12,7 +12,9 @@ on: required: true type: string -permissions: read-all +permissions: + contents: write + packages: write jobs: build-copr-hook: From 89a9e0b99d579df9b97f87a35ba33653f496636e Mon Sep 17 00:00:00 2001 From: isseysandei Date: Mon, 13 Jan 2025 09:39:01 +0100 Subject: [PATCH 05/19] Added illuminance sensors to the node's environmental sensor page (#5832) * illuminance sensors added * added white_lux to debug --- src/modules/Telemetry/EnvironmentTelemetry.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index e72b03bd4..be5df6f63 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -253,6 +253,16 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt display->drawString(x, y += _fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq)); } + if (lastMeasurement.variant.environment_metrics.lux != 0) { + display->drawString(x, y += _fontHeight(FONT_SMALL), + "Illuminance: " + String(lastMeasurement.variant.environment_metrics.lux, 2) + "lx"); + } + + if (lastMeasurement.variant.environment_metrics.white_lux != 0) { + display->drawString(x, y += _fontHeight(FONT_SMALL), + "W_Lux: " + String(lastMeasurement.variant.environment_metrics.white_lux, 2) + "lx"); + } + if (lastMeasurement.variant.environment_metrics.distance != 0) display->drawString(x, y += _fontHeight(FONT_SMALL), "Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm"); @@ -277,8 +287,10 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac sender, t->variant.environment_metrics.barometric_pressure, t->variant.environment_metrics.current, t->variant.environment_metrics.gas_resistance, t->variant.environment_metrics.relative_humidity, t->variant.environment_metrics.temperature); - LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f", sender, t->variant.environment_metrics.voltage, - t->variant.environment_metrics.iaq, t->variant.environment_metrics.distance, t->variant.environment_metrics.lux); + LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f, white_lux=%f", sender, + t->variant.environment_metrics.voltage, t->variant.environment_metrics.iaq, + t->variant.environment_metrics.distance, t->variant.environment_metrics.lux, + t->variant.environment_metrics.white_lux); LOG_INFO("(Received from %s): wind speed=%fm/s, direction=%d degrees, weight=%fkg", sender, t->variant.environment_metrics.wind_speed, t->variant.environment_metrics.wind_direction, From 1c0f43c8e2fb4cbbc285eeac319d339ab63c866f Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 13 Jan 2025 06:28:18 -0600 Subject: [PATCH 06/19] NRF52 SafeFile should not remove / rename files (#5840) --- src/SafeFile.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index c5d7b335e..825ba3028 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -6,6 +6,11 @@ static File openFile(const char *filename, bool fullAtomic) { concurrency::LockGuard g(spiLock); + LOG_DEBUG("Opening %s, fullAtomic=%d", filename, fullAtomic); +#ifdef ARCH_NRF52 + lfs_assert_failed = false; + return FSCom.open(filename, FILE_O_WRITE); +#endif if (!fullAtomic) FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists) @@ -14,7 +19,6 @@ static File openFile(const char *filename, bool fullAtomic) // clear any previous LFS errors lfs_assert_failed = false; - return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE); } @@ -57,6 +61,10 @@ bool SafeFile::close() spiLock->lock(); f.close(); spiLock->unlock(); + +#ifdef ARCH_NRF52 + return true; +#endif if (!testReadback()) return false; From 6366633cd402a902b496959df9784cfbbb68c7a2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 07:05:59 -0600 Subject: [PATCH 07/19] [create-pull-request] automated change (#5841) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobufs b/protobufs index c55f120a9..76f806e1b 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit c55f120a9c1ce90c85e4826907a0b9bcb2d5f5a2 +Subproject commit 76f806e1bb1e2a7b157a14fadd095775f63db5e4 From e2dd845051cf40003a8c4102a8d7dad90dd4d3d2 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 13 Jan 2025 08:30:20 -0600 Subject: [PATCH 08/19] Fix devicestate protobuf / filesize allocation (#5835) --- src/mesh/NodeDB.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 8e084c99d..a9e5565ef 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -958,7 +958,7 @@ void NodeDB::loadFromDisk() #endif // static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM - auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES_FS * sizeof(meshtastic_NodeInfo), + auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES_FS * meshtastic_NodeInfoLite_size, sizeof(meshtastic_DeviceState), &meshtastic_DeviceState_msg, &devicestate); // See https://github.com/meshtastic/firmware/issues/4184#issuecomment-2269390786 @@ -1147,8 +1147,9 @@ bool NodeDB::saveDeviceStateToDisk() #endif // Note: if MAX_NUM_NODES=100 and meshtastic_NodeInfoLite_size=166, so will be approximately 17KB // Because so huge we _must_ not use fullAtomic, because the filesystem is probably too small to hold two copies of this - return saveProto(prefFileName, sizeof(devicestate) + numMeshNodes * meshtastic_NodeInfoLite_size, &meshtastic_DeviceState_msg, - &devicestate, false); + size_t deviceStateSize; + pb_get_encoded_size(&deviceStateSize, meshtastic_DeviceState_fields, &devicestate); + return saveProto(prefFileName, deviceStateSize, &meshtastic_DeviceState_msg, &devicestate, false); } bool NodeDB::saveToDiskNoRetry(int saveWhat) From de42d96adf41a36ce4aa509715fb101dc1736376 Mon Sep 17 00:00:00 2001 From: Austin Date: Mon, 13 Jan 2025 11:46:07 -0500 Subject: [PATCH 09/19] Actions: Fix issues with new Release process (#5845) --- .github/workflows/main_matrix.yml | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index b55696301..66a398566 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -320,19 +320,23 @@ jobs: run: ls -lR - name: Add deb files to release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ steps.version.outputs.long }} - files: | - ./output/meshtasticd_${{ steps.version.outputs.long }}_arm64.deb - ./output/meshtasticd_${{ steps.version.outputs.long }}_armhf.deb - ./output/meshtasticd_${{ steps.version.outputs.long }}_amd64.deb - ./output/meshtasticd-${{ steps.version.outputs.deb }}-src.zip + run: | + gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_arm64.deb + gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_armhf.deb + gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_amd64.deb + gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd-${{ steps.version.outputs.deb }}-src.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Bump version.properties run: >- bin/bump_version.py + - name: Install debian tools for changelog + run: | + sudo apt-get update -y + sudo apt-get install -y devscripts + - name: Update debian changelog run: >- debian/ci_changelog.sh @@ -397,9 +401,8 @@ jobs: run: ls -lR - name: Add bins and debug elfs to release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ steps.version.outputs.long }} - files: | - ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip - ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip + run: | + gh release upload v${{ steps.version.outputs.long }} ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip + gh release upload v${{ steps.version.outputs.long }} ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d5cd6f87a02e63dd6ab6f5c851107912a2cf5a9f Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 13 Jan 2025 12:15:27 -0600 Subject: [PATCH 10/19] Kablammo --- .github/workflows/main_matrix.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 66a398566..0a0ea9954 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -332,11 +332,6 @@ jobs: run: >- bin/bump_version.py - - name: Install debian tools for changelog - run: | - sudo apt-get update -y - sudo apt-get install -y devscripts - - name: Update debian changelog run: >- debian/ci_changelog.sh From 038430db2336bdea1ec9093dd539943d6a56a870 Mon Sep 17 00:00:00 2001 From: isseysandei Date: Tue, 14 Jan 2025 02:00:00 +0100 Subject: [PATCH 11/19] Environmental sensors page scrolling (#5847) * env scrolling first iteration * ran trunk --- .../Telemetry/EnvironmentTelemetry.cpp | 77 +++++++++++++------ 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index be5df6f63..2fc9bab0b 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -222,7 +222,11 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt } // Display "Env. From: ..." on its own - display->drawString(x, y, "Env. From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); + display->drawString(x, y, "Env. From: " + String(lastSender) + " (" + String(agoSecs) + "s)"); + + // Prepare sensor data strings + String sensorData[10]; + int sensorCount = 0; if (lastMeasurement.variant.environment_metrics.has_temperature || lastMeasurement.variant.environment_metrics.has_relative_humidity) { @@ -232,48 +236,73 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt String(UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F"; } - // Continue with the remaining details - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Temp/Hum: " + last_temp + " / " + - String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%"); + sensorData[sensorCount++] = + "Temp/Hum: " + last_temp + " / " + String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%"; } if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0) { - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA"); + sensorData[sensorCount++] = + "Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA"; } if (lastMeasurement.variant.environment_metrics.voltage != 0) { - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " + - String(lastMeasurement.variant.environment_metrics.current, 0) + "mA"); + sensorData[sensorCount++] = "Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " + + String(lastMeasurement.variant.environment_metrics.current, 0) + "mA"; } if (lastMeasurement.variant.environment_metrics.iaq != 0) { - display->drawString(x, y += _fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq)); + sensorData[sensorCount++] = "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq); + } + + if (lastMeasurement.variant.environment_metrics.distance != 0) { + sensorData[sensorCount++] = "Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm"; + } + + if (lastMeasurement.variant.environment_metrics.weight != 0) { + sensorData[sensorCount++] = "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg"; + } + + if (lastMeasurement.variant.environment_metrics.radiation != 0) { + sensorData[sensorCount++] = "Rad: " + String(lastMeasurement.variant.environment_metrics.radiation, 2) + "µR/h"; } if (lastMeasurement.variant.environment_metrics.lux != 0) { - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Illuminance: " + String(lastMeasurement.variant.environment_metrics.lux, 2) + "lx"); + sensorData[sensorCount++] = "Illuminance: " + String(lastMeasurement.variant.environment_metrics.lux, 2) + "lx"; } if (lastMeasurement.variant.environment_metrics.white_lux != 0) { - display->drawString(x, y += _fontHeight(FONT_SMALL), - "W_Lux: " + String(lastMeasurement.variant.environment_metrics.white_lux, 2) + "lx"); + sensorData[sensorCount++] = "W_Lux: " + String(lastMeasurement.variant.environment_metrics.white_lux, 2) + "lx"; } - if (lastMeasurement.variant.environment_metrics.distance != 0) - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm"); + static int scrollOffset = 0; + static bool scrollingDown = true; + static uint32_t lastScrollTime = millis(); - if (lastMeasurement.variant.environment_metrics.weight != 0) - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg"); + // Draw up to 3 sensor data lines + int linesToShow = min(3, sensorCount); + for (int i = 0; i < linesToShow; i++) { + int index = (scrollOffset + i) % sensorCount; + display->drawString(x, y += _fontHeight(FONT_SMALL), sensorData[index]); + } - if (lastMeasurement.variant.environment_metrics.radiation != 0) - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Rad: " + String(lastMeasurement.variant.environment_metrics.radiation, 2) + "µR/h"); + // Only scroll if there are more than 3 sensor data lines + if (sensorCount > 3) { + // Update scroll offset every 5 seconds + if (millis() - lastScrollTime > 5000) { + if (scrollingDown) { + scrollOffset++; + if (scrollOffset + linesToShow >= sensorCount) { + scrollingDown = false; + } + } else { + scrollOffset--; + if (scrollOffset <= 0) { + scrollingDown = true; + } + } + lastScrollTime = millis(); + } + } } bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) From 729c39fb8621e0b76f4178fee88cc1dfa2711d06 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:18:34 -0600 Subject: [PATCH 12/19] [create-pull-request] automated change (#5849) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- debian/changelog | 5 +++-- version.properties | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index 035d5f034..8b1799c52 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,6 @@ -meshtasticd (2.5.19.0) UNRELEASED; urgency=medium +meshtasticd (2.5.20.0) UNRELEASED; urgency=medium * Initial packaging + * GitHub Actions Automatic version bump - -- Austin Lane Thu, 02 Jan 2025 12:00:00 +0000 \ No newline at end of file + -- Austin Lane Mon, 13 Jan 2025 19:24:14 +0000 diff --git a/version.properties b/version.properties index 800529e39..4312ae59a 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 19 +build = 20 From dd9ab7f0e1f74c2cdb33d1fe3d044cebab56cad0 Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 14 Jan 2025 02:17:54 -0500 Subject: [PATCH 13/19] Small Fix: Release_Channels permissions (#5852) --- .github/workflows/hook_copr.yml | 38 ++++++++------------------ .github/workflows/release_channels.yml | 4 ++- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/.github/workflows/hook_copr.yml b/.github/workflows/hook_copr.yml index 94d9d095f..c30038d6b 100644 --- a/.github/workflows/hook_copr.yml +++ b/.github/workflows/hook_copr.yml @@ -38,38 +38,24 @@ jobs: project_name = "${{ inputs.copr_project }}" if project_name == "daily": - hook_secret = "${{ secrets.COPR_HOOK_DAILY }}" - project_id = 160277 + hook_secret = "${{ secrets.COPR_HOOK_DAILY }}" + project_id = 160277 elif project_name == "alpha": - hook_secret = "${{ secrets.COPR_HOOK_ALPHA }}" - project_id = 160278 + hook_secret = "${{ secrets.COPR_HOOK_ALPHA }}" + project_id = 160278 elif project_name == "beta": - hook_secret = "${{ secrets.COPR_HOOK_BETA }}" - project_id = 160279 + hook_secret = "${{ secrets.COPR_HOOK_BETA }}" + project_id = 160279 else: - raise ValueError(f"Unknown COPR project: {project_name}") + raise ValueError(f"Unknown COPR project: {project_name}") webhook_url = f"https://copr.fedorainfracloud.org/webhooks/github/{project_id}/{hook_secret}/meshtasticd/" copr_payload = { - "event": "push", - "payload": { - "ref": "${{ github.ref }}", - "after": "${{ github.sha }}", - "repository": { - "id": "${{ github.repository_id }}", - "full_name": "${{ github.repository }}", - "git_url": "${{ github.repositoryUrl }}", - "owner": { - "name": "${{ github.repository_owner }}" - } - }, - "pusher": { - "name": "${{ github.actor }}" - }, - "sender": { - "login": "github-actions[bot]" - } + "ref": "${{ github.ref }}", + "after": "${{ github.sha }}", + "repository": { + "clone_url": "${{ github.server_url }}/${{ github.repository }}.git", } } - r = requests.post(webhook_url, json=copr_payload) + r = requests.post(webhook_url, json=copr_payload, headers={"X-GitHub-Event": "push"}) r.raise_for_status() diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index 8891826d2..afb7319ed 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -4,7 +4,9 @@ on: release: types: [published, released] -permissions: read-all +permissions: + contents: write + packages: write jobs: package-ppa: From fb2c008c89cbd9930eda10193baf66c0c45fc6b4 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 14 Jan 2025 18:55:02 -0600 Subject: [PATCH 14/19] Update version.properties --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index 4312ae59a..800529e39 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 20 +build = 19 From 85de193845bae37e1a19a87372e8c3d187f0ecb2 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 15 Jan 2025 06:46:12 -0600 Subject: [PATCH 15/19] Fix NRF52 default append write mode of files (#5858) * Fix NRF52 default append write mode of files * Inside the lock --- src/SafeFile.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index 825ba3028..f874164ae 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -9,7 +9,9 @@ static File openFile(const char *filename, bool fullAtomic) LOG_DEBUG("Opening %s, fullAtomic=%d", filename, fullAtomic); #ifdef ARCH_NRF52 lfs_assert_failed = false; - return FSCom.open(filename, FILE_O_WRITE); + File file = FSCom.open(filename, FILE_O_WRITE); + file.seek(0); + return file; #endif if (!fullAtomic) FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists) @@ -59,6 +61,9 @@ bool SafeFile::close() return false; spiLock->lock(); +#ifdef ARCH_NRF52 + f.truncate(); +#endif f.close(); spiLock->unlock(); From f9876cfe9c52691b56305270e1246fdc6d72f37f Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Thu, 16 Jan 2025 02:19:51 +1300 Subject: [PATCH 16/19] Wait for disconnection (#5859) --- src/platform/nrf52/NRF52Bluetooth.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 541e90ab2..b98620f33 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -210,7 +210,10 @@ void NRF52Bluetooth::shutdown() LOG_INFO("NRF52 bluetooth disconnecting handle %d", i); Bluefruit.disconnect(i); } - delay(100); // wait for ondisconnect; + // Wait for disconnection + while (Bluefruit.connected()) + yield(); + LOG_INFO("All bluetooth connections ended"); } Bluefruit.Advertising.stop(); } From 4cd2ba54795f47b732608eab8e834c50ea8cc9c1 Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Thu, 16 Jan 2025 23:23:57 +1300 Subject: [PATCH 17/19] More lines of environmental telemetry on-screen (#5853) --- src/modules/Telemetry/EnvironmentTelemetry.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 2fc9bab0b..fe1d0d2a9 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -278,8 +278,18 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt static bool scrollingDown = true; static uint32_t lastScrollTime = millis(); - // Draw up to 3 sensor data lines - int linesToShow = min(3, sensorCount); + // Determine how many lines we can fit on display + // Calculated once only: display dimensions don't change during runtime. + static int maxLines = 0; + if (!maxLines) { + const int16_t paddingTop = _fontHeight(FONT_SMALL); // Heading text + const int16_t paddingBottom = 8; // Indicator dots + maxLines = (display->getHeight() - paddingTop - paddingBottom) / _fontHeight(FONT_SMALL); + assert(maxLines > 0); + } + + // Draw as many lines of data as we can fit + int linesToShow = min(maxLines, sensorCount); for (int i = 0; i < linesToShow; i++) { int index = (scrollOffset + i) % sensorCount; display->drawString(x, y += _fontHeight(FONT_SMALL), sensorData[index]); From 262f1d25a20ad68036fec402b2e5d7ca0b19adcf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2025 06:15:17 -0600 Subject: [PATCH 18/19] [create-pull-request] automated change (#5860) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- debian/changelog | 3 ++- version.properties | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 8b1799c52..a1a359cfb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,5 +2,6 @@ meshtasticd (2.5.20.0) UNRELEASED; urgency=medium * Initial packaging * GitHub Actions Automatic version bump + * GitHub Actions Automatic version bump - -- Austin Lane Mon, 13 Jan 2025 19:24:14 +0000 + -- Austin Lane Wed, 15 Jan 2025 14:08:54 +0000 diff --git a/version.properties b/version.properties index 800529e39..4312ae59a 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 19 +build = 20 From a48df917374019f408608e814451e9029a75d002 Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Fri, 17 Jan 2025 01:38:22 +1300 Subject: [PATCH 19/19] Canned messages: allow GPIO0 with "scan and select" input (#5838) * Allow GPIO0; check for conflict with user button * Guard for no BUTTON_PIN; handle portduino * Portduino settings: attempt two We don't really need to #include radio code here just to check if the pin is RADIOLIB_NC. We're only interested if scanAndSelect pin matches user button pin, but they won't match if user button is RADIOLIB_NC. * Portduino attempt 3: glue --- src/input/ScanAndSelect.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/input/ScanAndSelect.cpp b/src/input/ScanAndSelect.cpp index d8767fab8..1262f99b4 100644 --- a/src/input/ScanAndSelect.cpp +++ b/src/input/ScanAndSelect.cpp @@ -7,6 +7,9 @@ #include "ScanAndSelect.h" #include "modules/CannedMessageModule.h" #include +#ifdef ARCH_PORTDUINO // Only to check for pin conflict with user button +#include "platform/portduino/PortduinoGlue.h" +#endif // Config static const char name[] = "scanAndSelect"; // should match "allow input source" string @@ -30,7 +33,9 @@ bool ScanAndSelectInput::init() if (strcasecmp(moduleConfig.canned_message.allow_input_source, name) != 0) return false; - // Use any available inputbroker pin as the button + // Determine which pin to use for the single scan-and-select button + // User can specify this by setting any of the inputbroker pins + // If all values are zero, we'll assume the user *does* want GPIO0 if (moduleConfig.canned_message.inputbroker_pin_press) pin = moduleConfig.canned_message.inputbroker_pin_press; else if (moduleConfig.canned_message.inputbroker_pin_a) @@ -38,7 +43,25 @@ bool ScanAndSelectInput::init() else if (moduleConfig.canned_message.inputbroker_pin_b) pin = moduleConfig.canned_message.inputbroker_pin_b; else - return false; // Short circuit: no button found + pin = 0; // GPIO 0 then + + // Short circuit: if selected pin conficts with the user button +#if defined(ARCH_PORTDUINO) + int pinUserButton = 0; + if (settingsMap.count(user) != 0) { + pinUserButton = settingsMap[user]; + } +#elif defined(USERPREFS_BUTTON_PIN) + int pinUserButton = config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN; +#elif defined(BUTTON_PIN) + int pinUserButton = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; +#else + int pinUserButton = config.device.button_gpio; +#endif + if (pin == pinUserButton) { + LOG_ERROR("ScanAndSelect conflict with user button"); + return false; + } // Set-up the button pinMode(pin, INPUT_PULLUP);