From 6ef8e24b97a40472e07cafa7458e788216c1978d Mon Sep 17 00:00:00 2001 From: Grzegorz Godlewski Date: Sat, 14 Mar 2026 14:09:40 +0100 Subject: [PATCH 01/14] variants: add linux portduino native firmware support --- boards/linux.json | 21 ++++ src/helpers/radiolib/LinuxSX1262.h | 48 ++++++++ src/helpers/radiolib/LinuxSX1262Wrapper.h | 22 ++++ variants/linux/LinuxBoard.cpp | 137 ++++++++++++++++++++++ variants/linux/LinuxBoard.h | 92 +++++++++++++++ variants/linux/meshcored.ini | 28 +++++ variants/linux/meshcored.service | 29 +++++ variants/linux/platformio.ini | 47 ++++++++ variants/linux/target.cpp | 54 +++++++++ variants/linux/target.h | 32 +++++ variants/portduino/platformio.ini | 41 +++++++ 11 files changed, 551 insertions(+) create mode 100644 boards/linux.json create mode 100644 src/helpers/radiolib/LinuxSX1262.h create mode 100644 src/helpers/radiolib/LinuxSX1262Wrapper.h create mode 100644 variants/linux/LinuxBoard.cpp create mode 100644 variants/linux/LinuxBoard.h create mode 100644 variants/linux/meshcored.ini create mode 100644 variants/linux/meshcored.service create mode 100644 variants/linux/platformio.ini create mode 100644 variants/linux/target.cpp create mode 100644 variants/linux/target.h create mode 100644 variants/portduino/platformio.ini diff --git a/boards/linux.json b/boards/linux.json new file mode 100644 index 0000000000..21b5cdc33a --- /dev/null +++ b/boards/linux.json @@ -0,0 +1,21 @@ +{ + "build": { + "arduino": { + }, + "core": "linux", + "extra_flags": [ + ], + "hwids": [], + "mcu": "arm64", + "variant": "linux" + }, + "connectivity": ["wifi", "bluetooth"], + "debug": {}, + "frameworks": ["portduino", "linux"], + "name": "Linux", + "upload": { + "maximum_ram_size": 0, + "maximum_size": 0 + }, + "vendor": "Linux" +} diff --git a/src/helpers/radiolib/LinuxSX1262.h b/src/helpers/radiolib/LinuxSX1262.h new file mode 100644 index 0000000000..8d5977e8b0 --- /dev/null +++ b/src/helpers/radiolib/LinuxSX1262.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received +#define SX126X_IRQ_PREAMBLE_DETECTED 0x04 +#define SX126X_PREAMBLE_LENGTH 16 + +extern LinuxBoard board; + +class LinuxSX1262 : public SX1262 { + public: + LinuxSX1262(Module *mod) : SX1262(mod) { } + + bool std_init(SPIClass* spi = NULL) + { + LinuxConfig config = board.config; + + Serial.printf("Radio begin %f %f %d %d %f\n", config.lora_freq, config.lora_bw, config.lora_sf, config.lora_cr, config.lora_tcxo); + int status = begin(config.lora_freq, config.lora_bw, config.lora_sf, config.lora_cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, config.lora_tx_power, SX126X_PREAMBLE_LENGTH, config.lora_tcxo); + // if radio init fails with -707/-706, try again with tcxo voltage set to 0.0f + if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) { + status = begin(config.lora_freq, config.lora_bw, config.lora_sf, config.lora_cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, config.lora_tx_power, SX126X_PREAMBLE_LENGTH, 0.0f); + } + if (status != RADIOLIB_ERR_NONE) { + Serial.print("ERROR: radio init failed: "); + Serial.println(status); + return false; // fail + } + + setCRC(1); + + setCurrentLimit(config.current_limit); + setDio2AsRfSwitch(config.dio2_as_rf_switch); + setRxBoostedGainMode(config.rx_boosted_gain); + if (config.lora_rxen_pin != RADIOLIB_NC || config.lora_txen_pin != RADIOLIB_NC) { + setRfSwitchPins(config.lora_rxen_pin, config.lora_txen_pin); + } + + return true; + } + + bool isReceiving() { + uint16_t irq = getIrqFlags(); + bool detected = (irq & SX126X_IRQ_HEADER_VALID) || (irq & SX126X_IRQ_PREAMBLE_DETECTED); + return detected; + } +}; diff --git a/src/helpers/radiolib/LinuxSX1262Wrapper.h b/src/helpers/radiolib/LinuxSX1262Wrapper.h new file mode 100644 index 0000000000..fbbfd19c9b --- /dev/null +++ b/src/helpers/radiolib/LinuxSX1262Wrapper.h @@ -0,0 +1,22 @@ +#pragma once + +#include "LinuxSX1262.h" +#include "RadioLibWrappers.h" + +class LinuxSX1262Wrapper : public RadioLibWrapper { +public: + LinuxSX1262Wrapper(LinuxSX1262& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } + bool isReceivingPacket() override { + return ((LinuxSX1262 *)_radio)->isReceiving(); + } + float getCurrentRSSI() override { + return ((LinuxSX1262 *)_radio)->getRSSI(false); + } + float getLastRSSI() const override { return ((LinuxSX1262 *)_radio)->getRSSI(); } + float getLastSNR() const override { return ((LinuxSX1262 *)_radio)->getSNR(); } + + float packetScore(float snr, int packet_len) override { + int sf = ((LinuxSX1262 *)_radio)->spreadingFactor; + return packetScoreInt(snr, sf, packet_len); + } +}; diff --git a/variants/linux/LinuxBoard.cpp b/variants/linux/LinuxBoard.cpp new file mode 100644 index 0000000000..0feb95d2bc --- /dev/null +++ b/variants/linux/LinuxBoard.cpp @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include "linux/gpio/LinuxGPIOPin.h" +#include "LinuxBoard.h" + +int initGPIOPin(uint8_t pinNum, const std::string gpioChipName, uint8_t line) +{ +#ifdef PORTDUINO_LINUX_HARDWARE + char gpio_name[32]; + snprintf(gpio_name, sizeof(gpio_name), "GPIO%d", pinNum); + + try { + GPIOPin *csPin; + csPin = new LinuxGPIOPin(pinNum, gpioChipName.c_str(), line, gpio_name); + csPin->setSilent(); + gpioBind(csPin); + return 0; + } catch (...) { + MESH_DEBUG_PRINTLN("Warning, cannot claim pin %d", pinNum); + return 1; + } +#else + return 0; +#endif +} + +void portduinoSetup() { +} + +void LinuxBoard::begin() { + config.load("/etc/meshcored/meshcored.ini"); + + Serial.printf("SPI begin %s\n", config.spidev); + SPI.begin(config.spidev); + + Serial.printf("LoRa pins NSS=%d BUSY=%d IRQ=%d RESET=%d TX=%d RX=%d\n", + (int)config.lora_nss_pin, + (int)config.lora_busy_pin, + (int)config.lora_irq_pin, + (int)config.lora_reset_pin, + (int)config.lora_rxen_pin, + (int)config.lora_txen_pin); + + if (config.lora_nss_pin != RADIOLIB_NC) { + initGPIOPin(config.lora_nss_pin, "gpiochip0", config.lora_nss_pin); + } + if (config.lora_busy_pin != RADIOLIB_NC) { + initGPIOPin(config.lora_busy_pin, "gpiochip0", config.lora_busy_pin); + } + if (config.lora_irq_pin != RADIOLIB_NC) { + initGPIOPin(config.lora_irq_pin, "gpiochip0", config.lora_irq_pin); + } + if (config.lora_reset_pin != RADIOLIB_NC) { + initGPIOPin(config.lora_reset_pin, "gpiochip0", config.lora_reset_pin); + } + if (config.lora_rxen_pin != RADIOLIB_NC) { + initGPIOPin(config.lora_rxen_pin, "gpiochip0", config.lora_rxen_pin); + } + if (config.lora_txen_pin != RADIOLIB_NC) { + initGPIOPin(config.lora_txen_pin, "gpiochip0", config.lora_txen_pin); + } +} + +void trim(char *str) { + char *end; + while (isspace((unsigned char)*str)) str++; + if (*str == 0) { *str = 0; return; } + end = str + strlen(str) - 1; + while (end > str && isspace((unsigned char)*end)) end--; + end[1] = '\0'; +} + +char *safe_copy(char *value, size_t maxlen) { + char *retval; + size_t length = strlen(value) + 1; + if (length > maxlen) length = maxlen; + + retval = (char *)malloc(length); + strncpy(retval, value, length - 1); + retval[length - 1] = '\0'; + return retval; +} + +int LinuxConfig::load(const char *filename) { + FILE *f = fopen(filename, "r"); + if (!f) return -1; + + char line[512]; + while (fgets(line, sizeof(line), f)) { + char *p = line; + // skip whitespace + while (isspace(*p)) p++; + // skip empty lines and comments + if (*p == '\0' || *p == '#' || *p == ';') continue; + + char *key = p; + while (*p && !isspace(*p) && *p != '=') p++; + if (*p == '\0') continue; + *p++ = '\0'; + + while (*p && (isspace(*p) || *p == '=')) p++; + char *value = p; + p = value; + while (*p && *p != '\n' && *p != '\r' && *p != '#' && *p != ';') p++; + *p = '\0'; + + trim(key); + trim(value); + + if (strcmp(key, "spidev") == 0) spidev = safe_copy(value, 32); + else if (strcmp(key, "lora_freq") == 0) lora_freq = atof(value); + else if (strcmp(key, "lora_bw") == 0) lora_bw = atof(value); + else if (strcmp(key, "lora_sf") == 0) lora_sf = (uint8_t)atoi(value); + else if (strcmp(key, "lora_cr") == 0) lora_cr = (uint8_t)atoi(value); + else if (strcmp(key, "lora_tcxo") == 0) lora_tcxo = atof(value); + else if (strcmp(key, "lora_tx_power") == 0) lora_tx_power = atoi(value); + else if (strcmp(key, "current_limit") == 0) current_limit = atof(value); + else if (strcmp(key, "dio2_as_rf_switch") == 0) dio2_as_rf_switch = value != 0; + else if (strcmp(key, "rx_boosted_gain") == 0) rx_boosted_gain = value != 0; + + else if (strcmp(key, "lora_irq_pin") == 0) lora_irq_pin = atoi(value); + else if (strcmp(key, "lora_reset_pin") == 0) lora_reset_pin = atoi(value); + else if (strcmp(key, "lora_nss_pin") == 0) lora_nss_pin = atoi(value); + else if (strcmp(key, "lora_busy_pin") == 0) lora_busy_pin = atoi(value); + else if (strcmp(key, "lora_rxen_pin") == 0) lora_rxen_pin = atoi(value); + else if (strcmp(key, "lora_txen_pin") == 0) lora_txen_pin = atoi(value); + + else if (strcmp(key, "advert_name") == 0) advert_name = safe_copy(value, 100); + else if (strcmp(key, "admin_password") == 0) admin_password = safe_copy(value, 100); + else if (strcmp(key, "lat") == 0) lat = atof(value); + else if (strcmp(key, "lon") == 0) lon = atof(value); + } + fclose(f); + return 0; +} diff --git a/variants/linux/LinuxBoard.h b/variants/linux/LinuxBoard.h new file mode 100644 index 0000000000..c7ae7501c8 --- /dev/null +++ b/variants/linux/LinuxBoard.h @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include +#include + +class LinuxConfig { +public: + float lora_freq = LORA_FREQ; + float lora_bw = LORA_BW; + uint8_t lora_sf = LORA_SF; +#ifdef LORA_CR + uint8_t lora_cr = LORA_CR; +#else + uint8_t lora_cr = 5; +#endif + + uint32_t lora_irq_pin = RADIOLIB_NC; + uint32_t lora_reset_pin = RADIOLIB_NC; + uint32_t lora_nss_pin = RADIOLIB_NC; + uint32_t lora_busy_pin = RADIOLIB_NC; + uint32_t lora_rxen_pin = RADIOLIB_NC; + uint32_t lora_txen_pin = RADIOLIB_NC; + + int8_t lora_tx_power = 22; + float current_limit = 140; + bool dio2_as_rf_switch = false; + bool rx_boosted_gain = true; + + char* spidev = "/dev/spidev0.0"; + + float lora_tcxo = 1.8f; + + char *advert_name = "Linux Repeater"; + char *admin_password = "password"; + float lat = 0.0f; + float lon = 0.0f; + + int load(const char *filename); +}; + +class LinuxBoard : public mesh::MainBoard { +protected: + uint8_t startup_reason; + uint8_t btn_prev_state; + +public: + void begin(); + + uint16_t getBattMilliVolts() override { + return 0; + } + + uint8_t getStartupReason() const override { return startup_reason; } + + const char* getManufacturerName() const override { + return "Linux"; + } + + int buttonStateChanged() { + return 0; + } + + void powerOff() override { + exit(0); + } + + void reboot() override { + exit(0); + } + + LinuxConfig config; +}; + +class LinuxRTCClock : public mesh::RTCClock { +public: + LinuxRTCClock() { } + void begin() { + } + uint32_t getCurrentTime() override { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec; + } + void setCurrentTime(uint32_t time) override { + struct timeval tv; + tv.tv_sec = time; + tv.tv_usec = 0; + settimeofday(&tv, NULL); + } +}; diff --git a/variants/linux/meshcored.ini b/variants/linux/meshcored.ini new file mode 100644 index 0000000000..6fb346fbbb --- /dev/null +++ b/variants/linux/meshcored.ini @@ -0,0 +1,28 @@ +advert_name = "Sample Router" +admin_password = "password" +lat = 0.0 +lon = 0.0 + +# Waveshare LoRa hat +#lora_irq_pin = 16 +#lora_reset_pin = 18 +#lora_nss_pin = 21 +#lora_busy_pin = 20 + +lora_irq_pin = 22 +lora_reset_pin = 13 +#lora_nss_pin = # SS pin handled by RPI +#lora_busy_pin = # Seems to be unused? +#lora_rxen_pin +#lora_txen_pin + +spidev = /dev/spidev0.0 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +#lora_tx_power = 22 +#current_limit = 140 +#dio2_as_rf_switch = 1 +#rx_boosted_gain = 1 diff --git a/variants/linux/meshcored.service b/variants/linux/meshcored.service new file mode 100644 index 0000000000..345a10724b --- /dev/null +++ b/variants/linux/meshcored.service @@ -0,0 +1,29 @@ +# /var/lib/systemd/system/meshcored.service +[Unit] +Description=Meshcore Daemon (meshcored) +After=network.target +Wants=network.target + +[Service] +Type=simple +User=meshcore +Group=meshcore +ExecStart=/usr/bin/stdbuf -oL /usr/bin/meshcored --fsdir /var/lib/meshcore +WorkingDirectory=/var/lib/meshcore +Restart=on-failure +RestartSec=5 +LimitNOFILE=65535 + +# Security hardening +ProtectSystem=strict +ProtectHome=yes +PrivateTmp=yes +NoNewPrivileges=yes +ReadWritePaths=/var/lib/meshcore # allow writing only to its own data dir + +# Create data dir with correct ownership if it doesn't exist +ExecStartPre=/bin/mkdir -p /var/lib/meshcore +ExecStartPre=/bin/chown meshcore:meshcore /var/lib/meshcore + +[Install] +WantedBy=multi-user.target diff --git a/variants/linux/platformio.ini b/variants/linux/platformio.ini new file mode 100644 index 0000000000..d60a50fe71 --- /dev/null +++ b/variants/linux/platformio.ini @@ -0,0 +1,47 @@ +[linux_base] +extends = portduino_base +build_flags = ${portduino_base.build_flags} + -I variants/linux + -I /usr/include +board = cross_platform +board_level = extra +lib_deps = + ${portduino_base.lib_deps} + melopero/Melopero RV3028@^1.1.0 + +build_src_filter = ${portduino_base.build_src_filter} + +<../variants/linux> + - + - + - + - + - + +[env:linux] +extends = linux_base +; The pkg-config commands below optionally add link flags. +; the || : is just a "or run the null command" to avoid returning an error code +build_flags = ${linux_base.build_flags} + !pkg-config --cflags --libs libbsd-overlay --silence-errors || : + +[env:linux_repeater] +extends = linux_base +build_flags = + ${linux_base.build_flags} + -D RADIO_CLASS=LinuxSX1262 + -D WRAPPER_CLASS=LinuxSX1262Wrapper + -D USE_CUSTOM_SX1262_WRAPPER + -D SKIP_CONFIG_OVERWRITE=1 + -D ADVERT_NAME='"Linux Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=100 + -D LORA_TX_POWER=22 + -D MESH_DEBUG=1 + +build_src_filter = ${linux_base.build_src_filter} + +<../examples/simple_repeater> + +lib_deps = + ${linux_base.lib_deps} diff --git a/variants/linux/target.cpp b/variants/linux/target.cpp new file mode 100644 index 0000000000..0f5941f890 --- /dev/null +++ b/variants/linux/target.cpp @@ -0,0 +1,54 @@ +#include +#include "target.h" + +class PortduinoHal : public ArduinoHal +{ +public: + PortduinoHal(SPIClass &spi, SPISettings spiSettings) : ArduinoHal(spi, spiSettings){}; + + void spiTransfer(uint8_t *out, size_t len, uint8_t *in) { + spi->transfer(out, in, len); + } +}; + +LinuxBoard board; + +SPISettings spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0); +ArduinoHal *hal = new PortduinoHal(SPI, spiSettings); +RADIO_CLASS radio = new Module(hal, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC); +WRAPPER_CLASS radio_driver(radio, board); + +LinuxRTCClock rtc_clock; +EnvironmentSensorManager sensors; + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); +#endif + +bool radio_init() { + rtc_clock.begin(); + + radio = new Module(hal, board.config.lora_nss_pin, board.config.lora_irq_pin, board.config.lora_reset_pin, board.config.lora_busy_pin); + return radio.std_init(&SPI); +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/linux/target.h b/variants/linux/target.h new file mode 100644 index 0000000000..1f5539ca94 --- /dev/null +++ b/variants/linux/target.h @@ -0,0 +1,32 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#ifdef DISPLAY_CLASS + #include + #include +#endif + +#if (USE_CUSTOM_SX1262_WRAPPER) +#include +#endif + +extern LinuxBoard board; +extern WRAPPER_CLASS radio_driver; +extern LinuxRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +#ifdef DISPLAY_CLASS + extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; +#endif + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/portduino/platformio.ini b/variants/portduino/platformio.ini new file mode 100644 index 0000000000..5785e965aa --- /dev/null +++ b/variants/portduino/platformio.ini @@ -0,0 +1,41 @@ +[portduino_base] +platform = + # renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop + https://github.com/meshtastic/platform-native/archive/f566d364204416cdbf298e349213f7d551f793d9.zip +framework = arduino + +build_src_filter = + ${env.build_src_filter} + - + - + - + - + - + - + - + - + - + +lib_deps = + ${env.lib_deps} + rweather/Crypto@0.4.0 + adafruit/Adafruit seesaw Library@1.7.9 + electroniccats/CayenneLPP @ 1.6.1 + adafruit/RTClib @ ^2.1.3 + jgromes/RadioLib@7.4.0 + +build_flags = + ${arduino_base.build_flags} + -DARCH_PORTDUINO + -DPORTDUINO_PLATFORM + -DRADIOLIB_EEPROM_UNSUPPORTED + -DPORTDUINO_LINUX_HARDWARE + -fPIC + -lpthread + -lstdc++fs + -lbluetooth + -lgpiod + -li2c + -luv + -std=gnu17 + -std=c++17 From 2ede192d673f0491ae66ebb30b06ebe66c96532b Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Sat, 14 Mar 2026 14:38:31 +0100 Subject: [PATCH 02/14] variants: add scaffolding for linux native (#1) * variants: add scaffolding for linux native * address review comments * address review comments --- examples/simple_repeater/MyMesh.cpp | 8 +-- examples/simple_repeater/MyMesh.h | 2 + examples/simple_repeater/main.cpp | 8 +++ src/helpers/ClientACL.cpp | 2 +- src/helpers/CommonCLI.cpp | 2 +- src/helpers/IdentityStore.cpp | 4 +- src/helpers/IdentityStore.h | 2 +- src/helpers/RegionMap.cpp | 2 +- src/helpers/TxtDataHelpers.cpp | 7 +++ variants/linux/LinuxBoard.cpp | 1 + variants/linux/LinuxBoard.h | 3 + variants/linux/README.md | 92 +++++++++++++++++++++++++++++ variants/linux/meshcored.ini | 1 + variants/linux/meshcored.service | 2 +- 14 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 variants/linux/README.md diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 096907494b..c1c1fc19a5 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -389,7 +389,7 @@ mesh::Packet *MyMesh::createSelfAdvert() { File MyMesh::openAppend(const char *fname) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) return _fs->open(fname, FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) +#elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) return _fs->open(fname, "a"); #else return _fs->open(fname, "a", true); @@ -1004,6 +1004,8 @@ bool MyMesh::formatFileSystem() { return LittleFS.format(); #elif defined(ESP32) return SPIFFS.format(); +#elif defined(ARCH_PORTDUINO) + return false; // not supported on Linux #else #error "need to implement file system erase" return false; @@ -1155,9 +1157,7 @@ void MyMesh::formatPacketStatsReply(char *reply) { void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) IdentityStore store(*_fs, ""); -#elif defined(ESP32) - IdentityStore store(*_fs, "/identity"); -#elif defined(RP2040_PLATFORM) +#elif defined(ESP32) || defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) IdentityStore store(*_fs, "/identity"); #else #error "need to define saveIdentity()" diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 7597c6c6f6..d643e0cced 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -11,6 +11,8 @@ #include #elif defined(ESP32) #include +#elif defined(ARCH_PORTDUINO) + #include #endif #ifdef WITH_RS232_BRIDGE diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 2ce056f521..38e1fa5b06 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -69,6 +69,14 @@ void setup() { fs = &LittleFS; IdentityStore store(LittleFS, "/identity"); store.begin(); +#elif defined(ARCH_PORTDUINO) + if (::mkdir(board.config.data_dir, 0755) != 0 && errno != EEXIST) { + Serial.printf("WARNING: could not create data_dir '%s': %s\n", board.config.data_dir, strerror(errno)); + } + portduinoVFS->mountpoint(board.config.data_dir); + fs = &PortduinoFS; + IdentityStore store(PortduinoFS, "/identity"); + store.begin(); #else #error "need to define filesystem" #endif diff --git a/src/helpers/ClientACL.cpp b/src/helpers/ClientACL.cpp index 1282382737..40a98a5b7a 100644 --- a/src/helpers/ClientACL.cpp +++ b/src/helpers/ClientACL.cpp @@ -4,7 +4,7 @@ static File openWrite(FILESYSTEM* _fs, const char* filename) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove(filename); return _fs->open(filename, FILE_O_WRITE); - #elif defined(RP2040_PLATFORM) + #elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) return _fs->open(filename, "w"); #else return _fs->open(filename, "w", true); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index b78ad6ebd6..3fd0d3c359 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -130,7 +130,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) fs->remove("/com_prefs"); File file = fs->open("/com_prefs", FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) +#elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) File file = fs->open("/com_prefs", "w"); #else File file = fs->open("/com_prefs", "w", true); diff --git a/src/helpers/IdentityStore.cpp b/src/helpers/IdentityStore.cpp index dc85d69cdd..0a9700f0aa 100644 --- a/src/helpers/IdentityStore.cpp +++ b/src/helpers/IdentityStore.cpp @@ -49,7 +49,7 @@ bool IdentityStore::save(const char *name, const mesh::LocalIdentity& id) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove(filename); File file = _fs->open(filename, FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) +#elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) File file = _fs->open(filename, "w"); #else File file = _fs->open(filename, "w", true); @@ -71,7 +71,7 @@ bool IdentityStore::save(const char *name, const mesh::LocalIdentity& id, const #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove(filename); File file = _fs->open(filename, FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) +#elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) File file = _fs->open(filename, "w"); #else File file = _fs->open(filename, "w", true); diff --git a/src/helpers/IdentityStore.h b/src/helpers/IdentityStore.h index d0d7ee457e..a96aa536cc 100644 --- a/src/helpers/IdentityStore.h +++ b/src/helpers/IdentityStore.h @@ -1,6 +1,6 @@ #pragma once -#if defined(ESP32) || defined(RP2040_PLATFORM) +#if defined(ESP32) || defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) #include #define FILESYSTEM fs::FS #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp index 7b8399e260..ddc31287f8 100644 --- a/src/helpers/RegionMap.cpp +++ b/src/helpers/RegionMap.cpp @@ -62,7 +62,7 @@ static File openWrite(FILESYSTEM* _fs, const char* filename) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove(filename); return _fs->open(filename, FILE_O_WRITE); - #elif defined(RP2040_PLATFORM) + #elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) return _fs->open(filename, "w"); #else return _fs->open(filename, "w", true); diff --git a/src/helpers/TxtDataHelpers.cpp b/src/helpers/TxtDataHelpers.cpp index d327931fde..60832f6653 100644 --- a/src/helpers/TxtDataHelpers.cpp +++ b/src/helpers/TxtDataHelpers.cpp @@ -1,4 +1,7 @@ #include "TxtDataHelpers.h" +#if defined(ARCH_PORTDUINO) + #include +#endif void StrHelper::strncpy(char* dest, const char* src, size_t buf_sz) { while (buf_sz > 1 && *src) { @@ -102,7 +105,11 @@ static void _ftoa(float f, char *p, int *status) *p++ = '0'; else { +#if defined(ARCH_PORTDUINO) + sprintf(p, "%" PRId32, int_part); +#else ltoa(int_part, p, 10); +#endif while (*p) p++; } diff --git a/variants/linux/LinuxBoard.cpp b/variants/linux/LinuxBoard.cpp index 0feb95d2bc..dee65baed1 100644 --- a/variants/linux/LinuxBoard.cpp +++ b/variants/linux/LinuxBoard.cpp @@ -131,6 +131,7 @@ int LinuxConfig::load(const char *filename) { else if (strcmp(key, "admin_password") == 0) admin_password = safe_copy(value, 100); else if (strcmp(key, "lat") == 0) lat = atof(value); else if (strcmp(key, "lon") == 0) lon = atof(value); + else if (strcmp(key, "data_dir") == 0) data_dir = safe_copy(value, 256); } fclose(f); return 0; diff --git a/variants/linux/LinuxBoard.h b/variants/linux/LinuxBoard.h index c7ae7501c8..584ef195f6 100644 --- a/variants/linux/LinuxBoard.h +++ b/variants/linux/LinuxBoard.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include class LinuxConfig { @@ -36,6 +38,7 @@ class LinuxConfig { char *admin_password = "password"; float lat = 0.0f; float lon = 0.0f; + char *data_dir = "/var/lib/meshcore"; int load(const char *filename); }; diff --git a/variants/linux/README.md b/variants/linux/README.md new file mode 100644 index 0000000000..68d9416a0c --- /dev/null +++ b/variants/linux/README.md @@ -0,0 +1,92 @@ +# MeshCore Linux Variant + +Native Linux support for MeshCore, targeting Raspberry Pi (Zero, 3, 4, 5) and similar SBCs with an SX1262 LoRa radio attached over SPI. Uses the [Portduino](https://github.com/meshtastic/platform-native) Arduino-compatibility layer to run the same firmware codebase on Linux without modification to the core library. + +## Hardware + +- Raspberry Pi (any model with SPI) +- SX1262-based LoRa module wired to the Pi's SPI bus (e.g. Waveshare SX1262 HAT, RAK2287, similar) +- SPI, IRQ, RESET, and optionally BUSY/RXEN/TXEN GPIO pins + +## Build + +**Dependencies** (install on the build machine and on the Pi): + +```sh +# Arch Linux +sudo pacman -S libgpiod i2c-tools + +# Debian/Raspberry Pi OS +sudo apt install libgpiod-dev libi2c-dev +``` + +**Build with PlatformIO:** + +```sh +FIRMWARE_VERSION=dev pio run -e linux_repeater +# binary: .pio/build/linux_repeater/program +``` + +## Configuration + +The binary reads `/etc/meshcored/meshcored.ini` on startup. Copy and edit the sample: + +```sh +sudo mkdir -p /etc/meshcored +sudo cp variants/linux/meshcored.ini /etc/meshcored/meshcored.ini +sudo nano /etc/meshcored/meshcored.ini +``` + +Key settings: + +| Key | Default | Notes | +|-----|---------|-------| +| `spidev` | `/dev/spidev0.0` | SPI device node | +| `lora_irq_pin` | (none) | GPIO pin number for IRQ | +| `lora_reset_pin` | (none) | GPIO pin number for RESET | +| `lora_nss_pin` | (none) | GPIO pin number for NSS/CS (if not handled by SPI driver) | +| `lora_busy_pin` | (none) | GPIO pin number for BUSY | +| `lora_freq` | `869.618` | Frequency in MHz | +| `lora_bw` | `62.5` | Bandwidth in kHz | +| `lora_sf` | `8` | Spreading factor | +| `lora_cr` | `8` | Coding rate | +| `lora_tcxo` | `1.8` | TCXO voltage (V); set to `0.0` if no TCXO | +| `lora_tx_power` | `22` | TX power in dBm | +| `advert_name` | `"Linux Repeater"` | Node name broadcast to the mesh | +| `admin_password` | `"password"` | Change this | +| `lat` / `lon` | `0.0` | GPS coordinates for advertisement | +| `data_dir` | `/var/lib/meshcore` | Where identity and prefs are stored | + +## Running + +Enable SPI and set GPIO permissions on the Pi: + +```sh +# Raspberry Pi OS +sudo raspi-config # Interface Options → SPI → Enable +sudo usermod -aG spi,gpio $USER +``` + +Run directly: + +```sh +sudo .pio/build/linux_repeater/program +``` + +## Systemd Service + +```sh +sudo cp variants/linux/meshcored.service /etc/systemd/system/ +sudo useradd -r -s /sbin/nologin meshcore +sudo install -d -o meshcore -g meshcore /var/lib/meshcore +# copy binary to /usr/bin/meshcored +sudo systemctl enable --now meshcored +sudo journalctl -u meshcored -f +``` + +## Known Gaps / TODO + +- **No CLI argument parsing** — config path is hardcoded to `/etc/meshcored/meshcored.ini`; `data_dir` is only configurable via the INI file. +- **Only repeater firmware** — there is no `linux_companion` target yet; companion radio support (BLE/serial interface to a phone app) is not implemented for Linux. +- **`formatFileSystem()`** returns `false` (not implemented) — the CLI `format` command will report failure on Linux. +- **No power management** — `board.sleep()` is a no-op; the power-saving loop in `main.cpp` never actually sleeps. diff --git a/variants/linux/meshcored.ini b/variants/linux/meshcored.ini index 6fb346fbbb..1a10bb147d 100644 --- a/variants/linux/meshcored.ini +++ b/variants/linux/meshcored.ini @@ -2,6 +2,7 @@ advert_name = "Sample Router" admin_password = "password" lat = 0.0 lon = 0.0 +#data_dir = /var/lib/meshcore # Waveshare LoRa hat #lora_irq_pin = 16 diff --git a/variants/linux/meshcored.service b/variants/linux/meshcored.service index 345a10724b..c81ca4776f 100644 --- a/variants/linux/meshcored.service +++ b/variants/linux/meshcored.service @@ -8,7 +8,7 @@ Wants=network.target Type=simple User=meshcore Group=meshcore -ExecStart=/usr/bin/stdbuf -oL /usr/bin/meshcored --fsdir /var/lib/meshcore +ExecStart=/usr/bin/stdbuf -oL /usr/bin/meshcored WorkingDirectory=/var/lib/meshcore Restart=on-failure RestartSec=5 From e907f689dd0928ab9900c2b24f62d75281d3c7dd Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Sat, 14 Mar 2026 19:12:11 +0100 Subject: [PATCH 03/14] variants: allow linux repeater to be configured at runtime (#2) * variants: allow linux repeater to be configured at runtime * address review comments * address review comments * address review comments --- examples/simple_repeater/MyMesh.cpp | 13 +++ variants/linux/99-meshcore.rules | 6 ++ variants/linux/LinuxBoard.cpp | 9 ++ variants/linux/README.md | 108 ++++++++++++++++++------ variants/linux/meshcored.ini | 4 +- variants/linux/meshcored.ini.pow-sx1262 | 24 ++++++ variants/linux/meshcored.ini.waveshare | 24 ++++++ variants/linux/meshcored.service | 5 +- variants/linux/platformio.ini | 4 - 9 files changed, 165 insertions(+), 32 deletions(-) create mode 100644 variants/linux/99-meshcore.rules create mode 100644 variants/linux/meshcored.ini.pow-sx1262 create mode 100644 variants/linux/meshcored.ini.waveshare diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index c1c1fc19a5..a8d8053ec2 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -927,6 +927,19 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc void MyMesh::begin(FILESYSTEM *fs) { mesh::Mesh::begin(); _fs = fs; +#if defined(ARCH_PORTDUINO) + // Apply runtime INI config as first-run defaults before loading persisted prefs. + // If /com_prefs exists, loadPrefs() below will overwrite these with the saved values. + StrHelper::strncpy(_prefs.node_name, board.config.advert_name, sizeof(_prefs.node_name)); + _prefs.node_lat = board.config.lat; + _prefs.node_lon = board.config.lon; + StrHelper::strncpy(_prefs.password, board.config.admin_password, sizeof(_prefs.password)); + _prefs.freq = board.config.lora_freq; + _prefs.bw = board.config.lora_bw; + _prefs.sf = board.config.lora_sf; + _prefs.cr = board.config.lora_cr; + _prefs.tx_power_dbm = board.config.lora_tx_power; +#endif // load persisted prefs _cli.loadPrefs(_fs); acl.load(_fs, self_id); diff --git a/variants/linux/99-meshcore.rules b/variants/linux/99-meshcore.rules new file mode 100644 index 0000000000..4c3df92e47 --- /dev/null +++ b/variants/linux/99-meshcore.rules @@ -0,0 +1,6 @@ +# udev rules for meshcored — grant the meshcore group access to SPI and GPIO devices. +# Works on Arch Linux, Debian/Raspberry Pi OS, and other distributions. +# Install: sudo cp 99-meshcore.rules /etc/udev/rules.d/ + +SUBSYSTEM=="spidev", GROUP="meshcore", MODE="0660" +KERNEL=="gpiochip*", GROUP="meshcore", MODE="0660" diff --git a/variants/linux/LinuxBoard.cpp b/variants/linux/LinuxBoard.cpp index dee65baed1..e975884c15 100644 --- a/variants/linux/LinuxBoard.cpp +++ b/variants/linux/LinuxBoard.cpp @@ -109,6 +109,15 @@ int LinuxConfig::load(const char *filename) { trim(key); trim(value); + // strip optional surrounding quotes from string values + { + size_t vlen = strlen(value); + if (vlen >= 2 && (value[0] == '"' || value[0] == '\'') && value[vlen-1] == value[0]) { + value[vlen-1] = '\0'; + value++; + } + } + if (strcmp(key, "spidev") == 0) spidev = safe_copy(value, 32); else if (strcmp(key, "lora_freq") == 0) lora_freq = atof(value); else if (strcmp(key, "lora_bw") == 0) lora_bw = atof(value); diff --git a/variants/linux/README.md b/variants/linux/README.md index 68d9416a0c..276fd8e6c7 100644 --- a/variants/linux/README.md +++ b/variants/linux/README.md @@ -5,7 +5,7 @@ Native Linux support for MeshCore, targeting Raspberry Pi (Zero, 3, 4, 5) and si ## Hardware - Raspberry Pi (any model with SPI) -- SX1262-based LoRa module wired to the Pi's SPI bus (e.g. Waveshare SX1262 HAT, RAK2287, similar) +- SX1262-based LoRa module wired to the Pi's SPI bus (e.g. Waveshare SX1262 HAT, PoW SX1262 HAT) - SPI, IRQ, RESET, and optionally BUSY/RXEN/TXEN GPIO pins ## Build @@ -20,73 +20,133 @@ sudo pacman -S libgpiod i2c-tools sudo apt install libgpiod-dev libi2c-dev ``` -**Build with PlatformIO:** +**Build with `build.sh`** (recommended — embeds version and commit hash): ```sh -FIRMWARE_VERSION=dev pio run -e linux_repeater +FIRMWARE_VERSION=dev ./build.sh build-firmware linux_repeater # binary: .pio/build/linux_repeater/program ``` -## Configuration +Alternatively, build directly with PlatformIO (no version metadata): + +```sh +FIRMWARE_VERSION=dev pio run -e linux_repeater +``` + +## Setup + +### 1. Install the binary + +```sh +sudo cp .pio/build/linux_repeater/program /usr/bin/meshcored +``` + +### 2. Create the config file + +Two ready-made templates are provided in `variants/linux/`: -The binary reads `/etc/meshcored/meshcored.ini` on startup. Copy and edit the sample: +| Template | Hardware | +|----------|----------| +| `meshcored.ini.pow-sx1262` | RPi Zero 2W + PoW SX1262 HAT | +| `meshcored.ini.waveshare` | RPi 3/4/5 + Waveshare SX1262 LoRa HAT | ```sh sudo mkdir -p /etc/meshcored -sudo cp variants/linux/meshcored.ini /etc/meshcored/meshcored.ini +# Pick the template that matches your hardware: +sudo cp variants/linux/meshcored.ini.waveshare /etc/meshcored/meshcored.ini sudo nano /etc/meshcored/meshcored.ini ``` +The config file has two roles: + +- **Hardware config** (always read on every startup): SPI device, GPIO pin numbers, LoRa radio parameters. +- **First-run node defaults**: `advert_name`, `admin_password`, `lat`, `lon`. On the first boot these are saved to `data_dir`. After that, use the serial CLI to change them (`set name`, `set password`, etc.) — the INI values are no longer consulted for these fields. + Key settings: | Key | Default | Notes | |-----|---------|-------| | `spidev` | `/dev/spidev0.0` | SPI device node | -| `lora_irq_pin` | (none) | GPIO pin number for IRQ | -| `lora_reset_pin` | (none) | GPIO pin number for RESET | -| `lora_nss_pin` | (none) | GPIO pin number for NSS/CS (if not handled by SPI driver) | -| `lora_busy_pin` | (none) | GPIO pin number for BUSY | +| `lora_irq_pin` | (none) | GPIO line number for IRQ | +| `lora_reset_pin` | (none) | GPIO line number for RESET | +| `lora_nss_pin` | (none) | GPIO line number for NSS/CS (if not handled by the SPI driver) | +| `lora_busy_pin` | (none) | GPIO line number for BUSY | | `lora_freq` | `869.618` | Frequency in MHz | | `lora_bw` | `62.5` | Bandwidth in kHz | | `lora_sf` | `8` | Spreading factor | | `lora_cr` | `8` | Coding rate | -| `lora_tcxo` | `1.8` | TCXO voltage (V); set to `0.0` if no TCXO | +| `lora_tcxo` | `1.8` | TCXO voltage (V); set to `0.0` if your module has no TCXO | | `lora_tx_power` | `22` | TX power in dBm | -| `advert_name` | `"Linux Repeater"` | Node name broadcast to the mesh | -| `admin_password` | `"password"` | Change this | -| `lat` / `lon` | `0.0` | GPS coordinates for advertisement | -| `data_dir` | `/var/lib/meshcore` | Where identity and prefs are stored | +| `advert_name` | `"Linux Repeater"` | Node name — first-run default only | +| `admin_password` | `"password"` | Admin password — **change this**, first-run default only | +| `lat` / `lon` | `0.0` | GPS coordinates for advertisement — first-run default only | +| `data_dir` | `/var/lib/meshcore` | Where identity and node prefs are persisted | -## Running - -Enable SPI and set GPIO permissions on the Pi: +### 3. Enable SPI and GPIO access ```sh # Raspberry Pi OS -sudo raspi-config # Interface Options → SPI → Enable +sudo raspi-config # Interface Options → SPI → Enable sudo usermod -aG spi,gpio $USER ``` -Run directly: +On Arch Linux and other distributions without `spi`/`gpio` groups, use the provided udev rules instead (also works on Raspberry Pi OS): ```sh -sudo .pio/build/linux_repeater/program +sudo cp variants/linux/99-meshcore.rules /etc/udev/rules.d/ +sudo udevadm control --reload-rules && sudo udevadm trigger ``` -## Systemd Service +### 4. Run + +**Directly** (for testing): + +```sh +sudo /usr/bin/meshcored +``` + +`sudo` is needed on first run to create `data_dir` if it doesn't exist. Once the directory is created and owned appropriately, it can run as a non-root user. + +**As a systemd service** (recommended for production): ```sh sudo cp variants/linux/meshcored.service /etc/systemd/system/ +sudo cp variants/linux/99-meshcore.rules /etc/udev/rules.d/ +sudo udevadm control --reload-rules && sudo udevadm trigger sudo useradd -r -s /sbin/nologin meshcore -sudo install -d -o meshcore -g meshcore /var/lib/meshcore -# copy binary to /usr/bin/meshcored +sudo mkdir -p /var/lib/meshcore +sudo chown meshcore:meshcore /var/lib/meshcore +sudo chmod 640 /etc/meshcored/meshcored.ini +sudo chown root:meshcore /etc/meshcored/meshcored.ini +sudo systemctl daemon-reload sudo systemctl enable --now meshcored sudo journalctl -u meshcored -f ``` +### 5. Reconfiguring after first run + +Node name, password, and location can be changed via the serial CLI after first boot: + +``` +set name +set password +set lat +set lon +``` + +To reset all node prefs and re-apply the INI file defaults, delete the saved prefs and restart: + +```sh +sudo rm /var/lib/meshcore/com_prefs +sudo systemctl restart meshcored +``` + +> **Note:** LoRa radio parameters (`lora_freq`, `lora_bw`, `lora_sf`, `lora_cr`, `lora_tx_power`) are also first-run defaults. After first boot they are saved in `com_prefs` and the INI values are no longer read for those fields. To apply a changed radio parameter, use the CLI (`set freq`, `set sf`, etc.) or reset prefs as above. + ## Known Gaps / TODO - **No CLI argument parsing** — config path is hardcoded to `/etc/meshcored/meshcored.ini`; `data_dir` is only configurable via the INI file. - **Only repeater firmware** — there is no `linux_companion` target yet; companion radio support (BLE/serial interface to a phone app) is not implemented for Linux. - **`formatFileSystem()`** returns `false` (not implemented) — the CLI `format` command will report failure on Linux. - **No power management** — `board.sleep()` is a no-op; the power-saving loop in `main.cpp` never actually sleeps. +- **Portduino branding** — on startup the binary identifies itself as "An application written with portduino" with a Meshtastic bug URL. This is hardcoded in the Portduino framework and cannot be changed without patching the framework. diff --git a/variants/linux/meshcored.ini b/variants/linux/meshcored.ini index 1a10bb147d..2c57adbbb9 100644 --- a/variants/linux/meshcored.ini +++ b/variants/linux/meshcored.ini @@ -1,5 +1,5 @@ -advert_name = "Sample Router" -admin_password = "password" +advert_name = Sample Router +admin_password = password lat = 0.0 lon = 0.0 #data_dir = /var/lib/meshcore diff --git a/variants/linux/meshcored.ini.pow-sx1262 b/variants/linux/meshcored.ini.pow-sx1262 new file mode 100644 index 0000000000..1f2e03000f --- /dev/null +++ b/variants/linux/meshcored.ini.pow-sx1262 @@ -0,0 +1,24 @@ +# meshcored.ini — PoW SX1262 HAT on Raspberry Pi Zero 2W +# GPIO numbering is BCM (the number after "GPIO", e.g. GPIO22 = 22). +# +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 22 +lora_reset_pin = 13 +# lora_nss_pin — SS is handled by the SPI driver; not needed +# lora_busy_pin — not wired on this HAT +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = PoW Linux Repeater +admin_password = changeme +lat = 0.0 +lon = 0.0 + +# data_dir — where identity and node prefs are persisted (default shown): +#data_dir = /var/lib/meshcore diff --git a/variants/linux/meshcored.ini.waveshare b/variants/linux/meshcored.ini.waveshare new file mode 100644 index 0000000000..84df7f3acd --- /dev/null +++ b/variants/linux/meshcored.ini.waveshare @@ -0,0 +1,24 @@ +# meshcored.ini — Waveshare SX1262 LoRa HAT on Raspberry Pi 3/4/5 +# GPIO numbering is BCM (the number after "GPIO", e.g. GPIO16 = 16). +# +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 16 +lora_reset_pin = 18 +lora_nss_pin = 21 +lora_busy_pin = 20 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = Waveshare Linux Repeater +admin_password = changeme +lat = 0.0 +lon = 0.0 + +# data_dir — where identity and node prefs are persisted (default shown): +#data_dir = /var/lib/meshcore diff --git a/variants/linux/meshcored.service b/variants/linux/meshcored.service index c81ca4776f..b68ff2a62c 100644 --- a/variants/linux/meshcored.service +++ b/variants/linux/meshcored.service @@ -8,7 +8,7 @@ Wants=network.target Type=simple User=meshcore Group=meshcore -ExecStart=/usr/bin/stdbuf -oL /usr/bin/meshcored +ExecStart=/usr/bin/stdbuf -oL /usr/bin/meshcored --fsdir /var/lib/meshcore WorkingDirectory=/var/lib/meshcore Restart=on-failure RestartSec=5 @@ -19,7 +19,8 @@ ProtectSystem=strict ProtectHome=yes PrivateTmp=yes NoNewPrivileges=yes -ReadWritePaths=/var/lib/meshcore # allow writing only to its own data dir +# Allow writing only to its own data dir +ReadWritePaths=/var/lib/meshcore # Create data dir with correct ownership if it doesn't exist ExecStartPre=/bin/mkdir -p /var/lib/meshcore diff --git a/variants/linux/platformio.ini b/variants/linux/platformio.ini index d60a50fe71..3fc49b2cfd 100644 --- a/variants/linux/platformio.ini +++ b/variants/linux/platformio.ini @@ -32,10 +32,6 @@ build_flags = -D WRAPPER_CLASS=LinuxSX1262Wrapper -D USE_CUSTOM_SX1262_WRAPPER -D SKIP_CONFIG_OVERWRITE=1 - -D ADVERT_NAME='"Linux Repeater"' - -D ADVERT_LAT=0.0 - -D ADVERT_LON=0.0 - -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=100 -D LORA_TX_POWER=22 -D MESH_DEBUG=1 From 354fe7c00abd6e03084c6cf42db0d16c27fbb88a Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:24:14 +0100 Subject: [PATCH 04/14] Fix boolean config parsing in LinuxBoard (#6) --- variants/linux/LinuxBoard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variants/linux/LinuxBoard.cpp b/variants/linux/LinuxBoard.cpp index e975884c15..4c25da0a11 100644 --- a/variants/linux/LinuxBoard.cpp +++ b/variants/linux/LinuxBoard.cpp @@ -126,8 +126,8 @@ int LinuxConfig::load(const char *filename) { else if (strcmp(key, "lora_tcxo") == 0) lora_tcxo = atof(value); else if (strcmp(key, "lora_tx_power") == 0) lora_tx_power = atoi(value); else if (strcmp(key, "current_limit") == 0) current_limit = atof(value); - else if (strcmp(key, "dio2_as_rf_switch") == 0) dio2_as_rf_switch = value != 0; - else if (strcmp(key, "rx_boosted_gain") == 0) rx_boosted_gain = value != 0; + else if (strcmp(key, "dio2_as_rf_switch") == 0) dio2_as_rf_switch = atoi(value) != 0; + else if (strcmp(key, "rx_boosted_gain") == 0) rx_boosted_gain = atoi(value) != 0; else if (strcmp(key, "lora_irq_pin") == 0) lora_irq_pin = atoi(value); else if (strcmp(key, "lora_reset_pin") == 0) lora_reset_pin = atoi(value); From f23a63a3a45d94ec3286e5f1fa1ee92dad011af4 Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Sun, 31 May 2026 23:05:58 +0200 Subject: [PATCH 05/14] replace portduino with ardulinux (#4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * replace portduino with ardulinux * replace portduino with ardulinux * replace portduino with ardulinux * Remove flags now owned by the ardulinux platform framework ARDULINUX_LINUX_HARDWARE, -lgpiod, and -li2c are detected and injected by builder/frameworks/arduino.py via pkg-config. Hardcoding them here caused linker failures on machines without libgpiod even though the framework would have gracefully omitted them. Also switch variants/ardulinux to the git+ platform URL (dropping the platform-native + platform_packages indirection), update the linux variant board name, and add arduino to the frameworks list in linux.json. * address review comments * Remove stale portduino branding note The startup string was already fixed in l5yth/ardulinux — main.cpp says "An application written with ardulinux". Remove the pending-fix note and update the description to match the current behaviour. * replace portduino with ardulinux * Wire up ardulinux platform and fix SPI/VFS/printf for Linux target variants/ardulinux/platformio.ini: revert local symlink:// back to git+ URL — the symlink only works in a co-located checkout and would break CI. variants/linux/LinuxBoard.cpp: - Add empty ardulinuxSetup() to satisfy the weak symbol; without it the default prints a noisy "No ardulinuxSetup() found" message on startup. - Replace Serial.printf with printf — Serial.printf is not available until after Serial.begin(); using stdio printf is safe at this init-time call site. - Pass 2MHz frequency to SPI.begin() to match the expected SPI clock. variants/linux/target.cpp: fix spiTransfer — ArduLinux's SPI only has a 2-arg transfer(buf, len) that operates in-place; copy out→in first then call the 2-arg form. examples/simple_repeater/main.cpp: fix arduLinuxVFS → ardulinuxVFS (case was wrong; symbol is defined as ardulinuxVFS in ArduLinuxFS.cpp). * set app info to meshcored * fix linux sx1262 wrapper * address review: fix GPIO hardware guard, document spiTransfer, use printf * docs: fix linux variant README (binary name, deps, SPI setup, config keys; use install(1)) --- boards/linux.json | 3 +- examples/simple_repeater/MyMesh.cpp | 8 +-- examples/simple_repeater/MyMesh.h | 4 +- examples/simple_repeater/main.cpp | 10 +-- src/helpers/ClientACL.cpp | 2 +- src/helpers/CommonCLI.cpp | 2 +- src/helpers/IdentityStore.cpp | 4 +- src/helpers/IdentityStore.h | 2 +- src/helpers/RegionMap.cpp | 2 +- src/helpers/TxtDataHelpers.cpp | 4 +- src/helpers/radiolib/LinuxSX1262Wrapper.h | 10 +++ .../{portduino => ardulinux}/platformio.ini | 12 ++-- variants/linux/LinuxBoard.cpp | 29 ++++---- variants/linux/README.md | 66 ++++++++++++++----- variants/linux/platformio.ini | 11 ++-- variants/linux/target.cpp | 13 ++-- 16 files changed, 119 insertions(+), 63 deletions(-) rename variants/{portduino => ardulinux}/platformio.ini (63%) diff --git a/boards/linux.json b/boards/linux.json index 21b5cdc33a..8a120faaca 100644 --- a/boards/linux.json +++ b/boards/linux.json @@ -11,8 +11,9 @@ }, "connectivity": ["wifi", "bluetooth"], "debug": {}, - "frameworks": ["portduino", "linux"], + "frameworks": ["arduino", "ardulinux", "linux"], "name": "Linux", + "url": "https://github.com/l5yth/ardulinux", "upload": { "maximum_ram_size": 0, "maximum_size": 0 diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index a8d8053ec2..322143eb5e 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -389,7 +389,7 @@ mesh::Packet *MyMesh::createSelfAdvert() { File MyMesh::openAppend(const char *fname) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) return _fs->open(fname, FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) +#elif defined(RP2040_PLATFORM) || defined(ARDULINUX_PLATFORM) return _fs->open(fname, "a"); #else return _fs->open(fname, "a", true); @@ -927,7 +927,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc void MyMesh::begin(FILESYSTEM *fs) { mesh::Mesh::begin(); _fs = fs; -#if defined(ARCH_PORTDUINO) +#if defined(ARDULINUX_PLATFORM) // Apply runtime INI config as first-run defaults before loading persisted prefs. // If /com_prefs exists, loadPrefs() below will overwrite these with the saved values. StrHelper::strncpy(_prefs.node_name, board.config.advert_name, sizeof(_prefs.node_name)); @@ -1017,7 +1017,7 @@ bool MyMesh::formatFileSystem() { return LittleFS.format(); #elif defined(ESP32) return SPIFFS.format(); -#elif defined(ARCH_PORTDUINO) +#elif defined(ARDULINUX_PLATFORM) return false; // not supported on Linux #else #error "need to implement file system erase" @@ -1170,7 +1170,7 @@ void MyMesh::formatPacketStatsReply(char *reply) { void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) IdentityStore store(*_fs, ""); -#elif defined(ESP32) || defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) +#elif defined(ESP32) || defined(RP2040_PLATFORM) || defined(ARDULINUX_PLATFORM) IdentityStore store(*_fs, "/identity"); #else #error "need to define saveIdentity()" diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index d643e0cced..6761f9a40d 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -11,8 +11,8 @@ #include #elif defined(ESP32) #include -#elif defined(ARCH_PORTDUINO) - #include +#elif defined(ARDULINUX_PLATFORM) + #include #endif #ifdef WITH_RS232_BRIDGE diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 38e1fa5b06..5d226658ae 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -69,13 +69,13 @@ void setup() { fs = &LittleFS; IdentityStore store(LittleFS, "/identity"); store.begin(); -#elif defined(ARCH_PORTDUINO) +#elif defined(ARDULINUX_PLATFORM) if (::mkdir(board.config.data_dir, 0755) != 0 && errno != EEXIST) { - Serial.printf("WARNING: could not create data_dir '%s': %s\n", board.config.data_dir, strerror(errno)); + printf("WARNING: could not create data_dir '%s': %s\n", board.config.data_dir, strerror(errno)); } - portduinoVFS->mountpoint(board.config.data_dir); - fs = &PortduinoFS; - IdentityStore store(PortduinoFS, "/identity"); + ardulinuxVFS->mountpoint(board.config.data_dir); + fs = &ArduLinuxFS; + IdentityStore store(ArduLinuxFS, "/identity"); store.begin(); #else #error "need to define filesystem" diff --git a/src/helpers/ClientACL.cpp b/src/helpers/ClientACL.cpp index 40a98a5b7a..f848ff432b 100644 --- a/src/helpers/ClientACL.cpp +++ b/src/helpers/ClientACL.cpp @@ -4,7 +4,7 @@ static File openWrite(FILESYSTEM* _fs, const char* filename) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove(filename); return _fs->open(filename, FILE_O_WRITE); - #elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) + #elif defined(RP2040_PLATFORM) || defined(ARDULINUX_PLATFORM) return _fs->open(filename, "w"); #else return _fs->open(filename, "w", true); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 3fd0d3c359..fb3a66cfbd 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -130,7 +130,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) fs->remove("/com_prefs"); File file = fs->open("/com_prefs", FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) +#elif defined(RP2040_PLATFORM) || defined(ARDULINUX_PLATFORM) File file = fs->open("/com_prefs", "w"); #else File file = fs->open("/com_prefs", "w", true); diff --git a/src/helpers/IdentityStore.cpp b/src/helpers/IdentityStore.cpp index 0a9700f0aa..3d29299098 100644 --- a/src/helpers/IdentityStore.cpp +++ b/src/helpers/IdentityStore.cpp @@ -49,7 +49,7 @@ bool IdentityStore::save(const char *name, const mesh::LocalIdentity& id) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove(filename); File file = _fs->open(filename, FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) +#elif defined(RP2040_PLATFORM) || defined(ARDULINUX_PLATFORM) File file = _fs->open(filename, "w"); #else File file = _fs->open(filename, "w", true); @@ -71,7 +71,7 @@ bool IdentityStore::save(const char *name, const mesh::LocalIdentity& id, const #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove(filename); File file = _fs->open(filename, FILE_O_WRITE); -#elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) +#elif defined(RP2040_PLATFORM) || defined(ARDULINUX_PLATFORM) File file = _fs->open(filename, "w"); #else File file = _fs->open(filename, "w", true); diff --git a/src/helpers/IdentityStore.h b/src/helpers/IdentityStore.h index a96aa536cc..4a1e22cccf 100644 --- a/src/helpers/IdentityStore.h +++ b/src/helpers/IdentityStore.h @@ -1,6 +1,6 @@ #pragma once -#if defined(ESP32) || defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) +#if defined(ESP32) || defined(RP2040_PLATFORM) || defined(ARDULINUX_PLATFORM) #include #define FILESYSTEM fs::FS #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp index ddc31287f8..5a699da4a2 100644 --- a/src/helpers/RegionMap.cpp +++ b/src/helpers/RegionMap.cpp @@ -62,7 +62,7 @@ static File openWrite(FILESYSTEM* _fs, const char* filename) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove(filename); return _fs->open(filename, FILE_O_WRITE); - #elif defined(RP2040_PLATFORM) || defined(ARCH_PORTDUINO) + #elif defined(RP2040_PLATFORM) || defined(ARDULINUX_PLATFORM) return _fs->open(filename, "w"); #else return _fs->open(filename, "w", true); diff --git a/src/helpers/TxtDataHelpers.cpp b/src/helpers/TxtDataHelpers.cpp index 60832f6653..6da0d741ab 100644 --- a/src/helpers/TxtDataHelpers.cpp +++ b/src/helpers/TxtDataHelpers.cpp @@ -1,5 +1,5 @@ #include "TxtDataHelpers.h" -#if defined(ARCH_PORTDUINO) +#if defined(ARDULINUX_PLATFORM) #include #endif @@ -105,7 +105,7 @@ static void _ftoa(float f, char *p, int *status) *p++ = '0'; else { -#if defined(ARCH_PORTDUINO) +#if defined(ARDULINUX_PLATFORM) sprintf(p, "%" PRId32, int_part); #else ltoa(int_part, p, 10); diff --git a/src/helpers/radiolib/LinuxSX1262Wrapper.h b/src/helpers/radiolib/LinuxSX1262Wrapper.h index fbbfd19c9b..9a75d37e69 100644 --- a/src/helpers/radiolib/LinuxSX1262Wrapper.h +++ b/src/helpers/radiolib/LinuxSX1262Wrapper.h @@ -6,6 +6,15 @@ class LinuxSX1262Wrapper : public RadioLibWrapper { public: LinuxSX1262Wrapper(LinuxSX1262& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } + + void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { + ((LinuxSX1262 *)_radio)->setFrequency(freq); + ((LinuxSX1262 *)_radio)->setSpreadingFactor(sf); + ((LinuxSX1262 *)_radio)->setBandwidth(bw); + ((LinuxSX1262 *)_radio)->setCodingRate(cr); + updatePreamble(sf); + } + bool isReceivingPacket() override { return ((LinuxSX1262 *)_radio)->isReceiving(); } @@ -19,4 +28,5 @@ class LinuxSX1262Wrapper : public RadioLibWrapper { int sf = ((LinuxSX1262 *)_radio)->spreadingFactor; return packetScoreInt(snr, sf, packet_len); } + uint8_t getSpreadingFactor() const override { return ((LinuxSX1262 *)_radio)->spreadingFactor; } }; diff --git a/variants/portduino/platformio.ini b/variants/ardulinux/platformio.ini similarity index 63% rename from variants/portduino/platformio.ini rename to variants/ardulinux/platformio.ini index 5785e965aa..549f34429d 100644 --- a/variants/portduino/platformio.ini +++ b/variants/ardulinux/platformio.ini @@ -1,7 +1,7 @@ -[portduino_base] +[ardulinux_base] platform = - # renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop - https://github.com/meshtastic/platform-native/archive/f566d364204416cdbf298e349213f7d551f793d9.zip + # renovate: datasource=git-refs depName=ardulinux packageName=https://github.com/l5yth/ardulinux gitBranch=main + git+https://github.com/l5yth/ardulinux.git framework = arduino build_src_filter = @@ -26,16 +26,12 @@ lib_deps = build_flags = ${arduino_base.build_flags} - -DARCH_PORTDUINO - -DPORTDUINO_PLATFORM + -DARDULINUX_PLATFORM -DRADIOLIB_EEPROM_UNSUPPORTED - -DPORTDUINO_LINUX_HARDWARE -fPIC -lpthread -lstdc++fs -lbluetooth - -lgpiod - -li2c -luv -std=gnu17 -std=c++17 diff --git a/variants/linux/LinuxBoard.cpp b/variants/linux/LinuxBoard.cpp index 4c25da0a11..40c33acf37 100644 --- a/variants/linux/LinuxBoard.cpp +++ b/variants/linux/LinuxBoard.cpp @@ -2,12 +2,19 @@ #include #include #include +#ifdef ARDULINUX_HARDWARE #include "linux/gpio/LinuxGPIOPin.h" +#endif #include "LinuxBoard.h" +#include "AppInfo.h" + +const char *ardulinuxAppName = "meshcored"; +const char *ardulinuxAppDescription = "a meshcore daemon for linux"; +const char *ardulinuxAppBugAddress = "https://github.com/meshcore-dev/MeshCore"; int initGPIOPin(uint8_t pinNum, const std::string gpioChipName, uint8_t line) { -#ifdef PORTDUINO_LINUX_HARDWARE +#ifdef ARDULINUX_HARDWARE char gpio_name[32]; snprintf(gpio_name, sizeof(gpio_name), "GPIO%d", pinNum); @@ -26,22 +33,22 @@ int initGPIOPin(uint8_t pinNum, const std::string gpioChipName, uint8_t line) #endif } -void portduinoSetup() { +void ardulinuxSetup() { } void LinuxBoard::begin() { config.load("/etc/meshcored/meshcored.ini"); - Serial.printf("SPI begin %s\n", config.spidev); - SPI.begin(config.spidev); + printf("SPI begin %s\n", config.spidev); + SPI.begin(config.spidev, 2000000); - Serial.printf("LoRa pins NSS=%d BUSY=%d IRQ=%d RESET=%d TX=%d RX=%d\n", - (int)config.lora_nss_pin, - (int)config.lora_busy_pin, - (int)config.lora_irq_pin, - (int)config.lora_reset_pin, - (int)config.lora_rxen_pin, - (int)config.lora_txen_pin); + printf("LoRa pins NSS=%d BUSY=%d IRQ=%d RESET=%d TX=%d RX=%d\n", + (int)config.lora_nss_pin, + (int)config.lora_busy_pin, + (int)config.lora_irq_pin, + (int)config.lora_reset_pin, + (int)config.lora_rxen_pin, + (int)config.lora_txen_pin); if (config.lora_nss_pin != RADIOLIB_NC) { initGPIOPin(config.lora_nss_pin, "gpiochip0", config.lora_nss_pin); diff --git a/variants/linux/README.md b/variants/linux/README.md index 276fd8e6c7..a597ddf2de 100644 --- a/variants/linux/README.md +++ b/variants/linux/README.md @@ -1,6 +1,6 @@ # MeshCore Linux Variant -Native Linux support for MeshCore, targeting Raspberry Pi (Zero, 3, 4, 5) and similar SBCs with an SX1262 LoRa radio attached over SPI. Uses the [Portduino](https://github.com/meshtastic/platform-native) Arduino-compatibility layer to run the same firmware codebase on Linux without modification to the core library. +Native Linux support for MeshCore, targeting Raspberry Pi (Zero, 3, 4, 5) and similar SBCs with an SX1262 LoRa radio attached over SPI. Uses [ArduLinux — Arduino API for Linux](https://github.com/l5yth/ardulinux) to run the same firmware codebase on Linux without modification to the core library. ## Hardware @@ -14,17 +14,33 @@ Native Linux support for MeshCore, targeting Raspberry Pi (Zero, 3, 4, 5) and si ```sh # Arch Linux -sudo pacman -S libgpiod i2c-tools +sudo pacman -S libgpiod i2c-tools bluez-libs libuv # Debian/Raspberry Pi OS -sudo apt install libgpiod-dev libi2c-dev +sudo apt install libgpiod-dev libi2c-dev libbluetooth-dev libuv1-dev +``` + +The ArduLinux platform always links `bluetooth`, `uv`, `pthread`, and +`stdc++fs`; `gpiod`/`i2c` are added automatically when libgpiod is detected via +`pkg-config` (without it the build falls back to simulated GPIO/I2C). Missing +`bluez-libs`/`libbluetooth-dev` shows up as a `cannot find -lbluetooth` link +error. + +You also need **PlatformIO Core** (`pio`) to build: + +```sh +# Arch Linux +sudo pacman -S platformio # or: pipx install platformio + +# Debian/Raspberry Pi OS +pipx install platformio # or: pip install --user platformio ``` **Build with `build.sh`** (recommended — embeds version and commit hash): ```sh FIRMWARE_VERSION=dev ./build.sh build-firmware linux_repeater -# binary: .pio/build/linux_repeater/program +# binary: .pio/build/linux_repeater/meshcored ``` Alternatively, build directly with PlatformIO (no version metadata): @@ -38,7 +54,7 @@ FIRMWARE_VERSION=dev pio run -e linux_repeater ### 1. Install the binary ```sh -sudo cp .pio/build/linux_repeater/program /usr/bin/meshcored +sudo install -m 755 .pio/build/linux_repeater/meshcored /usr/bin/meshcored ``` ### 2. Create the config file @@ -51,9 +67,8 @@ Two ready-made templates are provided in `variants/linux/`: | `meshcored.ini.waveshare` | RPi 3/4/5 + Waveshare SX1262 LoRa HAT | ```sh -sudo mkdir -p /etc/meshcored -# Pick the template that matches your hardware: -sudo cp variants/linux/meshcored.ini.waveshare /etc/meshcored/meshcored.ini +# Pick the template that matches your hardware (install -D creates /etc/meshcored): +sudo install -D -m 644 variants/linux/meshcored.ini.waveshare /etc/meshcored/meshcored.ini sudo nano /etc/meshcored/meshcored.ini ``` @@ -71,12 +86,17 @@ Key settings: | `lora_reset_pin` | (none) | GPIO line number for RESET | | `lora_nss_pin` | (none) | GPIO line number for NSS/CS (if not handled by the SPI driver) | | `lora_busy_pin` | (none) | GPIO line number for BUSY | +| `lora_rxen_pin` | (none) | GPIO line number for RX enable (RF switch); omit if unused | +| `lora_txen_pin` | (none) | GPIO line number for TX enable (RF switch); omit if unused | | `lora_freq` | `869.618` | Frequency in MHz | | `lora_bw` | `62.5` | Bandwidth in kHz | | `lora_sf` | `8` | Spreading factor | | `lora_cr` | `8` | Coding rate | | `lora_tcxo` | `1.8` | TCXO voltage (V); set to `0.0` if your module has no TCXO | | `lora_tx_power` | `22` | TX power in dBm | +| `current_limit` | `140` | Radio over-current protection limit in mA | +| `dio2_as_rf_switch` | `0` | Set to `1` to use DIO2 as the RF switch control (depends on module wiring) | +| `rx_boosted_gain` | `1` | `1` enables the SX126x RX boosted-gain mode; `0` disables | | `advert_name` | `"Linux Repeater"` | Node name — first-run default only | | `admin_password` | `"password"` | Admin password — **change this**, first-run default only | | `lat` / `lon` | `0.0` | GPS coordinates for advertisement — first-run default only | @@ -84,16 +104,32 @@ Key settings: ### 3. Enable SPI and GPIO access +First make sure the SPI interface is actually enabled — the radio needs a +`/dev/spidev*` node. Check with `ls /dev/spidev*`; if there is none: + ```sh # Raspberry Pi OS -sudo raspi-config # Interface Options → SPI → Enable +sudo raspi-config # Interface Options → SPI → Enable, then reboot + +# Arch Linux ARM (no raspi-config): enable the SPI device-tree overlay +echo 'dtparam=spi=on' | sudo tee -a /boot/config.txt # then reboot +``` + +> The boot config path varies by image — it is `/boot/config.txt` on most +> Raspberry Pi images but `/boot/firmware/config.txt` on some. After rebooting, +> confirm `/dev/spidev0.0` exists. + +Then grant access to the SPI and GPIO devices. On Raspberry Pi OS you can use the +`spi`/`gpio` groups: + +```sh sudo usermod -aG spi,gpio $USER ``` -On Arch Linux and other distributions without `spi`/`gpio` groups, use the provided udev rules instead (also works on Raspberry Pi OS): +On Arch Linux and other distributions without `spi`/`gpio` groups, use the provided udev rules instead (also works on Raspberry Pi OS); these grant access to the `meshcore` group used by the systemd service: ```sh -sudo cp variants/linux/99-meshcore.rules /etc/udev/rules.d/ +sudo install -m 644 variants/linux/99-meshcore.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules && sudo udevadm trigger ``` @@ -110,8 +146,8 @@ sudo /usr/bin/meshcored **As a systemd service** (recommended for production): ```sh -sudo cp variants/linux/meshcored.service /etc/systemd/system/ -sudo cp variants/linux/99-meshcore.rules /etc/udev/rules.d/ +sudo install -m 644 variants/linux/meshcored.service /etc/systemd/system/ +sudo install -m 644 variants/linux/99-meshcore.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules && sudo udevadm trigger sudo useradd -r -s /sbin/nologin meshcore sudo mkdir -p /var/lib/meshcore @@ -145,8 +181,8 @@ sudo systemctl restart meshcored ## Known Gaps / TODO -- **No CLI argument parsing** — config path is hardcoded to `/etc/meshcored/meshcored.ini`; `data_dir` is only configurable via the INI file. +- **Config path is hardcoded** — meshcored always loads `/etc/meshcored/meshcored.ini`; there is no flag to point it elsewhere. (The ArduLinux runtime itself accepts some flags such as `--fsdir`; how that interacts with the INI's `data_dir` is not yet verified on hardware — TODO.) - **Only repeater firmware** — there is no `linux_companion` target yet; companion radio support (BLE/serial interface to a phone app) is not implemented for Linux. - **`formatFileSystem()`** returns `false` (not implemented) — the CLI `format` command will report failure on Linux. - **No power management** — `board.sleep()` is a no-op; the power-saving loop in `main.cpp` never actually sleeps. -- **Portduino branding** — on startup the binary identifies itself as "An application written with portduino" with a Meshtastic bug URL. This is hardcoded in the Portduino framework and cannot be changed without patching the framework. +- **Upstream-sync fragility** — the radio wrapper (`LinuxSX1262Wrapper`) implements the `RadioLibWrapper` interface by hand, so an upstream change that adds a pure-virtual method (e.g. `setParams()`) breaks the Linux build until the override is added. Mirror `CustomSX1262Wrapper` when this happens. diff --git a/variants/linux/platformio.ini b/variants/linux/platformio.ini index 3fc49b2cfd..c8634b5e4b 100644 --- a/variants/linux/platformio.ini +++ b/variants/linux/platformio.ini @@ -1,15 +1,16 @@ [linux_base] -extends = portduino_base -build_flags = ${portduino_base.build_flags} +extends = ardulinux_base +build_flags = ${ardulinux_base.build_flags} -I variants/linux -I /usr/include -board = cross_platform +board = linux board_level = extra +board_build.progname = meshcored lib_deps = - ${portduino_base.lib_deps} + ${ardulinux_base.lib_deps} melopero/Melopero RV3028@^1.1.0 -build_src_filter = ${portduino_base.build_src_filter} +build_src_filter = ${ardulinux_base.build_src_filter} +<../variants/linux> - - diff --git a/variants/linux/target.cpp b/variants/linux/target.cpp index 0f5941f890..a69218e2ee 100644 --- a/variants/linux/target.cpp +++ b/variants/linux/target.cpp @@ -1,20 +1,25 @@ #include #include "target.h" -class PortduinoHal : public ArduinoHal +class ArduLinuxHal : public ArduinoHal { public: - PortduinoHal(SPIClass &spi, SPISettings spiSettings) : ArduinoHal(spi, spiSettings){}; + ArduLinuxHal(SPIClass &spi, SPISettings spiSettings) : ArduinoHal(spi, spiSettings){}; + // ArduLinux's SPIClass exposes only an in-place transfer(buf, len) that + // overwrites buf with the received bytes. RadioLib's HAL expects a + // full-duplex transfer(out, len, in), so copy out -> in first, then run the + // in-place exchange (out and in are distinct, non-overlapping buffers). void spiTransfer(uint8_t *out, size_t len, uint8_t *in) { - spi->transfer(out, in, len); + memcpy(in, out, len); + spi->transfer(in, len); } }; LinuxBoard board; SPISettings spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0); -ArduinoHal *hal = new PortduinoHal(SPI, spiSettings); +ArduinoHal *hal = new ArduLinuxHal(SPI, spiSettings); RADIO_CLASS radio = new Module(hal, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC); WRAPPER_CLASS radio_driver(radio, board); From 9bdfa145e933d310ea7a5ac62b881132aefbbeb7 Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Mon, 1 Jun 2026 08:19:54 +0200 Subject: [PATCH 06/14] pin ardulinux to 0.2.0 (#7) --- variants/ardulinux/platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variants/ardulinux/platformio.ini b/variants/ardulinux/platformio.ini index 549f34429d..b11f85c015 100644 --- a/variants/ardulinux/platformio.ini +++ b/variants/ardulinux/platformio.ini @@ -1,7 +1,7 @@ [ardulinux_base] platform = - # renovate: datasource=git-refs depName=ardulinux packageName=https://github.com/l5yth/ardulinux gitBranch=main - git+https://github.com/l5yth/ardulinux.git + # renovate: datasource=git-tags depName=ardulinux packageName=https://github.com/l5yth/ardulinux + git+https://github.com/l5yth/ardulinux.git#v0.2.0 framework = arduino build_src_filter = From 360ada1fdabf69e7930bc8343d5a6f140475aede Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:24:01 +0200 Subject: [PATCH 07/14] =?UTF-8?q?variants/linux:=20on-hardware=20smoke-tes?= =?UTF-8?q?t=20fixes=20(rx=5Fboost,=20dio2,=20--fsdir=E2=80=A6=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * variants/linux: on-hardware smoke-test fixes (rx_boost, dio2, --fsdir, docs) * address review comments --- examples/simple_repeater/MyMesh.cpp | 1 + examples/simple_repeater/main.cpp | 7 ++- src/helpers/radiolib/LinuxSX1262.h | 6 +++ src/helpers/radiolib/LinuxSX1262Wrapper.h | 7 +++ variants/linux/LinuxBoard.cpp | 1 - variants/linux/LinuxBoard.h | 1 - variants/linux/README.md | 59 +++++++++++++++-------- variants/linux/meshcored.ini | 3 +- variants/linux/meshcored.ini.pow-sx1262 | 4 +- variants/linux/meshcored.ini.waveshare | 7 ++- 10 files changed, 66 insertions(+), 30 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 322143eb5e..86a4100f1b 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -939,6 +939,7 @@ void MyMesh::begin(FILESYSTEM *fs) { _prefs.sf = board.config.lora_sf; _prefs.cr = board.config.lora_cr; _prefs.tx_power_dbm = board.config.lora_tx_power; + _prefs.rx_boosted_gain = board.config.rx_boosted_gain; #endif // load persisted prefs _cli.loadPrefs(_fs); diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 5d226658ae..cd1d03dbb9 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -70,10 +70,9 @@ void setup() { IdentityStore store(LittleFS, "/identity"); store.begin(); #elif defined(ARDULINUX_PLATFORM) - if (::mkdir(board.config.data_dir, 0755) != 0 && errno != EEXIST) { - printf("WARNING: could not create data_dir '%s': %s\n", board.config.data_dir, strerror(errno)); - } - ardulinuxVFS->mountpoint(board.config.data_dir); + // The VFS root is established by the ArduLinux core from --fsdir (default: the + // XDG data dir, e.g. ~/.local/share/meshcored/default), so there is no per-app + // mountpoint override here — --fsdir is the single source of truth for the data path. fs = &ArduLinuxFS; IdentityStore store(ArduLinuxFS, "/identity"); store.begin(); diff --git a/src/helpers/radiolib/LinuxSX1262.h b/src/helpers/radiolib/LinuxSX1262.h index 8d5977e8b0..bed50ee92e 100644 --- a/src/helpers/radiolib/LinuxSX1262.h +++ b/src/helpers/radiolib/LinuxSX1262.h @@ -45,4 +45,10 @@ class LinuxSX1262 : public SX1262 { bool detected = (irq & SX126X_IRQ_HEADER_VALID) || (irq & SX126X_IRQ_PREAMBLE_DETECTED); return detected; } + + bool getRxBoostedGainMode() { + uint8_t rxGain = 0; + readRegister(RADIOLIB_SX126X_REG_RX_GAIN, &rxGain, 1); + return (rxGain == RADIOLIB_SX126X_RX_GAIN_BOOSTED); + } }; diff --git a/src/helpers/radiolib/LinuxSX1262Wrapper.h b/src/helpers/radiolib/LinuxSX1262Wrapper.h index 9a75d37e69..17e168c70f 100644 --- a/src/helpers/radiolib/LinuxSX1262Wrapper.h +++ b/src/helpers/radiolib/LinuxSX1262Wrapper.h @@ -29,4 +29,11 @@ class LinuxSX1262Wrapper : public RadioLibWrapper { return packetScoreInt(snr, sf, packet_len); } uint8_t getSpreadingFactor() const override { return ((LinuxSX1262 *)_radio)->spreadingFactor; } + + void setRxBoostedGainMode(bool en) override { + ((LinuxSX1262 *)_radio)->setRxBoostedGainMode(en); + } + bool getRxBoostedGainMode() const override { + return ((LinuxSX1262 *)_radio)->getRxBoostedGainMode(); + } }; diff --git a/variants/linux/LinuxBoard.cpp b/variants/linux/LinuxBoard.cpp index 40c33acf37..9e9c79de48 100644 --- a/variants/linux/LinuxBoard.cpp +++ b/variants/linux/LinuxBoard.cpp @@ -147,7 +147,6 @@ int LinuxConfig::load(const char *filename) { else if (strcmp(key, "admin_password") == 0) admin_password = safe_copy(value, 100); else if (strcmp(key, "lat") == 0) lat = atof(value); else if (strcmp(key, "lon") == 0) lon = atof(value); - else if (strcmp(key, "data_dir") == 0) data_dir = safe_copy(value, 256); } fclose(f); return 0; diff --git a/variants/linux/LinuxBoard.h b/variants/linux/LinuxBoard.h index 584ef195f6..0a18f85c16 100644 --- a/variants/linux/LinuxBoard.h +++ b/variants/linux/LinuxBoard.h @@ -38,7 +38,6 @@ class LinuxConfig { char *admin_password = "password"; float lat = 0.0f; float lon = 0.0f; - char *data_dir = "/var/lib/meshcore"; int load(const char *filename); }; diff --git a/variants/linux/README.md b/variants/linux/README.md index a597ddf2de..ac8f1b36b8 100644 --- a/variants/linux/README.md +++ b/variants/linux/README.md @@ -75,7 +75,7 @@ sudo nano /etc/meshcored/meshcored.ini The config file has two roles: - **Hardware config** (always read on every startup): SPI device, GPIO pin numbers, LoRa radio parameters. -- **First-run node defaults**: `advert_name`, `admin_password`, `lat`, `lon`. On the first boot these are saved to `data_dir`. After that, use the serial CLI to change them (`set name`, `set password`, etc.) — the INI values are no longer consulted for these fields. +- **First-run node defaults**: `advert_name`, `admin_password`, `lat`, `lon`. On the first boot these are saved to the node's persisted prefs (`com_prefs`). After that, use the serial CLI to change them (`set name`, `set password`, etc.) — the INI values are no longer consulted for these fields. Key settings: @@ -95,12 +95,11 @@ Key settings: | `lora_tcxo` | `1.8` | TCXO voltage (V); set to `0.0` if your module has no TCXO | | `lora_tx_power` | `22` | TX power in dBm | | `current_limit` | `140` | Radio over-current protection limit in mA | -| `dio2_as_rf_switch` | `0` | Set to `1` to use DIO2 as the RF switch control (depends on module wiring) | +| `dio2_as_rf_switch` | `0` | `1` = use DIO2 to drive the TX/RX RF switch. **Required for the Waveshare Core1262** (without it the radio inits but TX/RX are dead); depends on module wiring | | `rx_boosted_gain` | `1` | `1` enables the SX126x RX boosted-gain mode; `0` disables | | `advert_name` | `"Linux Repeater"` | Node name — first-run default only | | `admin_password` | `"password"` | Admin password — **change this**, first-run default only | | `lat` / `lon` | `0.0` | GPS coordinates for advertisement — first-run default only | -| `data_dir` | `/var/lib/meshcore` | Where identity and node prefs are persisted | ### 3. Enable SPI and GPIO access @@ -118,40 +117,57 @@ echo 'dtparam=spi=on' | sudo tee -a /boot/config.txt # then reboot > The boot config path varies by image — it is `/boot/config.txt` on most > Raspberry Pi images but `/boot/firmware/config.txt` on some. After rebooting, > confirm `/dev/spidev0.0` exists. - -Then grant access to the SPI and GPIO devices. On Raspberry Pi OS you can use the -`spi`/`gpio` groups: +> +> **Arch Linux kernel caveat:** `dtparam=spi=on` is only honored by the Raspberry +> Pi `linux-rpi` (vendor) kernel. The mainline `linux-aarch64` kernel boots via +> U-Boot, which loads its own device tree and ignores `config.txt` overlays — so +> `/dev/spidev*` never appears regardless of `config.txt`. If SPI is missing after +> enabling it and rebooting, switch to the vendor kernel +> (`sudo pacman -S linux-rpi`, remove `linux-aarch64`) and reboot. + +Then grant non-root access to the SPI and GPIO devices using the provided udev +rules, which place `/dev/spidev*` and `/dev/gpiochip*` in a `meshcore` group. +Create the group, add yourself to it, and install the rules: ```sh -sudo usermod -aG spi,gpio $USER +sudo groupadd -f -r meshcore +sudo usermod -aG meshcore "$USER" # log out/in afterwards for this to take effect +sudo install -m 644 variants/linux/99-meshcore.rules /etc/udev/rules.d/ +sudo udevadm control --reload-rules && sudo udevadm trigger ``` -On Arch Linux and other distributions without `spi`/`gpio` groups, use the provided udev rules instead (also works on Raspberry Pi OS); these grant access to the `meshcore` group used by the systemd service: +Confirm the device nodes are now group-owned by `meshcore`: ```sh -sudo install -m 644 variants/linux/99-meshcore.rules /etc/udev/rules.d/ -sudo udevadm control --reload-rules && sudo udevadm trigger +ls -l /dev/gpiochip* /dev/spidev* # → crw-rw---- root meshcore ``` +Your current login session won't pick up the new group until you log out and +back in. To use it immediately in one shell, prefix the command with +`sg meshcore -c '…'`. (On Raspberry Pi OS you can instead use the built-in +`spi`/`gpio` groups: `sudo usermod -aG spi,gpio $USER`.) + ### 4. Run -**Directly** (for testing): +**Directly** (for testing). With the udev rules in place you can run as your own +user — no `sudo`. Data is persisted under the VFS root, which defaults to the XDG +data dir; pass `--fsdir` to choose another location: ```sh -sudo /usr/bin/meshcored +meshcored # VFS root: ~/.local/share/meshcored/default +meshcored --fsdir /var/lib/meshcore # explicit location +# before re-logging in (group not yet active in this shell): +sg meshcore -c 'meshcored --fsdir /var/lib/meshcore' ``` -`sudo` is needed on first run to create `data_dir` if it doesn't exist. Once the directory is created and owned appropriately, it can run as a non-root user. - -**As a systemd service** (recommended for production): +**As a systemd service** (recommended for production). The unit runs as the +`meshcore` user and passes `--fsdir /var/lib/meshcore`: ```sh sudo install -m 644 variants/linux/meshcored.service /etc/systemd/system/ sudo install -m 644 variants/linux/99-meshcore.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules && sudo udevadm trigger sudo useradd -r -s /sbin/nologin meshcore -sudo mkdir -p /var/lib/meshcore -sudo chown meshcore:meshcore /var/lib/meshcore sudo chmod 640 /etc/meshcored/meshcored.ini sudo chown root:meshcore /etc/meshcored/meshcored.ini sudo systemctl daemon-reload @@ -159,6 +175,11 @@ sudo systemctl enable --now meshcored sudo journalctl -u meshcored -f ``` +> The unit's `ExecStartPre` creates and chowns `/var/lib/meshcore`, so you don't +> need to pre-create it. If you smoke-tested by running directly first, clear any +> stale state so the service first-boots with the INI defaults: +> `sudo rm -rf /var/lib/meshcore/*` + ### 5. Reconfiguring after first run Node name, password, and location can be changed via the serial CLI after first boot: @@ -181,8 +202,8 @@ sudo systemctl restart meshcored ## Known Gaps / TODO -- **Config path is hardcoded** — meshcored always loads `/etc/meshcored/meshcored.ini`; there is no flag to point it elsewhere. (The ArduLinux runtime itself accepts some flags such as `--fsdir`; how that interacts with the INI's `data_dir` is not yet verified on hardware — TODO.) +- **Config path is hardcoded** — meshcored always loads `/etc/meshcored/meshcored.ini`; there is no flag to point it elsewhere. (The data *path* is separate and configurable: it is the ArduLinux VFS root, set with `--fsdir`.) - **Only repeater firmware** — there is no `linux_companion` target yet; companion radio support (BLE/serial interface to a phone app) is not implemented for Linux. - **`formatFileSystem()`** returns `false` (not implemented) — the CLI `format` command will report failure on Linux. - **No power management** — `board.sleep()` is a no-op; the power-saving loop in `main.cpp` never actually sleeps. -- **Upstream-sync fragility** — the radio wrapper (`LinuxSX1262Wrapper`) implements the `RadioLibWrapper` interface by hand, so an upstream change that adds a pure-virtual method (e.g. `setParams()`) breaks the Linux build until the override is added. Mirror `CustomSX1262Wrapper` when this happens. +- **Upstream-sync fragility** — the radio wrapper (`LinuxSX1262Wrapper`) implements the `RadioLibWrapper` interface by hand, so it can drift from upstream in two ways: a new **pure-virtual** method breaks the Linux build (e.g. `setParams()`), and a new **virtual-with-default** method silently no-ops on Linux until overridden (e.g. `set`/`getRxBoostedGainMode()`, which reported and applied the wrong state until added). Mirror `CustomSX1262Wrapper` when syncing. diff --git a/variants/linux/meshcored.ini b/variants/linux/meshcored.ini index 2c57adbbb9..794ffc490b 100644 --- a/variants/linux/meshcored.ini +++ b/variants/linux/meshcored.ini @@ -2,7 +2,8 @@ advert_name = Sample Router admin_password = password lat = 0.0 lon = 0.0 -#data_dir = /var/lib/meshcore +# Data path: set via the --fsdir CLI flag (the systemd unit uses +# /var/lib/meshcore), not in this file. # Waveshare LoRa hat #lora_irq_pin = 16 diff --git a/variants/linux/meshcored.ini.pow-sx1262 b/variants/linux/meshcored.ini.pow-sx1262 index 1f2e03000f..024c5d77cf 100644 --- a/variants/linux/meshcored.ini.pow-sx1262 +++ b/variants/linux/meshcored.ini.pow-sx1262 @@ -20,5 +20,5 @@ admin_password = changeme lat = 0.0 lon = 0.0 -# data_dir — where identity and node prefs are persisted (default shown): -#data_dir = /var/lib/meshcore +# Data is persisted under the VFS root set by the --fsdir CLI flag (default +# ~/.local/share/meshcored/default; the systemd unit uses /var/lib/meshcore). diff --git a/variants/linux/meshcored.ini.waveshare b/variants/linux/meshcored.ini.waveshare index 84df7f3acd..5aeb28e1d7 100644 --- a/variants/linux/meshcored.ini.waveshare +++ b/variants/linux/meshcored.ini.waveshare @@ -13,6 +13,9 @@ lora_sf = 8 lora_cr = 8 lora_tcxo = 1.8 lora_tx_power = 22 +# DIO2 drives the TX/RX RF switch on the Waveshare Core1262 — required for this HAT +# (without it the radio inits fine but TX/RX stay dead). 1 = enabled, 0 = disabled. +dio2_as_rf_switch = 1 # First-run node defaults (ignored after first boot; use CLI to change): advert_name = Waveshare Linux Repeater @@ -20,5 +23,5 @@ admin_password = changeme lat = 0.0 lon = 0.0 -# data_dir — where identity and node prefs are persisted (default shown): -#data_dir = /var/lib/meshcore +# Data is persisted under the VFS root set by the --fsdir CLI flag (default +# ~/.local/share/meshcored/default; the systemd unit uses /var/lib/meshcore). From 5cd07aebad51d85563dbc10327eecb13c2a07918 Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Fri, 5 Jun 2026 00:32:10 +0200 Subject: [PATCH 08/14] proofread README (#9) * proofread README * proofread README --- variants/linux/README.md | 37 +++++++++++++++++++++++++------- variants/linux/meshcored.service | 11 +++++----- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/variants/linux/README.md b/variants/linux/README.md index ac8f1b36b8..c949b1708b 100644 --- a/variants/linux/README.md +++ b/variants/linux/README.md @@ -30,7 +30,7 @@ You also need **PlatformIO Core** (`pio`) to build: ```sh # Arch Linux -sudo pacman -S platformio # or: pipx install platformio +sudo pacman -S platformio-core # or: pipx install platformio # Debian/Raspberry Pi OS pipx install platformio # or: pip install --user platformio @@ -149,6 +149,15 @@ back in. To use it immediately in one shell, prefix the command with ### 4. Run +`meshcored` takes a small set of options, parsed by the ArduLinux core: + +| Flag | Description | +|------|-------------| +| `-d`, `--fsdir=DIR` | Directory to use as the VFS root, where all data is persisted. Default: `~/.local/share/meshcored/default` | +| `-e`, `--erase` | Recursively wipe the VFS root, then start. This is a **full reset**: it also removes the node identity, so the node comes back with a new Repeater ID (see step 5). Never put this in the systemd unit. | +| `--usage`, `-?` / `--help` | Short usage / full option list | +| `-V`, `--version` | Print the firmware version | + **Directly** (for testing). With the udev rules in place you can run as your own user — no `sudo`. Data is persisted under the VFS root, which defaults to the XDG data dir; pass `--fsdir` to choose another location: @@ -167,7 +176,7 @@ sg meshcore -c 'meshcored --fsdir /var/lib/meshcore' sudo install -m 644 variants/linux/meshcored.service /etc/systemd/system/ sudo install -m 644 variants/linux/99-meshcore.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules && sudo udevadm trigger -sudo useradd -r -s /sbin/nologin meshcore +sudo useradd -r -g meshcore -s /sbin/nologin meshcore # -g: reuse the existing meshcore group (its udev rules grant device access) sudo chmod 640 /etc/meshcored/meshcored.ini sudo chown root:meshcore /etc/meshcored/meshcored.ini sudo systemctl daemon-reload @@ -175,10 +184,10 @@ sudo systemctl enable --now meshcored sudo journalctl -u meshcored -f ``` -> The unit's `ExecStartPre` creates and chowns `/var/lib/meshcore`, so you don't -> need to pre-create it. If you smoke-tested by running directly first, clear any -> stale state so the service first-boots with the INI defaults: -> `sudo rm -rf /var/lib/meshcore/*` +> The unit's `StateDirectory=meshcore` makes systemd create `/var/lib/meshcore` +> owned by `meshcore:meshcore` before startup, so you don't need to pre-create it. +> If you smoke-tested by running directly first, clear any stale state so the +> service first-boots with the INI defaults: `sudo rm -rf /var/lib/meshcore/*` ### 5. Reconfiguring after first run @@ -191,19 +200,31 @@ set lat set lon ``` -To reset all node prefs and re-apply the INI file defaults, delete the saved prefs and restart: +There are two levels of reset: + +**Prefs only** — keeps the node identity (same Repeater ID). Delete the saved prefs so the INI first-run defaults are re-applied on the next boot: ```sh sudo rm /var/lib/meshcore/com_prefs sudo systemctl restart meshcored ``` +**Full reset** — also discards the identity, so the node returns with a **new** Repeater ID. This wipes the whole VFS root. The built-in `-e`/`--erase` flag does exactly that before starting, but for the managed service just clear the directory while it is stopped (keep `--erase` out of the unit — see the note below): + +```sh +sudo systemctl stop meshcored +sudo rm -rf /var/lib/meshcore/* +sudo systemctl start meshcored +``` + +> When running **directly** (not under systemd), `meshcored --fsdir /var/lib/meshcore --erase` is the equivalent one-shot full reset. Do **not** add `--erase` to the service unit: systemd re-runs `ExecStart` on every restart, so it would wipe the filesystem and regenerate the identity each time. (The firmware's own `reboot()` strips `--erase` to avoid self-wiping, but that protection does not extend to a systemd restart.) + > **Note:** LoRa radio parameters (`lora_freq`, `lora_bw`, `lora_sf`, `lora_cr`, `lora_tx_power`) are also first-run defaults. After first boot they are saved in `com_prefs` and the INI values are no longer read for those fields. To apply a changed radio parameter, use the CLI (`set freq`, `set sf`, etc.) or reset prefs as above. ## Known Gaps / TODO - **Config path is hardcoded** — meshcored always loads `/etc/meshcored/meshcored.ini`; there is no flag to point it elsewhere. (The data *path* is separate and configurable: it is the ArduLinux VFS root, set with `--fsdir`.) - **Only repeater firmware** — there is no `linux_companion` target yet; companion radio support (BLE/serial interface to a phone app) is not implemented for Linux. -- **`formatFileSystem()`** returns `false` (not implemented) — the CLI `format` command will report failure on Linux. +- **Serial `erase` command is a no-op** — `formatFileSystem()` returns `false` on Linux, so the interactive serial `erase` command reports failure. To wipe the filesystem, use the `--erase` *startup* flag (or clear the VFS dir) instead — see step 5. - **No power management** — `board.sleep()` is a no-op; the power-saving loop in `main.cpp` never actually sleeps. - **Upstream-sync fragility** — the radio wrapper (`LinuxSX1262Wrapper`) implements the `RadioLibWrapper` interface by hand, so it can drift from upstream in two ways: a new **pure-virtual** method breaks the Linux build (e.g. `setParams()`), and a new **virtual-with-default** method silently no-ops on Linux until overridden (e.g. `set`/`getRxBoostedGainMode()`, which reported and applied the wrong state until added). Mirror `CustomSX1262Wrapper` when syncing. diff --git a/variants/linux/meshcored.service b/variants/linux/meshcored.service index b68ff2a62c..d95be0ba4c 100644 --- a/variants/linux/meshcored.service +++ b/variants/linux/meshcored.service @@ -19,12 +19,13 @@ ProtectSystem=strict ProtectHome=yes PrivateTmp=yes NoNewPrivileges=yes -# Allow writing only to its own data dir -ReadWritePaths=/var/lib/meshcore -# Create data dir with correct ownership if it doesn't exist -ExecStartPre=/bin/mkdir -p /var/lib/meshcore -ExecStartPre=/bin/chown meshcore:meshcore /var/lib/meshcore +# systemd creates /var/lib/meshcore owned by User:Group before the namespace is +# set up, and makes it the unit's only writable path. This must come from +# StateDirectory rather than ExecStartPre+ReadWritePaths: with ProtectSystem=strict +# the read-write path has to exist when the mount namespace is built, which happens +# before ExecStartPre runs (a mkdir there fails with status=226/NAMESPACE). +StateDirectory=meshcore [Install] WantedBy=multi-user.target From 474d45e3671357d0adcdcf009efddde089e22d3b Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Fri, 5 Jun 2026 22:39:07 +0200 Subject: [PATCH 09/14] variants/linux: use linux_base for platformio (#10) * variants/linux: use linux_base for platformio * variants/linux: use linux_base for platformio --- examples/simple_repeater/MyMesh.cpp | 7 ++-- examples/simple_repeater/main.cpp | 5 +-- platformio.ini | 49 +++++++++++++++++++++++++ variants/ardulinux/platformio.ini | 37 ------------------- variants/linux/99-meshcore.rules | 5 +-- variants/linux/README.md | 34 ++++++++--------- variants/linux/meshcored.ini | 2 - variants/linux/meshcored.ini.pow-sx1262 | 10 ++--- variants/linux/meshcored.ini.waveshare | 9 ++--- variants/linux/meshcored.service | 8 ---- variants/linux/platformio.ini | 22 ----------- variants/linux/target.cpp | 5 --- 12 files changed, 81 insertions(+), 112 deletions(-) delete mode 100644 variants/ardulinux/platformio.ini diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 86a4100f1b..34cec242e9 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -928,8 +928,8 @@ void MyMesh::begin(FILESYSTEM *fs) { mesh::Mesh::begin(); _fs = fs; #if defined(ARDULINUX_PLATFORM) - // Apply runtime INI config as first-run defaults before loading persisted prefs. - // If /com_prefs exists, loadPrefs() below will overwrite these with the saved values. + // apply runtime INI config as first-run defaults before loading persisted prefs + // if /com_prefs exists, loadPrefs() below will overwrite these with the saved values StrHelper::strncpy(_prefs.node_name, board.config.advert_name, sizeof(_prefs.node_name)); _prefs.node_lat = board.config.lat; _prefs.node_lon = board.config.lon; @@ -1019,7 +1019,8 @@ bool MyMesh::formatFileSystem() { #elif defined(ESP32) return SPIFFS.format(); #elif defined(ARDULINUX_PLATFORM) - return false; // not supported on Linux + // not supported on linux + return false; #else #error "need to implement file system erase" return false; diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index cd1d03dbb9..2d4b69890b 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -70,9 +70,8 @@ void setup() { IdentityStore store(LittleFS, "/identity"); store.begin(); #elif defined(ARDULINUX_PLATFORM) - // The VFS root is established by the ArduLinux core from --fsdir (default: the - // XDG data dir, e.g. ~/.local/share/meshcored/default), so there is no per-app - // mountpoint override here — --fsdir is the single source of truth for the data path. + // the VFS root is established by the ArduLinux core from --fsdir + // (default: the XDG data dir, e.g. ~/.local/share/meshcored/default) fs = &ArduLinuxFS; IdentityStore store(ArduLinuxFS, "/identity"); store.begin(); diff --git a/platformio.ini b/platformio.ini index e16f7b8304..2c8b945ef5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -119,6 +119,55 @@ lib_deps = ${arduino_base.lib_deps} file://arch/stm32/Adafruit_LittleFS_stm32 SubGhz +; ----------------- LINUX --------------------- + +[linux_base] +platform = + # renovate: datasource=git-tags depName=ardulinux packageName=https://github.com/l5yth/ardulinux + git+https://github.com/l5yth/ardulinux.git#v0.2.0 +framework = arduino +board = linux +board_level = extra +board_build.progname = meshcored +build_src_filter = + ${env.build_src_filter} + - + - + - + - + - + - + - + - + - + +<../variants/linux> + - + - + - + - + - +lib_deps = + ${env.lib_deps} + rweather/Crypto@0.4.0 + adafruit/Adafruit seesaw Library@1.7.9 + electroniccats/CayenneLPP @ 1.6.1 + adafruit/RTClib @ ^2.1.3 + jgromes/RadioLib@7.4.0 + melopero/Melopero RV3028@^1.1.0 +build_flags = + ${arduino_base.build_flags} + -DARDULINUX_PLATFORM + -DRADIOLIB_EEPROM_UNSUPPORTED + -fPIC + -lpthread + -lstdc++fs + -lbluetooth + -luv + -std=gnu17 + -std=c++17 + -I variants/linux + -I /usr/include + [sensor_base] build_flags = -D ENV_INCLUDE_GPS=1 diff --git a/variants/ardulinux/platformio.ini b/variants/ardulinux/platformio.ini deleted file mode 100644 index b11f85c015..0000000000 --- a/variants/ardulinux/platformio.ini +++ /dev/null @@ -1,37 +0,0 @@ -[ardulinux_base] -platform = - # renovate: datasource=git-tags depName=ardulinux packageName=https://github.com/l5yth/ardulinux - git+https://github.com/l5yth/ardulinux.git#v0.2.0 -framework = arduino - -build_src_filter = - ${env.build_src_filter} - - - - - - - - - - - - - - - - - - - -lib_deps = - ${env.lib_deps} - rweather/Crypto@0.4.0 - adafruit/Adafruit seesaw Library@1.7.9 - electroniccats/CayenneLPP @ 1.6.1 - adafruit/RTClib @ ^2.1.3 - jgromes/RadioLib@7.4.0 - -build_flags = - ${arduino_base.build_flags} - -DARDULINUX_PLATFORM - -DRADIOLIB_EEPROM_UNSUPPORTED - -fPIC - -lpthread - -lstdc++fs - -lbluetooth - -luv - -std=gnu17 - -std=c++17 diff --git a/variants/linux/99-meshcore.rules b/variants/linux/99-meshcore.rules index 4c3df92e47..4071c847f6 100644 --- a/variants/linux/99-meshcore.rules +++ b/variants/linux/99-meshcore.rules @@ -1,6 +1,5 @@ -# udev rules for meshcored — grant the meshcore group access to SPI and GPIO devices. -# Works on Arch Linux, Debian/Raspberry Pi OS, and other distributions. -# Install: sudo cp 99-meshcore.rules /etc/udev/rules.d/ +# udev rules for meshcored +# Grant the meshcore group access to SPI and GPIO devices. SUBSYSTEM=="spidev", GROUP="meshcore", MODE="0660" KERNEL=="gpiochip*", GROUP="meshcore", MODE="0660" diff --git a/variants/linux/README.md b/variants/linux/README.md index c949b1708b..c47b8eee15 100644 --- a/variants/linux/README.md +++ b/variants/linux/README.md @@ -1,6 +1,6 @@ # MeshCore Linux Variant -Native Linux support for MeshCore, targeting Raspberry Pi (Zero, 3, 4, 5) and similar SBCs with an SX1262 LoRa radio attached over SPI. Uses [ArduLinux — Arduino API for Linux](https://github.com/l5yth/ardulinux) to run the same firmware codebase on Linux without modification to the core library. +Native Linux support for MeshCore, targeting Raspberry Pi (Zero, 3, 4, 5) and similar SBCs with an SX1262 LoRa radio attached over SPI. Uses [ArduLinux, Arduino API for Linux](https://github.com/l5yth/ardulinux) to run the same firmware codebase on Linux without modification to the core library. ## Hardware @@ -36,7 +36,7 @@ sudo pacman -S platformio-core # or: pipx install platformio pipx install platformio # or: pip install --user platformio ``` -**Build with `build.sh`** (recommended — embeds version and commit hash): +**Build with `build.sh`** (recommended, embeds version and commit hash): ```sh FIRMWARE_VERSION=dev ./build.sh build-firmware linux_repeater @@ -75,7 +75,7 @@ sudo nano /etc/meshcored/meshcored.ini The config file has two roles: - **Hardware config** (always read on every startup): SPI device, GPIO pin numbers, LoRa radio parameters. -- **First-run node defaults**: `advert_name`, `admin_password`, `lat`, `lon`. On the first boot these are saved to the node's persisted prefs (`com_prefs`). After that, use the serial CLI to change them (`set name`, `set password`, etc.) — the INI values are no longer consulted for these fields. +- **First-run node defaults**: `advert_name`, `admin_password`, `lat`, `lon`. On the first boot these are saved to the node's persisted prefs (`com_prefs`). After that, use the serial CLI to change them (`set name`, `set password`, etc.), the INI values are no longer consulted for these fields. Key settings: @@ -97,13 +97,13 @@ Key settings: | `current_limit` | `140` | Radio over-current protection limit in mA | | `dio2_as_rf_switch` | `0` | `1` = use DIO2 to drive the TX/RX RF switch. **Required for the Waveshare Core1262** (without it the radio inits but TX/RX are dead); depends on module wiring | | `rx_boosted_gain` | `1` | `1` enables the SX126x RX boosted-gain mode; `0` disables | -| `advert_name` | `"Linux Repeater"` | Node name — first-run default only | -| `admin_password` | `"password"` | Admin password — **change this**, first-run default only | -| `lat` / `lon` | `0.0` | GPS coordinates for advertisement — first-run default only | +| `advert_name` | `"Linux Repeater"` | Node name, first-run default only | +| `admin_password` | `"password"` | Admin password, **change this**, first-run default only | +| `lat` / `lon` | `0.0` | GPS coordinates for advertisement, first-run default only | ### 3. Enable SPI and GPIO access -First make sure the SPI interface is actually enabled — the radio needs a +First make sure the SPI interface is actually enabled, the radio needs a `/dev/spidev*` node. Check with `ls /dev/spidev*`; if there is none: ```sh @@ -114,13 +114,13 @@ sudo raspi-config # Interface Options → SPI → Enable, then reboot echo 'dtparam=spi=on' | sudo tee -a /boot/config.txt # then reboot ``` -> The boot config path varies by image — it is `/boot/config.txt` on most +> The boot config path varies by image, it is `/boot/config.txt` on most > Raspberry Pi images but `/boot/firmware/config.txt` on some. After rebooting, > confirm `/dev/spidev0.0` exists. > > **Arch Linux kernel caveat:** `dtparam=spi=on` is only honored by the Raspberry > Pi `linux-rpi` (vendor) kernel. The mainline `linux-aarch64` kernel boots via -> U-Boot, which loads its own device tree and ignores `config.txt` overlays — so +> U-Boot, which loads its own device tree and ignores `config.txt` overlays, so > `/dev/spidev*` never appears regardless of `config.txt`. If SPI is missing after > enabling it and rebooting, switch to the vendor kernel > (`sudo pacman -S linux-rpi`, remove `linux-aarch64`) and reboot. @@ -159,7 +159,7 @@ back in. To use it immediately in one shell, prefix the command with | `-V`, `--version` | Print the firmware version | **Directly** (for testing). With the udev rules in place you can run as your own -user — no `sudo`. Data is persisted under the VFS root, which defaults to the XDG +user, no `sudo`. Data is persisted under the VFS root, which defaults to the XDG data dir; pass `--fsdir` to choose another location: ```sh @@ -202,14 +202,14 @@ set lon There are two levels of reset: -**Prefs only** — keeps the node identity (same Repeater ID). Delete the saved prefs so the INI first-run defaults are re-applied on the next boot: +**Prefs only**, keeps the node identity (same Repeater ID). Delete the saved prefs so the INI first-run defaults are re-applied on the next boot: ```sh sudo rm /var/lib/meshcore/com_prefs sudo systemctl restart meshcored ``` -**Full reset** — also discards the identity, so the node returns with a **new** Repeater ID. This wipes the whole VFS root. The built-in `-e`/`--erase` flag does exactly that before starting, but for the managed service just clear the directory while it is stopped (keep `--erase` out of the unit — see the note below): +**Full reset**, also discards the identity, so the node returns with a **new** Repeater ID. This wipes the whole VFS root. The built-in `-e`/`--erase` flag does exactly that before starting, but for the managed service just clear the directory while it is stopped (keep `--erase` out of the unit, see the note below): ```sh sudo systemctl stop meshcored @@ -223,8 +223,8 @@ sudo systemctl start meshcored ## Known Gaps / TODO -- **Config path is hardcoded** — meshcored always loads `/etc/meshcored/meshcored.ini`; there is no flag to point it elsewhere. (The data *path* is separate and configurable: it is the ArduLinux VFS root, set with `--fsdir`.) -- **Only repeater firmware** — there is no `linux_companion` target yet; companion radio support (BLE/serial interface to a phone app) is not implemented for Linux. -- **Serial `erase` command is a no-op** — `formatFileSystem()` returns `false` on Linux, so the interactive serial `erase` command reports failure. To wipe the filesystem, use the `--erase` *startup* flag (or clear the VFS dir) instead — see step 5. -- **No power management** — `board.sleep()` is a no-op; the power-saving loop in `main.cpp` never actually sleeps. -- **Upstream-sync fragility** — the radio wrapper (`LinuxSX1262Wrapper`) implements the `RadioLibWrapper` interface by hand, so it can drift from upstream in two ways: a new **pure-virtual** method breaks the Linux build (e.g. `setParams()`), and a new **virtual-with-default** method silently no-ops on Linux until overridden (e.g. `set`/`getRxBoostedGainMode()`, which reported and applied the wrong state until added). Mirror `CustomSX1262Wrapper` when syncing. +- **Config path is hardcoded**, meshcored always loads `/etc/meshcored/meshcored.ini`; there is no flag to point it elsewhere. (The data *path* is separate and configurable: it is the ArduLinux VFS root, set with `--fsdir`.) +- **Only repeater firmware**, there is no `linux_companion` target yet; companion radio support (BLE/serial interface to a phone app) is not implemented for Linux. +- **Serial `erase` command is a no-op**, `formatFileSystem()` returns `false` on Linux, so the interactive serial `erase` command reports failure. To wipe the filesystem, use the `--erase` *startup* flag (or clear the VFS dir) instead, see step 5. +- **No power management**, `board.sleep()` is a no-op; the power-saving loop in `main.cpp` never actually sleeps. +- **Upstream-sync fragility**, the radio wrapper (`LinuxSX1262Wrapper`) implements the `RadioLibWrapper` interface by hand, so it can drift from upstream in two ways: a new **pure-virtual** method breaks the Linux build (e.g. `setParams()`), and a new **virtual-with-default** method silently no-ops on Linux until overridden (e.g. `set`/`getRxBoostedGainMode()`, which reported and applied the wrong state until added). Mirror `CustomSX1262Wrapper` when syncing. diff --git a/variants/linux/meshcored.ini b/variants/linux/meshcored.ini index 794ffc490b..76a7cf6b01 100644 --- a/variants/linux/meshcored.ini +++ b/variants/linux/meshcored.ini @@ -2,8 +2,6 @@ advert_name = Sample Router admin_password = password lat = 0.0 lon = 0.0 -# Data path: set via the --fsdir CLI flag (the systemd unit uses -# /var/lib/meshcore), not in this file. # Waveshare LoRa hat #lora_irq_pin = 16 diff --git a/variants/linux/meshcored.ini.pow-sx1262 b/variants/linux/meshcored.ini.pow-sx1262 index 024c5d77cf..8efb7ce2cf 100644 --- a/variants/linux/meshcored.ini.pow-sx1262 +++ b/variants/linux/meshcored.ini.pow-sx1262 @@ -1,12 +1,12 @@ -# meshcored.ini — PoW SX1262 HAT on Raspberry Pi Zero 2W +# meshcored.ini - PoW SX1262 HAT on Raspberry Pi Zero 2W # GPIO numbering is BCM (the number after "GPIO", e.g. GPIO22 = 22). -# + # Hardware config (read on every startup): spidev = /dev/spidev0.0 lora_irq_pin = 22 lora_reset_pin = 13 -# lora_nss_pin — SS is handled by the SPI driver; not needed -# lora_busy_pin — not wired on this HAT +# lora_nss_pin - SS is handled by the SPI driver; not needed +# lora_busy_pin - not wired on this HAT lora_freq = 869.618 lora_bw = 62.5 lora_sf = 8 @@ -20,5 +20,3 @@ admin_password = changeme lat = 0.0 lon = 0.0 -# Data is persisted under the VFS root set by the --fsdir CLI flag (default -# ~/.local/share/meshcored/default; the systemd unit uses /var/lib/meshcore). diff --git a/variants/linux/meshcored.ini.waveshare b/variants/linux/meshcored.ini.waveshare index 5aeb28e1d7..54139c8b35 100644 --- a/variants/linux/meshcored.ini.waveshare +++ b/variants/linux/meshcored.ini.waveshare @@ -1,6 +1,6 @@ -# meshcored.ini — Waveshare SX1262 LoRa HAT on Raspberry Pi 3/4/5 +# meshcored.ini - Waveshare SX1262 LoRa HAT on Raspberry Pi 3/4/5 # GPIO numbering is BCM (the number after "GPIO", e.g. GPIO16 = 16). -# + # Hardware config (read on every startup): spidev = /dev/spidev0.0 lora_irq_pin = 16 @@ -13,8 +13,7 @@ lora_sf = 8 lora_cr = 8 lora_tcxo = 1.8 lora_tx_power = 22 -# DIO2 drives the TX/RX RF switch on the Waveshare Core1262 — required for this HAT -# (without it the radio inits fine but TX/RX stay dead). 1 = enabled, 0 = disabled. +# DIO2 drives the TX/RX RF switch on the Waveshare Core1262 dio2_as_rf_switch = 1 # First-run node defaults (ignored after first boot; use CLI to change): @@ -23,5 +22,3 @@ admin_password = changeme lat = 0.0 lon = 0.0 -# Data is persisted under the VFS root set by the --fsdir CLI flag (default -# ~/.local/share/meshcored/default; the systemd unit uses /var/lib/meshcore). diff --git a/variants/linux/meshcored.service b/variants/linux/meshcored.service index d95be0ba4c..72d42f0a76 100644 --- a/variants/linux/meshcored.service +++ b/variants/linux/meshcored.service @@ -13,18 +13,10 @@ WorkingDirectory=/var/lib/meshcore Restart=on-failure RestartSec=5 LimitNOFILE=65535 - -# Security hardening ProtectSystem=strict ProtectHome=yes PrivateTmp=yes NoNewPrivileges=yes - -# systemd creates /var/lib/meshcore owned by User:Group before the namespace is -# set up, and makes it the unit's only writable path. This must come from -# StateDirectory rather than ExecStartPre+ReadWritePaths: with ProtectSystem=strict -# the read-write path has to exist when the mount namespace is built, which happens -# before ExecStartPre runs (a mkdir there fails with status=226/NAMESPACE). StateDirectory=meshcore [Install] diff --git a/variants/linux/platformio.ini b/variants/linux/platformio.ini index c8634b5e4b..8f42ce46b1 100644 --- a/variants/linux/platformio.ini +++ b/variants/linux/platformio.ini @@ -1,27 +1,5 @@ -[linux_base] -extends = ardulinux_base -build_flags = ${ardulinux_base.build_flags} - -I variants/linux - -I /usr/include -board = linux -board_level = extra -board_build.progname = meshcored -lib_deps = - ${ardulinux_base.lib_deps} - melopero/Melopero RV3028@^1.1.0 - -build_src_filter = ${ardulinux_base.build_src_filter} - +<../variants/linux> - - - - - - - - - - - [env:linux] extends = linux_base -; The pkg-config commands below optionally add link flags. -; the || : is just a "or run the null command" to avoid returning an error code build_flags = ${linux_base.build_flags} !pkg-config --cflags --libs libbsd-overlay --silence-errors || : diff --git a/variants/linux/target.cpp b/variants/linux/target.cpp index a69218e2ee..41609fc32e 100644 --- a/variants/linux/target.cpp +++ b/variants/linux/target.cpp @@ -5,11 +5,6 @@ class ArduLinuxHal : public ArduinoHal { public: ArduLinuxHal(SPIClass &spi, SPISettings spiSettings) : ArduinoHal(spi, spiSettings){}; - - // ArduLinux's SPIClass exposes only an in-place transfer(buf, len) that - // overwrites buf with the received bytes. RadioLib's HAL expects a - // full-duplex transfer(out, len, in), so copy out -> in first, then run the - // in-place exchange (out and in are distinct, non-overlapping buffers). void spiTransfer(uint8_t *out, size_t len, uint8_t *in) { memcpy(in, out, len); spi->transfer(in, len); From ed1a5607838c9925b53718af1fc71e94d3a87199 Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Mon, 8 Jun 2026 21:55:49 +0200 Subject: [PATCH 10/14] pin ardulinux to wire-fix branch, document pkg-config (#12) * pin ardulinux to wire-fix branch, document pkg-config * pin ardulinux 0.2.1 --- platformio.ini | 2 +- variants/linux/README.md | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/platformio.ini b/platformio.ini index 2c8b945ef5..f6d02fc5cf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -124,7 +124,7 @@ lib_deps = ${arduino_base.lib_deps} [linux_base] platform = # renovate: datasource=git-tags depName=ardulinux packageName=https://github.com/l5yth/ardulinux - git+https://github.com/l5yth/ardulinux.git#v0.2.0 + git+https://github.com/l5yth/ardulinux.git#v0.2.1 framework = arduino board = linux board_level = extra diff --git a/variants/linux/README.md b/variants/linux/README.md index c47b8eee15..33ba96c45a 100644 --- a/variants/linux/README.md +++ b/variants/linux/README.md @@ -14,17 +14,20 @@ Native Linux support for MeshCore, targeting Raspberry Pi (Zero, 3, 4, 5) and si ```sh # Arch Linux -sudo pacman -S libgpiod i2c-tools bluez-libs libuv +sudo pacman -S pkgconf libgpiod i2c-tools bluez-libs libuv # Debian/Raspberry Pi OS -sudo apt install libgpiod-dev libi2c-dev libbluetooth-dev libuv1-dev +sudo apt install pkg-config libgpiod-dev libi2c-dev libbluetooth-dev libuv1-dev ``` +> On DietPi the base image does not include `pkg-config`; without it the build +> silently falls back to simulated GPIO/I2C even when `libgpiod-dev` is +> installed, and the resulting `meshcored` won't talk to the radio. + The ArduLinux platform always links `bluetooth`, `uv`, `pthread`, and `stdc++fs`; `gpiod`/`i2c` are added automatically when libgpiod is detected via -`pkg-config` (without it the build falls back to simulated GPIO/I2C). Missing -`bluez-libs`/`libbluetooth-dev` shows up as a `cannot find -lbluetooth` link -error. +`pkg-config`. Missing `bluez-libs`/`libbluetooth-dev` shows up as a `cannot +find -lbluetooth` link error. You also need **PlatformIO Core** (`pio`) to build: From 6ec1c1ed3e5c198258955dce0b8b972b924e535d Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Tue, 9 Jun 2026 12:22:10 +0200 Subject: [PATCH 11/14] variants/linux: fail loud when libgpiod is missing or pin claim fails (#14) * variants/linux: fail loud when libgpiod is missing or pin claim fails * variants/linux: exit when configured GPIO pins fail to bind --- variants/linux/LinuxBoard.cpp | 37 ++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/variants/linux/LinuxBoard.cpp b/variants/linux/LinuxBoard.cpp index 9e9c79de48..24683c3b1d 100644 --- a/variants/linux/LinuxBoard.cpp +++ b/variants/linux/LinuxBoard.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #ifdef ARDULINUX_HARDWARE #include "linux/gpio/LinuxGPIOPin.h" #endif @@ -24,8 +25,13 @@ int initGPIOPin(uint8_t pinNum, const std::string gpioChipName, uint8_t line) csPin->setSilent(); gpioBind(csPin); return 0; + } catch (const std::exception& e) { + printf("ERROR: cannot claim GPIO line %d on %s for pin %d: %s\n", + (int)line, gpioChipName.c_str(), (int)pinNum, e.what()); + return 1; } catch (...) { - MESH_DEBUG_PRINTLN("Warning, cannot claim pin %d", pinNum); + printf("ERROR: cannot claim GPIO line %d on %s for pin %d (unknown exception)\n", + (int)line, gpioChipName.c_str(), (int)pinNum); return 1; } #else @@ -37,6 +43,17 @@ void ardulinuxSetup() { } void LinuxBoard::begin() { +#ifndef ARDULINUX_HARDWARE + printf("FATAL: meshcored was built without libgpiod support; all GPIO/I2C\n" + " operations would be simulated and the radio cannot be driven.\n" + " Install pkg-config and libgpiod-dev on the build machine, clear\n" + " the PlatformIO cache, and rebuild:\n" + " sudo apt install -y pkg-config libgpiod-dev\n" + " rm -rf ~/.platformio/platforms/ardulinux* .pio\n" + " pio run -e linux_repeater\n"); + exit(1); +#endif + config.load("/etc/meshcored/meshcored.ini"); printf("SPI begin %s\n", config.spidev); @@ -50,23 +67,29 @@ void LinuxBoard::begin() { (int)config.lora_rxen_pin, (int)config.lora_txen_pin); + int failures = 0; if (config.lora_nss_pin != RADIOLIB_NC) { - initGPIOPin(config.lora_nss_pin, "gpiochip0", config.lora_nss_pin); + failures += initGPIOPin(config.lora_nss_pin, "gpiochip0", config.lora_nss_pin); } if (config.lora_busy_pin != RADIOLIB_NC) { - initGPIOPin(config.lora_busy_pin, "gpiochip0", config.lora_busy_pin); + failures += initGPIOPin(config.lora_busy_pin, "gpiochip0", config.lora_busy_pin); } if (config.lora_irq_pin != RADIOLIB_NC) { - initGPIOPin(config.lora_irq_pin, "gpiochip0", config.lora_irq_pin); + failures += initGPIOPin(config.lora_irq_pin, "gpiochip0", config.lora_irq_pin); } if (config.lora_reset_pin != RADIOLIB_NC) { - initGPIOPin(config.lora_reset_pin, "gpiochip0", config.lora_reset_pin); + failures += initGPIOPin(config.lora_reset_pin, "gpiochip0", config.lora_reset_pin); } if (config.lora_rxen_pin != RADIOLIB_NC) { - initGPIOPin(config.lora_rxen_pin, "gpiochip0", config.lora_rxen_pin); + failures += initGPIOPin(config.lora_rxen_pin, "gpiochip0", config.lora_rxen_pin); } if (config.lora_txen_pin != RADIOLIB_NC) { - initGPIOPin(config.lora_txen_pin, "gpiochip0", config.lora_txen_pin); + failures += initGPIOPin(config.lora_txen_pin, "gpiochip0", config.lora_txen_pin); + } + + if (failures > 0) { + printf("FATAL: %d GPIO pin(s) failed to bind; cannot start radio.\n", failures); + exit(1); } } From b1d9b8fd2b9e60cac7313e9e06dfdf0899dad239 Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Tue, 9 Jun 2026 12:56:01 +0200 Subject: [PATCH 12/14] variants/linux: make lora_gpiochip configurable, refresh docs (#15) * variants/linux: make lora_gpiochip configurable, refresh docs * ci: variants/linux: add lora_gpiochip hint to base meshcored.ini too * bump ardulinux to 0.2.2 --- platformio.ini | 2 +- variants/linux/LinuxBoard.cpp | 13 +++++++------ variants/linux/LinuxBoard.h | 1 + variants/linux/README.md | 13 +++++++------ variants/linux/meshcored.ini | 1 + variants/linux/meshcored.ini.pow-sx1262 | 1 + variants/linux/meshcored.ini.waveshare | 1 + 7 files changed, 19 insertions(+), 13 deletions(-) diff --git a/platformio.ini b/platformio.ini index f6d02fc5cf..a94bf43587 100644 --- a/platformio.ini +++ b/platformio.ini @@ -124,7 +124,7 @@ lib_deps = ${arduino_base.lib_deps} [linux_base] platform = # renovate: datasource=git-tags depName=ardulinux packageName=https://github.com/l5yth/ardulinux - git+https://github.com/l5yth/ardulinux.git#v0.2.1 + git+https://github.com/l5yth/ardulinux.git#v0.2.2 framework = arduino board = linux board_level = extra diff --git a/variants/linux/LinuxBoard.cpp b/variants/linux/LinuxBoard.cpp index 24683c3b1d..c218a2f113 100644 --- a/variants/linux/LinuxBoard.cpp +++ b/variants/linux/LinuxBoard.cpp @@ -69,22 +69,22 @@ void LinuxBoard::begin() { int failures = 0; if (config.lora_nss_pin != RADIOLIB_NC) { - failures += initGPIOPin(config.lora_nss_pin, "gpiochip0", config.lora_nss_pin); + failures += initGPIOPin(config.lora_nss_pin, config.lora_gpiochip, config.lora_nss_pin); } if (config.lora_busy_pin != RADIOLIB_NC) { - failures += initGPIOPin(config.lora_busy_pin, "gpiochip0", config.lora_busy_pin); + failures += initGPIOPin(config.lora_busy_pin, config.lora_gpiochip, config.lora_busy_pin); } if (config.lora_irq_pin != RADIOLIB_NC) { - failures += initGPIOPin(config.lora_irq_pin, "gpiochip0", config.lora_irq_pin); + failures += initGPIOPin(config.lora_irq_pin, config.lora_gpiochip, config.lora_irq_pin); } if (config.lora_reset_pin != RADIOLIB_NC) { - failures += initGPIOPin(config.lora_reset_pin, "gpiochip0", config.lora_reset_pin); + failures += initGPIOPin(config.lora_reset_pin, config.lora_gpiochip, config.lora_reset_pin); } if (config.lora_rxen_pin != RADIOLIB_NC) { - failures += initGPIOPin(config.lora_rxen_pin, "gpiochip0", config.lora_rxen_pin); + failures += initGPIOPin(config.lora_rxen_pin, config.lora_gpiochip, config.lora_rxen_pin); } if (config.lora_txen_pin != RADIOLIB_NC) { - failures += initGPIOPin(config.lora_txen_pin, "gpiochip0", config.lora_txen_pin); + failures += initGPIOPin(config.lora_txen_pin, config.lora_gpiochip, config.lora_txen_pin); } if (failures > 0) { @@ -149,6 +149,7 @@ int LinuxConfig::load(const char *filename) { } if (strcmp(key, "spidev") == 0) spidev = safe_copy(value, 32); + else if (strcmp(key, "lora_gpiochip") == 0) lora_gpiochip = safe_copy(value, 32); else if (strcmp(key, "lora_freq") == 0) lora_freq = atof(value); else if (strcmp(key, "lora_bw") == 0) lora_bw = atof(value); else if (strcmp(key, "lora_sf") == 0) lora_sf = (uint8_t)atoi(value); diff --git a/variants/linux/LinuxBoard.h b/variants/linux/LinuxBoard.h index 0a18f85c16..f3044d7d98 100644 --- a/variants/linux/LinuxBoard.h +++ b/variants/linux/LinuxBoard.h @@ -31,6 +31,7 @@ class LinuxConfig { bool rx_boosted_gain = true; char* spidev = "/dev/spidev0.0"; + char* lora_gpiochip = "gpiochip0"; float lora_tcxo = 1.8f; diff --git a/variants/linux/README.md b/variants/linux/README.md index 33ba96c45a..581aedfca0 100644 --- a/variants/linux/README.md +++ b/variants/linux/README.md @@ -20,14 +20,14 @@ sudo pacman -S pkgconf libgpiod i2c-tools bluez-libs libuv sudo apt install pkg-config libgpiod-dev libi2c-dev libbluetooth-dev libuv1-dev ``` -> On DietPi the base image does not include `pkg-config`; without it the build -> silently falls back to simulated GPIO/I2C even when `libgpiod-dev` is -> installed, and the resulting `meshcored` won't talk to the radio. - The ArduLinux platform always links `bluetooth`, `uv`, `pthread`, and `stdc++fs`; `gpiod`/`i2c` are added automatically when libgpiod is detected via -`pkg-config`. Missing `bluez-libs`/`libbluetooth-dev` shows up as a `cannot -find -lbluetooth` link error. +`pkg-config`. If `pkg-config` is missing (e.g. on DietPi, which does not ship +it in the base image), libgpiod goes undetected and the build falls back to +simulated GPIO/I2C — the resulting `meshcored` will refuse to start with a +`FATAL: meshcored was built without libgpiod support` message pointing back at +the missing dep. Missing `bluez-libs`/`libbluetooth-dev` shows up at link time +as `cannot find -lbluetooth`. You also need **PlatformIO Core** (`pio`) to build: @@ -85,6 +85,7 @@ Key settings: | Key | Default | Notes | |-----|---------|-------| | `spidev` | `/dev/spidev0.0` | SPI device node | +| `lora_gpiochip` | `gpiochip0` | Name of the `/dev/gpiochip*` device (or kernel label). `gpiochip0` is correct for Pi 3/4/Zero 2W; Pi 5 may need `gpiochip4` or `pinctrl-rp1` depending on kernel | | `lora_irq_pin` | (none) | GPIO line number for IRQ | | `lora_reset_pin` | (none) | GPIO line number for RESET | | `lora_nss_pin` | (none) | GPIO line number for NSS/CS (if not handled by the SPI driver) | diff --git a/variants/linux/meshcored.ini b/variants/linux/meshcored.ini index 76a7cf6b01..d7b17d790c 100644 --- a/variants/linux/meshcored.ini +++ b/variants/linux/meshcored.ini @@ -17,6 +17,7 @@ lora_reset_pin = 13 #lora_txen_pin spidev = /dev/spidev0.0 +# lora_gpiochip = gpiochip0 # Pi 3/4/Zero 2W default; Pi 5 may need gpiochip4 or pinctrl-rp1 lora_freq = 869.618 lora_bw = 62.5 lora_sf = 8 diff --git a/variants/linux/meshcored.ini.pow-sx1262 b/variants/linux/meshcored.ini.pow-sx1262 index 8efb7ce2cf..96b5040a9f 100644 --- a/variants/linux/meshcored.ini.pow-sx1262 +++ b/variants/linux/meshcored.ini.pow-sx1262 @@ -3,6 +3,7 @@ # Hardware config (read on every startup): spidev = /dev/spidev0.0 +# lora_gpiochip = gpiochip0 # Pi 3/4/Zero 2W default; Pi 5 may need gpiochip4 or pinctrl-rp1 lora_irq_pin = 22 lora_reset_pin = 13 # lora_nss_pin - SS is handled by the SPI driver; not needed diff --git a/variants/linux/meshcored.ini.waveshare b/variants/linux/meshcored.ini.waveshare index 54139c8b35..45b03bf82f 100644 --- a/variants/linux/meshcored.ini.waveshare +++ b/variants/linux/meshcored.ini.waveshare @@ -3,6 +3,7 @@ # Hardware config (read on every startup): spidev = /dev/spidev0.0 +# lora_gpiochip = gpiochip0 # Pi 3/4/Zero 2W default; Pi 5 may need gpiochip4 or pinctrl-rp1 lora_irq_pin = 16 lora_reset_pin = 18 lora_nss_pin = 21 From 4d189a90e04a97040c8f919bf2ab2b668dd40009 Mon Sep 17 00:00:00 2001 From: Littleaton <55291686+Littleaton@users.noreply.github.com> Date: Tue, 9 Jun 2026 13:07:28 -0600 Subject: [PATCH 13/14] added radio examples. --- .../meshcored.ini.ecb41-pge-e22-900m22s-mini | 22 ++++++++++++++++ .../meshcored.ini.ecb41-pge-e22-900m30s | 22 ++++++++++++++++ ...cored.ini.ecb41-pge-rak6421-rak13300-slot1 | 22 ++++++++++++++++ ...cored.ini.ecb41-pge-rak6421-rak13300-slot2 | 22 ++++++++++++++++ ...cored.ini.ecb41-pge-rak6421-rak13302-slot1 | 22 ++++++++++++++++ ...cored.ini.ecb41-pge-rak6421-rak13302-slot2 | 22 ++++++++++++++++ .../meshcored.ini.lyra-zero-e22-900m22s-mini | 22 ++++++++++++++++ .../meshcored.ini.lyra-zero-e22-900m30s | 22 ++++++++++++++++ ...cored.ini.lyra-zero-rak6421-rak13300-slot1 | 22 ++++++++++++++++ ...cored.ini.lyra-zero-rak6421-rak13300-slot2 | 22 ++++++++++++++++ ...cored.ini.lyra-zero-rak6421-rak13302-slot1 | 22 ++++++++++++++++ ...cored.ini.lyra-zero-rak6421-rak13302-slot2 | 22 ++++++++++++++++ .../meshcored.ini.nebra-hat-1w | 21 ++++++++++++++++ .../meshcored.ini.nebra-hat-2w | 21 ++++++++++++++++ .../meshcored.ini.ok3506-e22-900m22s-mini | 22 ++++++++++++++++ .../meshcored.ini.ok3506-e22-900m30s | 22 ++++++++++++++++ ...eshcored.ini.ok3506-rak6421-rak13300-slot1 | 22 ++++++++++++++++ ...eshcored.ini.ok3506-rak6421-rak13300-slot2 | 22 ++++++++++++++++ ...eshcored.ini.ok3506-rak6421-rak13302-slot1 | 22 ++++++++++++++++ ...eshcored.ini.ok3506-rak6421-rak13302-slot2 | 22 ++++++++++++++++ .../meshcored.ini.ok3506-waveshare-sx1262 | 21 ++++++++++++++++ .../meshcored.ini.pinedio-usb-sx1262 | 17 +++++++++++++ .../meshcored.ini.rpi-e22-900m22s-mini | 22 ++++++++++++++++ .../meshcored.ini.rpi-e22-900m30s | 22 ++++++++++++++++ .../meshcored.ini.rpi-rak6421-rak13300-slot1 | 22 ++++++++++++++++ .../meshcored.ini.rpi-rak6421-rak13300-slot2 | 19 ++++++++++++++ .../meshcored.ini.rpi-rak6421-rak13302-slot1 | 22 ++++++++++++++++ .../meshcored.ini.rpi-rak6421-rak13302-slot2 | 20 +++++++++++++++ .../meshcored.ini.usb-e22-adapter | 24 ++++++++++++++++++ .../meshcored.ini.usb-meshstick-sx1262 | 24 ++++++++++++++++++ .../meshcored.ini.usb-umesh-sx1262-30dbm | 25 +++++++++++++++++++ .../meshcored.ini.zebra-hat-1w | 21 ++++++++++++++++ .../meshcored.ini.zebra-hat-2w | 22 ++++++++++++++++ 33 files changed, 719 insertions(+) create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-e22-900m22s-mini create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-e22-900m30s create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot1 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot2 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot1 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot2 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-e22-900m22s-mini create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-e22-900m30s create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot1 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot2 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot1 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot2 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.nebra-hat-1w create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.nebra-hat-2w create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ok3506-e22-900m22s-mini create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ok3506-e22-900m30s create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot1 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot2 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot1 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot2 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.ok3506-waveshare-sx1262 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.pinedio-usb-sx1262 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.rpi-e22-900m22s-mini create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.rpi-e22-900m30s create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot1 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot2 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot1 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot2 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.usb-e22-adapter create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.usb-meshstick-sx1262 create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.usb-umesh-sx1262-30dbm create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.zebra-hat-1w create mode 100644 variants/linux/addon-radio-ini/meshcored.ini.zebra-hat-2w diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-e22-900m22s-mini b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-e22-900m22s-mini new file mode 100644 index 0000000000..7ba7517330 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-e22-900m22s-mini @@ -0,0 +1,22 @@ +# meshcored.ini - MeshAdv Mini E22-900M22S + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 3 +lora_reset_pin = 51 +lora_nss_pin = 14 +lora_busy_pin = 0 +lora_rxen_pin = 43 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = MeshAdv Mini E22-900M22S +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-e22-900m30s b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-e22-900m30s new file mode 100644 index 0000000000..1a3b3a62ab --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-e22-900m30s @@ -0,0 +1,22 @@ +# meshcored.ini - MeshAdv-Pi E22-900M30S + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 3 +lora_reset_pin = 12 +lora_nss_pin = 1 +lora_busy_pin = 0 +lora_rxen_pin = 43 +lora_txen_pin = 57 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = MeshAdv-Pi E22-900M30S +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot1 new file mode 100644 index 0000000000..217c7528cc --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot1 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13300 Slot 1 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 5 +lora_reset_pin = 3 +lora_busy_pin = 51 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 43@gpiochip1:11, 57@gpiochip1:25 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13300 Slot 1 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot2 new file mode 100644 index 0000000000..a1f241fc52 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot2 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13300 Slot 2 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.1 +lora_irq_pin = 12 +lora_reset_pin = 51 +lora_busy_pin = 11 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 2@gpiochip0:2, 50@gpiochip1:18 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13300 Slot 2 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot1 new file mode 100644 index 0000000000..62a72ae734 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot1 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13302 Slot 1 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 5 +lora_reset_pin = 3 +lora_busy_pin = 51 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 43@gpiochip1:11, 57@gpiochip1:25 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13302 Slot 1 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot2 new file mode 100644 index 0000000000..9f70270c5c --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot2 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13302 Slot 2 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.1 +lora_irq_pin = 12 +lora_reset_pin = 51 +lora_busy_pin = 11 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 2@gpiochip0:2, 50@gpiochip1:18 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13302 Slot 2 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-e22-900m22s-mini b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-e22-900m22s-mini new file mode 100644 index 0000000000..ef74345106 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-e22-900m22s-mini @@ -0,0 +1,22 @@ +# meshcored.ini - MeshAdv Mini E22-900M22S + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 57 +lora_reset_pin = 12 +lora_nss_pin = 10 +lora_busy_pin = 17 +lora_rxen_pin = 58 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = MeshAdv Mini E22-900M22S +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-e22-900m30s b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-e22-900m30s new file mode 100644 index 0000000000..a49e77620d --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-e22-900m30s @@ -0,0 +1,22 @@ +# meshcored.ini - MeshAdv-Pi E22-900M30S + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 57 +lora_reset_pin = 14 +lora_nss_pin = 18 +lora_busy_pin = 17 +lora_rxen_pin = 58 +lora_txen_pin = 50 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = MeshAdv-Pi E22-900M30S +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot1 new file mode 100644 index 0000000000..e3d7f73e52 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot1 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13300 Slot 1 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 5 +lora_reset_pin = 57 +lora_busy_pin = 12 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 58@gpiochip1:26, 50@gpiochip1:18 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13300 Slot 1 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot2 new file mode 100644 index 0000000000..f3c3701a20 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot2 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13300 Slot 2 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.1 +lora_irq_pin = 14 +lora_reset_pin = 12 +lora_busy_pin = 48 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 51@gpiochip1:19, 13@gpiochip0:13 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13300 Slot 2 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot1 new file mode 100644 index 0000000000..e3d7f73e52 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot1 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13300 Slot 1 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 5 +lora_reset_pin = 57 +lora_busy_pin = 12 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 58@gpiochip1:26, 50@gpiochip1:18 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13300 Slot 1 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot2 new file mode 100644 index 0000000000..f3c3701a20 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot2 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13300 Slot 2 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.1 +lora_irq_pin = 14 +lora_reset_pin = 12 +lora_busy_pin = 48 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 51@gpiochip1:19, 13@gpiochip0:13 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13300 Slot 2 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.nebra-hat-1w b/variants/linux/addon-radio-ini/meshcored.ini.nebra-hat-1w new file mode 100644 index 0000000000..412dcb8756 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.nebra-hat-1w @@ -0,0 +1,21 @@ +# meshcored.ini - NebraHat 1W + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 22 +lora_reset_pin = 18 +lora_busy_pin = 4 +lora_rxen_pin = 25 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = NebraHat 1W +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.nebra-hat-2w b/variants/linux/addon-radio-ini/meshcored.ini.nebra-hat-2w new file mode 100644 index 0000000000..0e35e952bc --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.nebra-hat-2w @@ -0,0 +1,21 @@ +# meshcored.ini - NebraHat 2W + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 22 +lora_reset_pin = 18 +lora_busy_pin = 4 +lora_rxen_pin = 25 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 8 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = NebraHat 2W +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-e22-900m22s-mini b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-e22-900m22s-mini new file mode 100644 index 0000000000..ec4a25f68f --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-e22-900m22s-mini @@ -0,0 +1,22 @@ +# meshcored.ini - MeshAdv Mini E22-900M22S + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 104 +lora_reset_pin = 102 +lora_nss_pin = 19 +lora_busy_pin = 9 +lora_rxen_pin = 2 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = MeshAdv Mini E22-900M22S +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-e22-900m30s b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-e22-900m30s new file mode 100644 index 0000000000..d21a4b9eb5 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-e22-900m30s @@ -0,0 +1,22 @@ +# meshcored.ini - MeshAdv-Pi E22-900M30S + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 104 +lora_reset_pin = 14 +lora_nss_pin = 8 +lora_busy_pin = 9 +lora_rxen_pin = 2 +lora_txen_pin = 3 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = MeshAdv-Pi E22-900M30S +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot1 new file mode 100644 index 0000000000..a7ac9da26e --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot1 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13300 Slot 1 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 109 +lora_reset_pin = 104 +lora_busy_pin = 102 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 2@gpiochip0:2, 3@gpiochip0:3 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13300 Slot 1 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot2 new file mode 100644 index 0000000000..efb1b28bfa --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot2 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13300 Slot 2 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.1 +lora_irq_pin = 14 +lora_reset_pin = 102 +lora_busy_pin = 10 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 106@gpiochip3:10, 103@gpiochip3:7 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13300 Slot 2 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot1 new file mode 100644 index 0000000000..07ad4f8d84 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot1 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13302 Slot 1 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 109 +lora_reset_pin = 104 +lora_busy_pin = 102 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 2@gpiochip0:2, 3@gpiochip0:3 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13302 Slot 1 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot2 new file mode 100644 index 0000000000..fe01f96e12 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot2 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13302 Slot 2 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.1 +lora_irq_pin = 14 +lora_reset_pin = 102 +lora_busy_pin = 10 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 106@gpiochip3:10, 103@gpiochip3:7 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13302 Slot 2 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-waveshare-sx1262 b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-waveshare-sx1262 new file mode 100644 index 0000000000..7553e52478 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-waveshare-sx1262 @@ -0,0 +1,21 @@ +# meshcored.ini - Waveshare SX1262 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 104 +lora_reset_pin = 14 +lora_nss_pin = 8 +lora_busy_pin = 9 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source SX126X_ANT_SW is not modeled in meshcored.ini. + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = Waveshare SX1262 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.pinedio-usb-sx1262 b/variants/linux/addon-radio-ini/meshcored.ini.pinedio-usb-sx1262 new file mode 100644 index 0000000000..69f82af308 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.pinedio-usb-sx1262 @@ -0,0 +1,17 @@ +# meshcored.ini - Pinedio USB SX1262 + +# Hardware config (read on every startup): +spidev = ch341 +lora_irq_pin = 10 +lora_nss_pin = 0 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = Pinedio USB SX1262 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.rpi-e22-900m22s-mini b/variants/linux/addon-radio-ini/meshcored.ini.rpi-e22-900m22s-mini new file mode 100644 index 0000000000..907d9f2585 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-e22-900m22s-mini @@ -0,0 +1,22 @@ +# meshcored.ini - MeshAdv Mini E22-900M22S + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 16 +lora_reset_pin = 24 +lora_nss_pin = 8 +lora_busy_pin = 20 +lora_rxen_pin = 12 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = MeshAdv Mini E22-900M22S +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.rpi-e22-900m30s b/variants/linux/addon-radio-ini/meshcored.ini.rpi-e22-900m30s new file mode 100644 index 0000000000..7e943cc734 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-e22-900m30s @@ -0,0 +1,22 @@ +# meshcored.ini - MeshAdv-Pi E22-900M30S + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 16 +lora_reset_pin = 18 +lora_nss_pin = 21 +lora_busy_pin = 20 +lora_rxen_pin = 12 +lora_txen_pin = 13 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +lora_tx_power = 22 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = MeshAdv-Pi E22-900M30S +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot1 new file mode 100644 index 0000000000..34cf8391e9 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot1 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13300 Slot 1 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 22 +lora_reset_pin = 16 +lora_busy_pin = 24 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 12, 13 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13300 Slot 1 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot2 new file mode 100644 index 0000000000..13efe3e4a2 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot2 @@ -0,0 +1,19 @@ +# meshcored.ini - RAK6421 + RAK13300 Slot 2 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.1 +lora_irq_pin = 18 +lora_reset_pin = 24 +lora_busy_pin = 19 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tx_power = 22 +# Source Enable_Pins: 26, 23 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13300 Slot 2 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot1 new file mode 100644 index 0000000000..e7cb52f304 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot1 @@ -0,0 +1,22 @@ +# meshcored.ini - RAK6421 + RAK13302 Slot 1 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 22 +lora_reset_pin = 16 +lora_busy_pin = 24 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source Enable_Pins: 12, 13 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13302 Slot 1 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot2 new file mode 100644 index 0000000000..88faa62d26 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot2 @@ -0,0 +1,20 @@ +# meshcored.ini - RAK6421 + RAK13302 Slot 2 + +# Hardware config (read on every startup): +spidev = /dev/spidev0.1 +lora_irq_pin = 18 +lora_reset_pin = 24 +lora_busy_pin = 19 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tx_power = 22 +# Source Enable_Pins: 26, 23 +# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = RAK6421 + RAK13302 Slot 2 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.usb-e22-adapter b/variants/linux/addon-radio-ini/meshcored.ini.usb-e22-adapter new file mode 100644 index 0000000000..de1fe5951e --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.usb-e22-adapter @@ -0,0 +1,24 @@ +# meshcored.ini - meshtoad-e22 + +# Hardware config (read on every startup): +spidev = ch341 +lora_irq_pin = 6 +lora_reset_pin = 2 +lora_nss_pin = 0 +lora_busy_pin = 4 +lora_rxen_pin = 1 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source USB_PID: 0x5512 +# Source USB_VID: 0x1A86 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = meshtoad-e22 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.usb-meshstick-sx1262 b/variants/linux/addon-radio-ini/meshcored.ini.usb-meshstick-sx1262 new file mode 100644 index 0000000000..eb159ca04e --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.usb-meshstick-sx1262 @@ -0,0 +1,24 @@ +# meshcored.ini - meshstick-1262 + +# Hardware config (read on every startup): +spidev = ch341 +lora_irq_pin = 6 +lora_reset_pin = 2 +lora_nss_pin = 0 +lora_busy_pin = 4 +lora_rxen_pin = 1 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source USB_PID: 0x5512 +# Source USB_VID: 0x1A86 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = meshstick-1262 +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.usb-umesh-sx1262-30dbm b/variants/linux/addon-radio-ini/meshcored.ini.usb-umesh-sx1262-30dbm new file mode 100644 index 0000000000..0d64c05a8a --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.usb-umesh-sx1262-30dbm @@ -0,0 +1,25 @@ +# meshcored.ini - umesh-1262-30dbm + +# Hardware config (read on every startup): +spidev = ch341 +lora_irq_pin = 6 +lora_reset_pin = 1 +lora_nss_pin = 0 +lora_busy_pin = 4 +lora_rxen_pin = 2 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 22 +# Source TX_GAIN_LORA: [12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 8, 8, 7] +# Source USB_PID: 0x5512 +# Source USB_VID: 0x1A86 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = umesh-1262-30dbm +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.zebra-hat-1w b/variants/linux/addon-radio-ini/meshcored.ini.zebra-hat-1w new file mode 100644 index 0000000000..1ce8a2b982 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.zebra-hat-1w @@ -0,0 +1,21 @@ +# meshcored.ini - ZebraHat 1W + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 22 +lora_reset_pin = 17 +lora_nss_pin = 24 +lora_busy_pin = 27 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 18 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = ZebraHat 1W +admin_password = changeme +lat = 0.0 +lon = 0.0 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.zebra-hat-2w b/variants/linux/addon-radio-ini/meshcored.ini.zebra-hat-2w new file mode 100644 index 0000000000..8988f8eed6 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.zebra-hat-2w @@ -0,0 +1,22 @@ +# meshcored.ini - ZebraHat 2W + +# Hardware config (read on every startup): +spidev = /dev/spidev0.0 +lora_irq_pin = 22 +lora_reset_pin = 17 +lora_nss_pin = 24 +lora_busy_pin = 27 +lora_rxen_pin = 25 +lora_freq = 869.618 +lora_bw = 62.5 +lora_sf = 8 +lora_cr = 8 +lora_tcxo = 1.8 +dio2_as_rf_switch = 1 +lora_tx_power = 8 + +# First-run node defaults (ignored after first boot; use CLI to change): +advert_name = ZebraHat 2W +admin_password = changeme +lat = 0.0 +lon = 0.0 From 0a976db91d9dffe94d108c1ef046778ec31919eb Mon Sep 17 00:00:00 2001 From: Littleaton <55291686+Littleaton@users.noreply.github.com> Date: Tue, 9 Jun 2026 13:14:29 -0600 Subject: [PATCH 14/14] added radio examples. --- .../meshcored.ini.ecb41-pge-rak6421-rak13300-slot1 | 2 -- .../meshcored.ini.ecb41-pge-rak6421-rak13300-slot2 | 2 -- .../meshcored.ini.ecb41-pge-rak6421-rak13302-slot1 | 2 -- .../meshcored.ini.ecb41-pge-rak6421-rak13302-slot2 | 2 -- .../meshcored.ini.lyra-zero-rak6421-rak13300-slot1 | 2 -- .../meshcored.ini.lyra-zero-rak6421-rak13300-slot2 | 2 -- .../meshcored.ini.lyra-zero-rak6421-rak13302-slot1 | 2 -- .../meshcored.ini.lyra-zero-rak6421-rak13302-slot2 | 2 -- .../meshcored.ini.ok3506-rak6421-rak13300-slot1 | 2 -- .../meshcored.ini.ok3506-rak6421-rak13300-slot2 | 2 -- .../meshcored.ini.ok3506-rak6421-rak13302-slot1 | 2 -- .../meshcored.ini.ok3506-rak6421-rak13302-slot2 | 2 -- .../addon-radio-ini/meshcored.ini.ok3506-waveshare-sx1262 | 1 - .../addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot1 | 2 -- .../addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot2 | 1 - .../addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot1 | 2 -- .../addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot2 | 2 -- variants/linux/addon-radio-ini/meshcored.ini.usb-e22-adapter | 2 -- .../linux/addon-radio-ini/meshcored.ini.usb-meshstick-sx1262 | 2 -- .../linux/addon-radio-ini/meshcored.ini.usb-umesh-sx1262-30dbm | 3 --- 20 files changed, 39 deletions(-) diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot1 index 217c7528cc..0f428d1ac6 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot1 +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot1 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 43@gpiochip1:11, 57@gpiochip1:25 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13300 Slot 1 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot2 index a1f241fc52..a31ed2d793 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot2 +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot2 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 2@gpiochip0:2, 50@gpiochip1:18 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13300 Slot 2 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot1 index 62a72ae734..89c9d62fdf 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot1 +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot1 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 43@gpiochip1:11, 57@gpiochip1:25 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13302 Slot 1 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot2 index 9f70270c5c..c5247c9245 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot2 +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot2 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 2@gpiochip0:2, 50@gpiochip1:18 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13302 Slot 2 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot1 index e3d7f73e52..a370bfd370 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot1 +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot1 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 58@gpiochip1:26, 50@gpiochip1:18 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13300 Slot 1 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot2 index f3c3701a20..75728ccf7c 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot2 +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot2 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 51@gpiochip1:19, 13@gpiochip0:13 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13300 Slot 2 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot1 index e3d7f73e52..a370bfd370 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot1 +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot1 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 58@gpiochip1:26, 50@gpiochip1:18 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13300 Slot 1 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot2 index f3c3701a20..75728ccf7c 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot2 +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot2 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 51@gpiochip1:19, 13@gpiochip0:13 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13300 Slot 2 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot1 index a7ac9da26e..a4d9fc5c66 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot1 +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot1 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 2@gpiochip0:2, 3@gpiochip0:3 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13300 Slot 1 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot2 index efb1b28bfa..71ce4c1575 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot2 +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot2 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 106@gpiochip3:10, 103@gpiochip3:7 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13300 Slot 2 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot1 index 07ad4f8d84..76f71f1001 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot1 +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot1 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 2@gpiochip0:2, 3@gpiochip0:3 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13302 Slot 1 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot2 index fe01f96e12..7b8f44072a 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot2 +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot2 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 106@gpiochip3:10, 103@gpiochip3:7 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13302 Slot 2 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-waveshare-sx1262 b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-waveshare-sx1262 index 7553e52478..d3a01349cf 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.ok3506-waveshare-sx1262 +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-waveshare-sx1262 @@ -12,7 +12,6 @@ lora_sf = 8 lora_cr = 8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source SX126X_ANT_SW is not modeled in meshcored.ini. # First-run node defaults (ignored after first boot; use CLI to change): advert_name = Waveshare SX1262 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot1 index 34cf8391e9..aa005f9e53 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot1 +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot1 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 12, 13 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13300 Slot 1 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot2 index 13efe3e4a2..509d8114c1 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot2 +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot2 @@ -10,7 +10,6 @@ lora_bw = 62.5 lora_sf = 8 lora_cr = 8 lora_tx_power = 22 -# Source Enable_Pins: 26, 23 # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13300 Slot 2 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot1 b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot1 index e7cb52f304..bc534ab4d3 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot1 +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot1 @@ -12,8 +12,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source Enable_Pins: 12, 13 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13302 Slot 1 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot2 b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot2 index 88faa62d26..82be27bd28 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot2 +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot2 @@ -10,8 +10,6 @@ lora_bw = 62.5 lora_sf = 8 lora_cr = 8 lora_tx_power = 22 -# Source Enable_Pins: 26, 23 -# Source TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8] # First-run node defaults (ignored after first boot; use CLI to change): advert_name = RAK6421 + RAK13302 Slot 2 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.usb-e22-adapter b/variants/linux/addon-radio-ini/meshcored.ini.usb-e22-adapter index de1fe5951e..58a676c4a3 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.usb-e22-adapter +++ b/variants/linux/addon-radio-ini/meshcored.ini.usb-e22-adapter @@ -14,8 +14,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source USB_PID: 0x5512 -# Source USB_VID: 0x1A86 # First-run node defaults (ignored after first boot; use CLI to change): advert_name = meshtoad-e22 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.usb-meshstick-sx1262 b/variants/linux/addon-radio-ini/meshcored.ini.usb-meshstick-sx1262 index eb159ca04e..75aa74682c 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.usb-meshstick-sx1262 +++ b/variants/linux/addon-radio-ini/meshcored.ini.usb-meshstick-sx1262 @@ -14,8 +14,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source USB_PID: 0x5512 -# Source USB_VID: 0x1A86 # First-run node defaults (ignored after first boot; use CLI to change): advert_name = meshstick-1262 diff --git a/variants/linux/addon-radio-ini/meshcored.ini.usb-umesh-sx1262-30dbm b/variants/linux/addon-radio-ini/meshcored.ini.usb-umesh-sx1262-30dbm index 0d64c05a8a..2875fe9dd5 100644 --- a/variants/linux/addon-radio-ini/meshcored.ini.usb-umesh-sx1262-30dbm +++ b/variants/linux/addon-radio-ini/meshcored.ini.usb-umesh-sx1262-30dbm @@ -14,9 +14,6 @@ lora_cr = 8 lora_tcxo = 1.8 dio2_as_rf_switch = 1 lora_tx_power = 22 -# Source TX_GAIN_LORA: [12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 8, 8, 7] -# Source USB_PID: 0x5512 -# Source USB_VID: 0x1A86 # First-run node defaults (ignored after first boot; use CLI to change): advert_name = umesh-1262-30dbm