Skip to content

Add native Linux/Ardulinux repeater target - #2025

Open
l5yth wants to merge 16 commits into
meshcore-dev:devfrom
l5yth:linux
Open

Add native Linux/Ardulinux repeater target#2025
l5yth wants to merge 16 commits into
meshcore-dev:devfrom
l5yth:linux

Conversation

@l5yth

@l5yth l5yth commented Mar 14, 2026

Copy link
Copy Markdown

This is a continuation of #1137 (and #395) to fix #619. The goal is to keep this diff clean and rebased on upstream so it can eventually be merged into the MeshCore firmware, but I'm also happy to maintain the fork until then.

It adds a linux_repeater PlatformIO environment that compiles the simple repeater firmware for Linux SBCs (Raspberry Pi Zero, 3, 4, 5) using the ArduLinux Arduino-compatibility layer. Compared to #1137, it no longer depends on Meshtastic's Portduino framework, and repeater name/password/etc. no longer need compile-time constants, they're parsed from an INI file on first run and changed thereafter via CLI or remote config.

New files

  • variants/linux/, LinuxBoard (SPI + libgpiod GPIO init), LinuxSX1262/LinuxSX1262Wrapper (RadioLib SX1262 driver), target.cpp/h, PlatformIO config, INI templates, systemd unit, and udev rules
  • boards/linux.json, board descriptor

Shared-helper changes (guarded by ARDULINUX_PLATFORM)

  • IdentityStore, ClientACL, CommonCLI, RegionMap: use the 2-arg fs::open(path, mode) (string-mode, as on RP2040) since the ArduLinux FS has no 3-arg overload
  • TxtDataHelpers: use sprintf/PRId32 instead of ltoa (not available)
  • MyMesh: filesystem init, formatFileSystem() stub, and saveIdentity() path for Linux; apply INI config as first-run defaults before loadPrefs()

Real hardware GPIO/I2C in LinuxBoard is compiled in automatically (guarded by ARDULINUX_HARDWARE, which ArduLinux defines when libgpiod is detected via pkg-config); without it the build falls back to ArduLinux's simulated devices.

The following repeater is live and in service using this linux branch on a Raspberry Pi 4B with a LoRa hat: MeshCoreD LinuxRepeater (63, region BER)

I'll attach screenshots from remote management as manual testing did not reveal any issues.

Screenshot_20260314-191150 Screenshot_20260314-191141

/cc @ggodlewski

@l5yth

l5yth commented Mar 15, 2026

Copy link
Copy Markdown
Author

The benefit of Portduino is it "just works" (tm) with very little overhead on our end.

If we prefer to not use an external framework, we can also implement a minimal Arduino abstraction layer ourselves. I gave it a go (unreviewed yet) and it only adds ~600-800 lines of code to the Linux variant.

l5yth#3

Please let me know if you prefer it this way over the rather minimalist Portduino variant.

@ggodlewski

Copy link
Copy Markdown

The benefit of Portduino is it "just works" (tm) with very little overhead on our end.

If we prefer to not use an external framework, we can also implement a minimal Arduino abstraction layer ourselves. I gave it a go (unreviewed yet) and it only adds ~600-800 lines of code to the Linux variant.

l5yth#3

Please let me know if you prefer it this way over the rather minimalist Portduino variant.

I like it more. Portduino did not allow me to run 2 concurrent meshcore services at once (with separate config and workdir).

@liamcottle

liamcottle commented Mar 15, 2026

Copy link
Copy Markdown
Member

I haven't read through all the comments and discussions on this, but my personal preference would be to avoid depending on a Meshtastic owned project.

Portduino does look like a neat project, but I tend to avoid GPL licensed code, and we've had issues with using some of their GPL licensed code in MIT related projects in the past.

Plus, it would be good to not have to rely on an external project getting updated if we can do it in house with a small wrapper/helper class to shim out the Arduino methods as you suggested.

@l5yth

l5yth commented Mar 16, 2026

Copy link
Copy Markdown
Author

Thanks, I appreciate the early feedback.

