diff --git a/boards/linux.json b/boards/linux.json new file mode 100644 index 0000000000..8a120faaca --- /dev/null +++ b/boards/linux.json @@ -0,0 +1,22 @@ +{ + "build": { + "arduino": { + }, + "core": "linux", + "extra_flags": [ + ], + "hwids": [], + "mcu": "arm64", + "variant": "linux" + }, + "connectivity": ["wifi", "bluetooth"], + "debug": {}, + "frameworks": ["arduino", "ardulinux", "linux"], + "name": "Linux", + "url": "https://github.com/l5yth/ardulinux", + "upload": { + "maximum_ram_size": 0, + "maximum_size": 0 + }, + "vendor": "Linux" +} diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 096907494b..34cec242e9 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(ARDULINUX_PLATFORM) return _fs->open(fname, "a"); #else return _fs->open(fname, "a", true); @@ -927,6 +927,20 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc 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 + 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; + _prefs.rx_boosted_gain = board.config.rx_boosted_gain; +#endif // load persisted prefs _cli.loadPrefs(_fs); acl.load(_fs, self_id); @@ -1004,6 +1018,9 @@ bool MyMesh::formatFileSystem() { return LittleFS.format(); #elif defined(ESP32) return SPIFFS.format(); +#elif defined(ARDULINUX_PLATFORM) + // not supported on linux + return false; #else #error "need to implement file system erase" return false; @@ -1155,9 +1172,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(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 7597c6c6f6..6761f9a40d 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -11,6 +11,8 @@ #include #elif defined(ESP32) #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 2ce056f521..2d4b69890b 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -69,6 +69,12 @@ void setup() { fs = &LittleFS; 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) + fs = &ArduLinuxFS; + IdentityStore store(ArduLinuxFS, "/identity"); + store.begin(); #else #error "need to define filesystem" #endif diff --git a/platformio.ini b/platformio.ini index e16f7b8304..a94bf43587 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.2 +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/src/helpers/ClientACL.cpp b/src/helpers/ClientACL.cpp index 1282382737..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) + #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 b78ad6ebd6..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) +#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 dc85d69cdd..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) +#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) +#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 d0d7ee457e..4a1e22cccf 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(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 7b8399e260..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) + #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 d327931fde..6da0d741ab 100644 --- a/src/helpers/TxtDataHelpers.cpp +++ b/src/helpers/TxtDataHelpers.cpp @@ -1,4 +1,7 @@ #include "TxtDataHelpers.h" +#if defined(ARDULINUX_PLATFORM) + #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(ARDULINUX_PLATFORM) + sprintf(p, "%" PRId32, int_part); +#else ltoa(int_part, p, 10); +#endif while (*p) p++; } diff --git a/src/helpers/radiolib/LinuxSX1262.h b/src/helpers/radiolib/LinuxSX1262.h new file mode 100644 index 0000000000..bed50ee92e --- /dev/null +++ b/src/helpers/radiolib/LinuxSX1262.h @@ -0,0 +1,54 @@ +#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; + } + + 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 new file mode 100644 index 0000000000..17e168c70f --- /dev/null +++ b/src/helpers/radiolib/LinuxSX1262Wrapper.h @@ -0,0 +1,39 @@ +#pragma once + +#include "LinuxSX1262.h" +#include "RadioLibWrappers.h" + +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(); + } + 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); + } + 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/99-meshcore.rules b/variants/linux/99-meshcore.rules new file mode 100644 index 0000000000..4071c847f6 --- /dev/null +++ b/variants/linux/99-meshcore.rules @@ -0,0 +1,5 @@ +# 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/LinuxBoard.cpp b/variants/linux/LinuxBoard.cpp new file mode 100644 index 0000000000..c218a2f113 --- /dev/null +++ b/variants/linux/LinuxBoard.cpp @@ -0,0 +1,177 @@ +#include +#include +#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 ARDULINUX_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 (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 (...) { + 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 + return 0; +#endif +} + +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); + SPI.begin(config.spidev, 2000000); + + 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); + + int failures = 0; + if (config.lora_nss_pin != RADIOLIB_NC) { + 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, config.lora_gpiochip, config.lora_busy_pin); + } + if (config.lora_irq_pin != RADIOLIB_NC) { + 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, config.lora_gpiochip, config.lora_reset_pin); + } + if (config.lora_rxen_pin != RADIOLIB_NC) { + 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, config.lora_gpiochip, config.lora_txen_pin); + } + + if (failures > 0) { + printf("FATAL: %d GPIO pin(s) failed to bind; cannot start radio.\n", failures); + exit(1); + } +} + +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); + + // 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_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); + 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 = 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); + 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..f3044d7d98 --- /dev/null +++ b/variants/linux/LinuxBoard.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#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"; + char* lora_gpiochip = "gpiochip0"; + + 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/README.md b/variants/linux/README.md new file mode 100644 index 0000000000..581aedfca0 --- /dev/null +++ b/variants/linux/README.md @@ -0,0 +1,234 @@ +# 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. + +## Hardware + +- Raspberry Pi (any model with SPI) +- 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 + +**Dependencies** (install on the build machine and on the Pi): + +```sh +# Arch Linux +sudo pacman -S pkgconf libgpiod i2c-tools bluez-libs libuv + +# Debian/Raspberry Pi OS +sudo apt install pkg-config 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`. 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: + +```sh +# Arch Linux +sudo pacman -S platformio-core # 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/meshcored +``` + +Alternatively, build directly with PlatformIO (no version metadata): + +```sh +FIRMWARE_VERSION=dev pio run -e linux_repeater +``` + +## Setup + +### 1. Install the binary + +```sh +sudo install -m 755 .pio/build/linux_repeater/meshcored /usr/bin/meshcored +``` + +### 2. Create the config file + +Two ready-made templates are provided in `variants/linux/`: + +| Template | Hardware | +|----------|----------| +| `meshcored.ini.pow-sx1262` | RPi Zero 2W + PoW SX1262 HAT | +| `meshcored.ini.waveshare` | RPi 3/4/5 + Waveshare SX1262 LoRa HAT | + +```sh +# 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 +``` + +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. + +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) | +| `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` | `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 | + +### 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, 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. +> +> **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 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 +``` + +Confirm the device nodes are now group-owned by `meshcore`: + +```sh +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 + +`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: + +```sh +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' +``` + +**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 -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 +sudo systemctl enable --now meshcored +sudo journalctl -u meshcored -f +``` + +> 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 + +Node name, password, and location can be changed via the serial CLI after first boot: + +``` +set name +set password +set lat +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: + +```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. +- **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/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..0f428d1ac6 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot1 @@ -0,0 +1,20 @@ +# 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 + +# 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..a31ed2d793 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13300-slot2 @@ -0,0 +1,20 @@ +# 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 + +# 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..89c9d62fdf --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-rak6421-rak13302-slot1 @@ -0,0 +1,20 @@ +# 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 + +# 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..c5247c9245 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ecb41-pge-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 = 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 + +# 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..a370bfd370 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot1 @@ -0,0 +1,20 @@ +# 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 + +# 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..75728ccf7c --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13300-slot2 @@ -0,0 +1,20 @@ +# 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 + +# 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..a370bfd370 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot1 @@ -0,0 +1,20 @@ +# 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 + +# 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..75728ccf7c --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.lyra-zero-rak6421-rak13302-slot2 @@ -0,0 +1,20 @@ +# 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 + +# 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..a4d9fc5c66 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot1 @@ -0,0 +1,20 @@ +# 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 + +# 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..71ce4c1575 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13300-slot2 @@ -0,0 +1,20 @@ +# 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 + +# 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..76f71f1001 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-rak6421-rak13302-slot1 @@ -0,0 +1,20 @@ +# 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 + +# 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..7b8f44072a --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-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 = 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 + +# 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..d3a01349cf --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.ok3506-waveshare-sx1262 @@ -0,0 +1,20 @@ +# 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 + +# 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..aa005f9e53 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot1 @@ -0,0 +1,20 @@ +# 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 + +# 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..509d8114c1 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13300-slot2 @@ -0,0 +1,18 @@ +# 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 + +# 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..bc534ab4d3 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot1 @@ -0,0 +1,20 @@ +# 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 + +# 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..82be27bd28 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.rpi-rak6421-rak13302-slot2 @@ -0,0 +1,18 @@ +# 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 + +# 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..58a676c4a3 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.usb-e22-adapter @@ -0,0 +1,22 @@ +# 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 + +# 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..75aa74682c --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.usb-meshstick-sx1262 @@ -0,0 +1,22 @@ +# 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 + +# 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..2875fe9dd5 --- /dev/null +++ b/variants/linux/addon-radio-ini/meshcored.ini.usb-umesh-sx1262-30dbm @@ -0,0 +1,22 @@ +# 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 + +# 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 diff --git a/variants/linux/meshcored.ini b/variants/linux/meshcored.ini new file mode 100644 index 0000000000..d7b17d790c --- /dev/null +++ b/variants/linux/meshcored.ini @@ -0,0 +1,29 @@ +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_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 +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.ini.pow-sx1262 b/variants/linux/meshcored.ini.pow-sx1262 new file mode 100644 index 0000000000..96b5040a9f --- /dev/null +++ b/variants/linux/meshcored.ini.pow-sx1262 @@ -0,0 +1,23 @@ +# 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_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 +# 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 + diff --git a/variants/linux/meshcored.ini.waveshare b/variants/linux/meshcored.ini.waveshare new file mode 100644 index 0000000000..45b03bf82f --- /dev/null +++ b/variants/linux/meshcored.ini.waveshare @@ -0,0 +1,25 @@ +# 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_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 +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 +# 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): +advert_name = Waveshare Linux Repeater +admin_password = changeme +lat = 0.0 +lon = 0.0 + diff --git a/variants/linux/meshcored.service b/variants/linux/meshcored.service new file mode 100644 index 0000000000..72d42f0a76 --- /dev/null +++ b/variants/linux/meshcored.service @@ -0,0 +1,23 @@ +# /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 +ProtectSystem=strict +ProtectHome=yes +PrivateTmp=yes +NoNewPrivileges=yes +StateDirectory=meshcore + +[Install] +WantedBy=multi-user.target diff --git a/variants/linux/platformio.ini b/variants/linux/platformio.ini new file mode 100644 index 0000000000..8f42ce46b1 --- /dev/null +++ b/variants/linux/platformio.ini @@ -0,0 +1,22 @@ +[env:linux] +extends = linux_base +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 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..41609fc32e --- /dev/null +++ b/variants/linux/target.cpp @@ -0,0 +1,54 @@ +#include +#include "target.h" + +class ArduLinuxHal : public ArduinoHal +{ +public: + ArduLinuxHal(SPIClass &spi, SPISettings spiSettings) : ArduinoHal(spi, spiSettings){}; + void spiTransfer(uint8_t *out, size_t len, uint8_t *in) { + memcpy(in, out, len); + spi->transfer(in, len); + } +}; + +LinuxBoard board; + +SPISettings spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0); +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); + +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();