Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions boards/linux.json
Original file line number Diff line number Diff line change
@@ -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"
}
23 changes: 19 additions & 4 deletions examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,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);
Expand Down Expand Up @@ -944,6 +944,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);
Expand Down Expand Up @@ -1023,6 +1037,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;
Expand Down Expand Up @@ -1178,9 +1195,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()"
Expand Down
2 changes: 2 additions & 0 deletions examples/simple_repeater/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#elif defined(ESP32)
#include <SPIFFS.h>
using File = fs::File;
#elif defined(ARDULINUX_PLATFORM)
#include <ArduLinuxFS.h>
#endif

#ifdef WITH_RS232_BRIDGE
Expand Down
41 changes: 36 additions & 5 deletions examples/simple_repeater/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ SimpleMeshTables tables;

MyMesh the_mesh(board, radio_driver, *new ArduinoMillis(), fast_rng, rtc_clock, tables);

// CLI console stream. On Linux the interactive CLI runs over a Unix-domain
// socket (so the socket carries only the CLI while Serial keeps the logs); on
// MCU targets, and as the Linux fallback, it is just Serial.
#if defined(ARDULINUX_PLATFORM) || defined(LINUX_PLATFORM)
#include <helpers/LinuxConsole.h>
static LinuxConsole linux_console;
#endif
static Stream* console = &Serial;

void halt() {
while (1) ;
}
Expand Down Expand Up @@ -45,6 +54,19 @@ void setup() {
external_watchdog.begin();
#endif

#if defined(ARDULINUX_PLATFORM) || defined(LINUX_PLATFORM)
// Bring up the local CLI console (config path, or a per-user default). Log
// which console is active so a failure (e.g. an unwritable configured path) is
// diagnosable rather than a silent no-CLI daemon; on failure the CLI stays on
// Serial (stdin/stdout).
if (linux_console.begin(board.config.console_path)) {
console = &linux_console;
Serial.print("CLI console on "); Serial.println(linux_console.path());
} else {
Serial.println("CLI console: unavailable (see stderr), using stdio");
}
#endif

#if defined(MESH_DEBUG) && defined(NRF52_PLATFORM)
// give some extra time for serial to settle so
// boot debug messages can be seen on terminal
Expand Down Expand Up @@ -81,6 +103,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
Expand Down Expand Up @@ -125,12 +153,12 @@ void setup() {
void loop() {
// Handle Serial CLI
int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read();
while (console->available() && len < sizeof(command)-1) {
char c = console->read();
if (c != '\n') {
command[len++] = c;
command[len] = 0;
Serial.print(c);
console->print(c);
}
if (c == '\r') break;
}
Expand All @@ -139,7 +167,7 @@ void loop() {
}

if (len > 0 && command[len - 1] == '\r') { // received complete line
Serial.print('\n');
console->print('\n');
command[len - 1] = 0; // replace newline with C string null terminator
char reply[160];
reply[0] = 0;
Expand All @@ -151,7 +179,7 @@ void loop() {
the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial!
#endif
if (reply[0]) {
Serial.print(" -> "); Serial.println(reply);
console->print(" -> "); console->println(reply);
}

command[0] = 0; // reset command buffer
Expand Down Expand Up @@ -203,5 +231,8 @@ void loop() {
board.sleep(30); // Sleep. Wake up after a while or when receiving a LoRa packet
}
#endif
} else {
// Small delay to prevent busy loop on platforms without power saving
delay(1);
}
}
47 changes: 47 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,51 @@ 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.3
framework = arduino
board = linux
board_level = extra
board_build.progname = meshcored
build_src_filter =
${env.build_src_filter}
-<platform/esp32/>
-<nimble/>
-<platform/nrf52/>
-<platform/stm32wl/>
-<platform/rp2xx0>
-<mesh/wifi/>
-<mesh/http/>
-<mesh/eth/>
-<modules/esp32>
+<../variants/linux>
-<helpers/esp32/*.cpp>
-<helpers/nrf52/*.cpp>
-<helpers/stm32/*.cpp>
-<helpers/ethernet/>
-<helpers/bridges/ESPNowBridge.cpp>
-<helpers/*/*Display.cpp>
lib_deps =
${arduino_base.lib_deps}
adafruit/Adafruit seesaw Library@1.7.9
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
Expand Down Expand Up @@ -164,6 +209,7 @@ test_framework = googletest
build_flags = -std=c++17
-I src
-I test/mocks
-D MESHCORE_HOST_TEST
test_build_src = yes
test_ignore = test_kiss_modem
build_src_filter =
Expand All @@ -172,6 +218,7 @@ build_src_filter =
+<../src/Packet.cpp>
+<../src/helpers/ConfigSerializer.cpp>
+<../src/helpers/DynamicConfigSerializer.cpp>
+<../src/helpers/PtyConsole.cpp>
lib_deps =
google/googletest @ 1.17.0

Expand Down
2 changes: 1 addition & 1 deletion src/helpers/ClientACL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ bool CommonCLI::savePrefs(FILESYSTEM* fs) {
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
fs->remove("/prefs.json");
File file = fs->open("/prefs.json", FILE_O_WRITE);
#elif defined(RP2040_PLATFORM)
#elif defined(RP2040_PLATFORM) || defined(ARDULINUX_PLATFORM)
File file = fs->open("/prefs.json", "w");
#else
File file = fs->open("/prefs.json", "w", true);
Expand Down
4 changes: 2 additions & 2 deletions src/helpers/IdentityStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/IdentityStore.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#if defined(ESP32) || defined(RP2040_PLATFORM)
#if defined(ESP32) || defined(RP2040_PLATFORM) || defined(ARDULINUX_PLATFORM)
#include <FS.h>
#define FILESYSTEM fs::FS
#elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
Expand Down
45 changes: 45 additions & 0 deletions src/helpers/LinuxConsole.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#if defined(ARDULINUX_PLATFORM) || defined(LINUX_PLATFORM)

#include <Arduino.h>
#include <stdio.h>
#include "PtyConsole.h"

// Arduino Stream adapter over PtyConsole.
//
// The repeater's text CLI reads from / writes to this instead of Serial, so the
// PTY carries only the CLI while Serial (stdout) keeps the debug logs — the
// clean log/CLI split. write() also mirrors each byte to stdout, so admin
// commands and their replies are still recorded in the log (journald) alongside
// the debug output, while the console stays free of log noise.
//
// A client attaches to the published PTY symlink, e.g.:
// meshcore-cli -r -s /run/meshcored/console
//
// All the PTY mechanics live in PtyConsole (host-unit-tested); this adapter is a
// thin delegating shim.
class LinuxConsole : public Stream {
PtyConsole _pty;

public:
// Open the PTY and publish the symlink (see PtyConsole::begin). Returns true
// on success; on failure the caller should fall back to Serial.
bool begin(const char *link) { return _pty.begin(link); }
void end() { _pty.end(); }
bool isOpen() const { return _pty.isOpen(); }
const char *path() const { return _pty.path(); }

int available() override { return _pty.available(); }
int read() override { return _pty.read(); }
int peek() override { return _pty.peek(); }
size_t write(uint8_t c) override {
putchar(c); // mirror to stdout so commands/replies reach journald
_pty.write(c); // and to the attached console client
return 1;
}
void flush() override { fflush(stdout); _pty.flush(); }
using Print::write; // pull in write(str) / write(buf, size)
};

#endif // ARDULINUX_PLATFORM || LINUX_PLATFORM
Loading