@ggodlewski are you still using your Pi Zero 2W? Mind giving this a test? It is upgraded to 1.14.0 now and I added your config to a ini file named .pow-sx1262 - it does read the ini on first run, so you don't need to set advert name and admin password as build flag anymore. It might need to recreate the com_prefs in the working dir.

@liamcottle Thanks for the comment. I spent some more time replacing the Portduino framework but it turns out it's not as straight-forward as I hoped it would be. As a middle ground, what do you think about hosting our own minimalist 'portduino' framework in the meshcore-dev organization (meshduino?)? Would give us a bit more control and we can iterate faster without needing to add too much code to the firmware itself.

Comment thread variants/linux/LinuxBoard.cpp Outdated
@l5yth l5yth changed the title Add native Linux/Portduino repeater target Add native Linux/Ardulinux repeater target May 31, 2026
@Littleaton

Littleaton commented Jun 5, 2026

Copy link
Copy Markdown

I would be happy to help you test any of this I have many pi/nebra setups i could use. @l5yth

@l5yth

l5yth commented Jun 5, 2026

Copy link
Copy Markdown
Author

@liamcottle this is ready for review. I removed platform native, framework portduino, and all monkey-patched meshtastic modules. Instead, I created a clean-slate platform for linux support of arduino API with only upstream dependencies (no copy-paste, no drive-by-patching).

Benefit is, that this allowed me to make meshcored on linux a proper cli daemon.

❯ meshcored --help 
meshcored linux-1.15.1-6c50a196
a meshcore daemon for linux

Usage: meshcored [OPTION...] 

  -d, --fsdir=DIR            The directory to use as the virtual filesystem
  -e, --erase                Erase virtual filesystem before use
      --usage                Give a short usage message
  -?, --help                 Give this help list
  -V, --version              Print program version

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Report bugs to https://github.com/meshcore-dev/MeshCore.

@Littleaton please do. I just finished the work on this. working branch is linux - and instructions are in the linux variant README. all you need to do is double check pin mapping on your hardware. let me know.

@Littleaton

Copy link
Copy Markdown

@liamcottle this is ready for review. I removed platform native, framework portduino, and all monkey-patched meshtastic modules. Instead, I created a clean-slate platform for linux support of arduino API with only upstream dependencies (no copy-paste, no drive-by-patching).

Benefit is, that this allowed me to make meshcored on linux a proper cli daemon.

❯ meshcored --help 
meshcored linux-1.15.1-6c50a196
a meshcore daemon for linux

Usage: meshcored [OPTION...] 

  -d, --fsdir=DIR            The directory to use as the virtual filesystem
  -e, --erase                Erase virtual filesystem before use
      --usage                Give a short usage message
  -?, --help                 Give this help list
  -V, --version              Print program version

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Report bugs to https://github.com/meshcore-dev/MeshCore.

@Littleaton please do. I just finished the work on this. working branch is linux - and instructions are in the linux variant README. all you need to do is double check pin mapping on your hardware. let me know.

very cool id be happy to contrib all the diff radio yaml/config files that are used, do you have this type of thing setup?

@Littleaton

Copy link
Copy Markdown

Also are you on discord so i can discuss/talk faster? :P @l5yth

@l5yth

l5yth commented Jun 5, 2026

Copy link
Copy Markdown
Author

I don't have Discord, sorry. Feel free to suggest new configs directly in my downstream repo, wdyt?

@Littleaton

Copy link
Copy Markdown

ok can do.

@rgrizzell

rgrizzell commented Jul 23, 2026

Copy link
Copy Markdown

I was able to compile this on Fedora 43 using the following steps. This provides an x86_64 binary, but also works when cross-compiling for arm64. I'm planning to follow-up with some real hardware testing.

toolbox create --release 43 meshcore
toolbox enter meshcore
sudo dnf install -y platformio gcc-c++ libgpiod-devel libi2c-devel bluez-libs-devel libuv-devel
pio run -e linux_repeater
Click to show build log
Processing linux_repeater (platform:
git+https://github.com/l5yth/ardulinux.git#v0.2.2; framework: arduino; board: linux)
--------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/ardulinux/linux.html
PLATFORM: ArduLinux (0.2.2+sha.96e424d) > Linux
HARDWARE: ARM64 0B RAM, 0B Flash
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 12 compatible libraries
Scanning dependencies...
Dependency Graph
|-- Crypto @ 0.4.0
|-- Adafruit seesaw Library @ 1.7.9
|-- CayenneLPP @ 1.6.1
|-- RTClib @ 2.1.4
|-- RadioLib @ 7.4.0
|-- Melopero RV3028 @ 1.2.0
|-- ed25519
|-- nrf52
Building in release mode
Compiling .pio/build/linux_repeater/FrameworkArduinoAPI/Common.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduinoAPI/IPAddress.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduinoAPI/Print.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduinoAPI/Stream.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduinoAPI/String.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/AppInfo.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/ArduLinuxGPIO.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/FS/ArduLinuxFS.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/FS/FS.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/FS/vfs_api.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/HardwareSPIStubs.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/Utility.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/XDGDirs.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/dtostrf.c.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/itoa.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/linux/LinuxCommon.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/linux/LinuxHardwareI2C.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/linux/LinuxHardwareSPI.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/linux/LinuxSerial.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/linux/gpio/LinuxGPIOPin.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/linux/millis.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/logging.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/main.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/main_entry.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/simulated/SimCommon.cpp.o
Compiling .pio/build/linux_repeater/FrameworkArduLinux/simulated/SimHardwareSPI.cpp.o
Compiling .pio/build/linux_repeater/examples/simple_repeater/MyMesh.cpp.o
Compiling .pio/build/linux_repeater/examples/simple_repeater/UITask.cpp.o
Compiling .pio/build/linux_repeater/examples/simple_repeater/main.cpp.o
Compiling .pio/build/linux_repeater/variants/linux/LinuxBoard.cpp.o
Compiling .pio/build/linux_repeater/variants/linux/target.cpp.o
Compiling .pio/build/linux_repeater/src/Dispatcher.cpp.o
Compiling .pio/build/linux_repeater/src/Identity.cpp.o
Compiling .pio/build/linux_repeater/src/Mesh.cpp.o
Compiling .pio/build/linux_repeater/src/Packet.cpp.o
Compiling .pio/build/linux_repeater/src/Utils.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/AdvertDataHelpers.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/ArduinoSerialInterface.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/AutoDiscoverRTCClock.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/BaseChatMesh.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/ClientACL.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/CommonCLI.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/ESP32Board.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/IdentityStore.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/NRF52Board.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/RTC_RX8130CE.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/RegionMap.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/StaticPoolPacketManager.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/TransportKeyStore.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/TxtDataHelpers.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/bridges/BridgeBase.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/bridges/RS232Bridge.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/radiolib/RadioLibWrappers.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/sensors/EnvironmentSensorManager.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/sensors/RAK12035_SoilMoisture.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/ui/GenericVibration.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/ui/MomentaryButton.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/ui/OLEDDisplayFonts.cpp.o
Compiling .pio/build/linux_repeater/src/helpers/ui/buzzer.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/AES128.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/AES192.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/AES256.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/AESCommon.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/AESEsp32.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/AuthenticatedCipher.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/BLAKE2b.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/BLAKE2s.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/BigNumberUtil.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/BlockCipher.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/CTR.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/ChaCha.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/ChaChaPoly.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/Cipher.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/Crypto.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/Curve25519.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/EAX.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/Ed25519.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/GCM.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/GF128.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/GHASH.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/HKDF.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/Hash.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/KeccakCore.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/NoiseSource.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/OMAC.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/P521.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/Poly1305.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/RNG.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/SHA224.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/SHA256.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/SHA3.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/SHA384.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/SHA512.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/SHAKE.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/XOF.cpp.o
Compiling .pio/build/linux_repeater/liba53/Crypto/XTS.cpp.o
Compiling .pio/build/linux_repeater/libadc/Adafruit BusIO/Adafruit_BusIO_Register.cpp.o
Compiling .pio/build/linux_repeater/libadc/Adafruit BusIO/Adafruit_GenericDevice.cpp.o
Compiling .pio/build/linux_repeater/libadc/Adafruit BusIO/Adafruit_I2CDevice.cpp.o
Compiling .pio/build/linux_repeater/libadc/Adafruit BusIO/Adafruit_SPIDevice.cpp.o
Compiling .pio/build/linux_repeater/lib92d/Adafruit seesaw Library/Adafruit_Crickit.cpp.o
Compiling .pio/build/linux_repeater/lib92d/Adafruit seesaw Library/Adafruit_NeoKey_1x4.cpp.o
Compiling .pio/build/linux_repeater/lib92d/Adafruit seesaw Library/Adafruit_NeoTrellis.cpp.o
Compiling .pio/build/linux_repeater/lib92d/Adafruit seesaw Library/Adafruit_TFTShield18.cpp.o
Compiling .pio/build/linux_repeater/lib92d/Adafruit seesaw Library/Adafruit_miniTFTWing.cpp.o
Compiling .pio/build/linux_repeater/lib92d/Adafruit seesaw Library/Adafruit_seesaw.cpp.o
Compiling .pio/build/linux_repeater/lib92d/Adafruit seesaw Library/seesaw_neopixel.cpp.o
Archiving .pio/build/linux_repeater/liba53/libCrypto.a
Indexing .pio/build/linux_repeater/liba53/libCrypto.a
Compiling .pio/build/linux_repeater/lib92d/Adafruit seesaw Library/seesaw_servo.cpp.o
Compiling .pio/build/linux_repeater/lib92d/Adafruit seesaw Library/seesaw_spectrum.cpp.o
Archiving .pio/build/linux_repeater/libadc/libAdafruit BusIO.a
Compiling .pio/build/linux_repeater/lib8b7/CayenneLPP/CayenneLPP.cpp.o
Indexing .pio/build/linux_repeater/libadc/libAdafruit BusIO.a
Compiling .pio/build/linux_repeater/lib8b7/CayenneLPP/CayenneLPPPolyline.cpp.o
Compiling .pio/build/linux_repeater/lib7d3/RTClib/RTC_DS1307.cpp.o
Compiling .pio/build/linux_repeater/lib7d3/RTClib/RTC_DS3231.cpp.o
Compiling .pio/build/linux_repeater/lib7d3/RTClib/RTC_Micros.cpp.o
Compiling .pio/build/linux_repeater/lib7d3/RTClib/RTC_Millis.cpp.o
Compiling .pio/build/linux_repeater/lib7d3/RTClib/RTC_PCF8523.cpp.o
Compiling .pio/build/linux_repeater/lib7d3/RTClib/RTC_PCF8563.cpp.o
Compiling .pio/build/linux_repeater/lib7d3/RTClib/RTClib.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/Hal.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/Module.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/hal/Arduino/ArduinoHal.cpp.o
Archiving .pio/build/linux_repeater/lib92d/libAdafruit seesaw Library.a
Indexing .pio/build/linux_repeater/lib92d/libAdafruit seesaw Library.a
Compiling .pio/build/linux_repeater/libb86/RadioLib/hal/RPiPico/PicoHal.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/CC1101/CC1101.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/LLCC68/LLCC68.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/LR11x0/LR1110.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/LR11x0/LR1120.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/LR11x0/LR1121.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/LR11x0/LR11x0.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/LR11x0/LR11x0_commands.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/LR11x0/LR11x0_crypto.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/LR11x0/LR11x0_gnss.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/LR11x0/LR11x0_wifi.cpp.o
Archiving .pio/build/linux_repeater/lib7d3/libRTClib.a
Indexing .pio/build/linux_repeater/lib7d3/libRTClib.a
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/RF69/RF69.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX123x/SX1231.cpp.o
Archiving .pio/build/linux_repeater/lib8b7/libCayenneLPP.a
Indexing .pio/build/linux_repeater/lib8b7/libCayenneLPP.a
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX123x/SX1233.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX126x/STM32WLx.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX126x/STM32WLx_Module.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX126x/SX1261.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX126x/SX1262.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX126x/SX1268.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX126x/SX126x.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX126x/SX126x_LR_FHSS.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX126x/SX126x_commands.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX126x/SX126x_config.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX127x/SX1272.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX127x/SX1273.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX127x/SX1276.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX127x/SX1277.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX127x/SX1278.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX127x/SX1279.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX127x/SX127x.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX128x/SX1280.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX128x/SX1281.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX128x/SX1282.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/SX128x/SX128x.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/Si443x/Si4430.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/Si443x/Si4431.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/Si443x/Si4432.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/Si443x/Si443x.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/modules/nRF24/nRF24.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/AFSK/AFSK.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/APRS/APRS.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/AX25/AX25.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/BellModem/BellModem.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/ExternalRadio/ExternalRadio.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/FSK4/FSK4.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/Hellschreiber/Hellschreiber.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/LoRaWAN/LoRaWAN.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/LoRaWAN/LoRaWANBands.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/Morse/Morse.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/Pager/Pager.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/PhysicalLayer/PhysicalLayer.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/Print/ITA2String.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/Print/Print.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/RTTY/RTTY.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/protocols/SSTV/SSTV.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/utils/CRC.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/utils/Cryptography.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/utils/FEC.cpp.o
Compiling .pio/build/linux_repeater/libb86/RadioLib/utils/Utils.cpp.o
Compiling .pio/build/linux_repeater/liba4b/Melopero RV3028/Melopero_RV3028.cpp.o
Compiling .pio/build/linux_repeater/lib3db/ed25519/add_scalar.c.o
Compiling .pio/build/linux_repeater/lib3db/ed25519/fe.c.o
Compiling .pio/build/linux_repeater/lib3db/ed25519/ge.c.o
Compiling .pio/build/linux_repeater/lib3db/ed25519/key_exchange.c.o
Compiling .pio/build/linux_repeater/lib3db/ed25519/keypair.c.o
Compiling .pio/build/linux_repeater/lib3db/ed25519/sc.c.o
Compiling .pio/build/linux_repeater/lib3db/ed25519/seed.c.o
Compiling .pio/build/linux_repeater/lib3db/ed25519/sha512.c.o
Compiling .pio/build/linux_repeater/lib3db/ed25519/sign.c.o
Compiling .pio/build/linux_repeater/lib3db/ed25519/verify.c.o
Archiving .pio/build/linux_repeater/lib3db/libed25519.a
Indexing .pio/build/linux_repeater/lib3db/libed25519.a
Archiving .pio/build/linux_repeater/libb86/libRadioLib.a
Indexing .pio/build/linux_repeater/libb86/libRadioLib.a
Archiving .pio/build/linux_repeater/liba4b/libMelopero RV3028.a
Indexing .pio/build/linux_repeater/liba4b/libMelopero RV3028.a
Linking .pio/build/linux_repeater/meshcored
============================================== [SUCCESS] Took 6.06 seconds ==============================================

Environment     Status    Duration
--------------  --------  ------------
linux_repeater  SUCCESS   00:00:06.062
============================================== 1 succeeded in 00:00:06.062 ==============================================
⬢ [rwgrizzell@toolbx MeshCore]$

The linux environment (not the board) is also defined, but it does not compile. It seems that it can be safely removed. The linux_base environment has everything necessary.

diff --git a/variants/linux/platformio.ini b/variants/linux/platformio.ini
index 8f42ce46..7dc65821 100644
--- a/variants/linux/platformio.ini
+++ b/variants/linux/platformio.ini
@@ -1,8 +1,3 @@
-[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 =

@l5yth

l5yth commented Jul 23, 2026

Copy link
Copy Markdown
Author

Thanks for the review. ✌️

The linux environment (not the board) is also defined, but it does not compile. It seems that it can be safely removed. The linux_base environment has everything necessary.

This is scaffholding for the companion code. See l5yth#16

I didn't merge the companion work yet because I want to get general feedback on the repeater target first.

@Littleaton

Copy link
Copy Markdown

I can also confirm the same findings as @rgrizzell and was able to run linux native on a linux system. I used Dietpi and was able to use a zebra hat, and a few other pi hat options and can confirm worked as a meshcore repeater.

@rgrizzell

rgrizzell commented Jul 26, 2026

Copy link
Copy Markdown

@l5yth I was doing a cross-compile for 32-bit ARM (armhf), and ran into a bug with the upstream Adafruit_BusIO library in Ardulinux. Once patched it compiles cleanly.

--- a/Adafruit_SPIDevice.h
+++ b/Adafruit_SPIDevice.h
@@ -80,7 +80,7 @@
 #elif (defined(__arm__) || defined(ARDUINO_FEATHER52)) &&                      \
     !defined(ARDUINO_ARCH_RP2040) && !defined(ARDUINO_SILABS) &&               \
     !defined(ARDUINO_UNOR4_MINIMA) && !defined(ARDUINO_UNOR4_WIFI) &&          \
-    !defined(PORTDUINO)
+    !defined(PORTDUINO) && !defined(ARDULINUX_PLATFORM)
 typedef volatile uint32_t BusIO_PortReg;
 typedef uint32_t BusIO_PortMask;
 #if !defined(__ASR6501__) && !defined(__ASR6502__)

Comment thread variants/linux/README.md Outdated
@oltaco

oltaco commented Aug 20, 2026

Copy link
Copy Markdown
Member

@l5yth This looks like it will be a good addition, I can see there's been a lot of work put into it already. Is there an intention to resolve the merge conflicts?

@rgrizzell

rgrizzell commented Aug 30, 2026

Copy link
Copy Markdown

I was able to rebase this branch on the latest commits in main. There were a few minor merge conflicts related to the switch from PORTDUINO to ARDULINUX midway through development, but easy enough to resolve.

https://github.com/rgrizzell/MeshCore/tree/linux-rebase

It builds cleanly with these two additional changes.

diff --git a/platformio.ini b/platformio.ini
index 2df022d8..bbccfaa5 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -147,6 +147,7 @@ build_src_filter =
   -<helpers/nrf52/*.cpp>
   -<helpers/stm32/*.cpp>
   -<helpers/bridges/ESPNowBridge.cpp>
+  -<helpers/ethernet/>
   -<helpers/*/*Display.cpp>
 lib_deps =
   ${env.lib_deps}
diff --git a/src/helpers/radiolib/LinuxSX1262Wrapper.h b/src/helpers/radiolib/LinuxSX1262Wrapper.h
index 17e168c7..58102d49 100644
--- a/src/helpers/radiolib/LinuxSX1262Wrapper.h
+++ b/src/helpers/radiolib/LinuxSX1262Wrapper.h
@@ -30,8 +30,8 @@ public:
   }
   uint8_t getSpreadingFactor() const override { return ((LinuxSX1262 *)_radio)->spreadingFactor; }

-  void setRxBoostedGainMode(bool en) override {
-    ((LinuxSX1262 *)_radio)->setRxBoostedGainMode(en);
+  bool setRxBoostedGainMode(bool en) override {
+    return ((LinuxSX1262 *)_radio)->setRxBoostedGainMode(en);
   }
   bool getRxBoostedGainMode() const override {
     return ((LinuxSX1262 *)_radio)->getRxBoostedGainMode();

@l5yth
l5yth force-pushed the linux branch 2 times, most recently from 1ac5e7d to 036c777 Compare August 31, 2026 11:15
@l5yth

l5yth commented Aug 31, 2026

Copy link
Copy Markdown
Author

@oltaco I'll be maintaining and rebasing this on a regular basis. The August summer break this year delayed my response though. I'd be happy to see this included, so I can rebase my other native linux work on upstream in future.

@rgrizzell thank you for your contributions and reviews. the upstream PR at Adafruit_BusIO is still pending. I updated ArduLinux and pinned a new release.

Please check again.

@rgrizzell

Copy link
Copy Markdown

@l5yth The latest version works great, thank you! 🎉

The only missing feature for the Linux variant is a way to send commands using meshcore-cli. I've spent some time and tokens to get that implemented. PR: l5yth#25

the upstream PR at Adafruit_BusIO is still pending.

I'll reach out to the Adafruit Devs and ask for a review. 🙏🏼

ggodlewski and others added 15 commits September 9, 2026 09:59
* variants: add scaffolding for linux native

* address review comments

* address review comments
* variants: allow linux repeater to be configured at runtime

* address review comments

* address review comments

* address review comments
* 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))
#8)

* variants/linux: on-hardware smoke-test fixes (rx_boost, dio2, --fsdir, docs)

* address review comments
* proofread README

* proofread README
* variants/linux: use linux_base for platformio

* variants/linux: use linux_base for platformio
* pin ardulinux to wire-fix branch, document pkg-config

* pin ardulinux 0.2.1
…#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: make lora_gpiochip configurable, refresh docs

* ci: variants/linux: add lora_gpiochip hint to base meshcored.ini too

* bump ardulinux to 0.2.2
`mesh::MainBoard::sleep()` is a no-op by default and `LinuxBoard` never overrode it, so `board.sleep(0)`/`sleep(30)` returned immediately and the repeater loop spun a core at 100%. Only `NRF52Board` and `ESP32Board` implement it.

Implement it with `sleep()`/`usleep()`, and add a `delay(1)` in the main loop's non-powersaving branch so platforms without power management do not spin either.

Reported and fixed by brianhealey, measured at 99% -> 0-9% CPU on a Pi Compute Module 5 with an SX1262. Cherry-picked from #21, which could not be merged.

(cherry picked from commit 11d6ddf)

Co-authored-by: brianhealey <brian.healey@gmail.com>
The linux repeater's CLI reads Serial, but on ardulinux Serial is a
stdout-only stub, so the CLI was unreachable — the node could only be
configured over the mesh, with no way to fix a bad radio preset locally
without a second node.

Add a console on a pseudo-terminal (PTY), in MeshCore application code
(not the ardulinux platform layer, so it survives a platform swap) with
logs and the CLI on separate streams. A PTY rather than a socket so
meshcore-cli's repeater mode can attach directly:
  meshcore-cli -r -s /run/meshcored/console
(meshcore-cli -r drives a raw-text serial CLI via pyserial, which needs
a tty.)

- src/helpers/PtyConsole.{h,cpp}: pure-POSIX PTY engine (no Arduino
  dependency, so it is host-unit-testable). posix_openpt + a stable
  symlink to /dev/pts/N; raw termios; maps '\n'->'\r' (1:1) so tools that
  send newline work with the CR-terminated CLI. The pts device is chmod
  0600 -- the unauthenticated local CLI's access gate. The master
  persists across client attach/detach, so there is no accept/reap and a
  write to a closed peer returns EIO (never SIGPIPE).
- src/helpers/LinuxConsole.h: thin Arduino Stream adapter; write()
  mirrors to stdout (so commands/replies reach journald) and the PTY.
- variants/linux: LinuxConfig.console_path INI key (default: a per-user
  path); systemd unit + README document /run/meshcored/console and
  meshcore-cli. User docs avoid the PTY/serial internals.
- examples/simple_repeater: route the CLI loop to the console stream;
  logs (MESH_DEBUG) and the boot banner stay on Serial. MCU targets keep
  console == &Serial, so their behavior is unchanged.
- platformio.ini [env:native]: define MESHCORE_HOST_TEST and add
  PtyConsole.cpp to build_src_filter. PtyConsole is compiled out without
  the macro and the suite does not link without the source, so both are
  required for CI to build test_console at all.
- test/test_console: 18 googletest cases for the engine. Client I/O,
  newline mapping, peek, reconnect, write-after-close and the 0600
  char-device perms; begin() idempotency; default link resolution via
  XDG_RUNTIME_DIR and the /tmp/meshcore-<uid> fallback; path() falling
  back to the pts device when the symlink cannot be published;
  available() accounting for a peeked byte alongside the queue; end()
  idempotency and post-close inertness; destructor cleanup. All three
  begin() failure paths are exercised: posix_openpt via RLIMIT_NOFILE,
  and grantpt/ptsname_r (unreachable once posix_openpt has succeeded)
  via strong definitions that forward to libc unless a test arms them.

Refs #25
@l5yth

l5yth commented Sep 9, 2026

Copy link
Copy Markdown
Author

@oltaco @liamcottle Could we get a review or an indication where we should go from here? We already have people that use the companion target (downstream) that we implemented on top of this work but I would prefer if we could look at the linux_repeater target first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants