diff --git a/.github/workflows/build_raspbian.yml b/.github/workflows/build_raspbian.yml new file mode 100644 index 0000000000..1860e90980 --- /dev/null +++ b/.github/workflows/build_raspbian.yml @@ -0,0 +1,30 @@ +name: Build Raspbian + +on: workflow_call + +permissions: + contents: write + packages: write + +jobs: + build-raspbian: + runs-on: [self-hosted, linux, ARM64] + steps: + - uses: actions/checkout@v3 + - name: Build base + id: base + uses: ./.github/actions/setup-base + + - name: Build Raspbian + run: bin/build-native.sh + + - name: Get release version string + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v3 + with: + name: firmware-native-${{ steps.version.outputs.version }}.zip + path: | + release/meshtasticd_linux_arm64 diff --git a/.trunk/configs/.shellcheckrc b/.trunk/configs/.shellcheckrc index 8c7b1ada8a..b2e8a14cc6 100644 --- a/.trunk/configs/.shellcheckrc +++ b/.trunk/configs/.shellcheckrc @@ -1,7 +1,10 @@ enable=all source-path=SCRIPTDIR disable=SC2154 +disable=SC2248 +disable=SC2250 # If you're having issues with shellcheck following source, disable the errors via: # disable=SC1090 # disable=SC1091 +# \ No newline at end of file diff --git a/bin/build-native.sh b/bin/build-native.sh index 8bc262860e..64c5adb504 100755 --- a/bin/build-native.sh +++ b/bin/build-native.sh @@ -2,8 +2,8 @@ set -e -VERSION=`bin/buildinfo.py long` -SHORT_VERSION=`bin/buildinfo.py short` +VERSION=$(bin/buildinfo.py long) +SHORT_VERSION=$(bin/buildinfo.py short) OUTDIR=release/ @@ -13,11 +13,15 @@ mkdir -p $OUTDIR/ rm -r $OUTDIR/* || true # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale -platformio pkg update +platformio pkg update -pio run --environment native -cp .pio/build/native/program $OUTDIR/meshtasticd_linux_amd64 +if command -v raspi-config &>/dev/null; then + pio run --environment raspbian + cp .pio/build/raspbian/program $OUTDIR/meshtasticd_linux_arm64 +else + pio run --environment native + cp .pio/build/native/program $OUTDIR/meshtasticd_linux_amd64 +fi cp bin/device-install.* $OUTDIR cp bin/device-update.* $OUTDIR - diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml new file mode 100644 index 0000000000..2a3abac6ff --- /dev/null +++ b/bin/config-dist.yaml @@ -0,0 +1,26 @@ +# Define your devices here. +# Use Broadcom pin numbering + +#Waveshare SX126X XXXM + +#USE_SX1262: true +#SX126X_DIO2_AS_RF_SWITCH: true +#SX126X_CS: 21 +#SX126X_DIO1: 16 +#SX126X_BUSY: 20 +#SX126X_RESET: 18 + +#Waveshare SX1302 LISTEN ONLY AT THIS TIME! + +#USE_SX1262: true +#SX126X_CS: 7 +#SX126X_DIO1: 17 +#SX126X_RESET: 22 + +#Adafruit RFM9x + +#USE_RF95: true +#RF95_RESET: 25 +#RF95_NSS: 7 +#RF95_IRQ: 22 +#RF95_DIO1: 23 diff --git a/bin/meshtasticd.service b/bin/meshtasticd.service new file mode 100644 index 0000000000..4ed1bfd8fa --- /dev/null +++ b/bin/meshtasticd.service @@ -0,0 +1,9 @@ +[unit] +description=Meshtastic Native Daemon + +[Service] +Type=simple +ExecStart=/usr/sbin/meshtasticd + +[Install] +WantedBy=multi-user.target diff --git a/bin/native-install.sh b/bin/native-install.sh new file mode 100755 index 0000000000..d1d0c8707b --- /dev/null +++ b/bin/native-install.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +cp release/meshtasticd_linux_arm64 /usr/sbin/meshtasticd +mkdir /etc/meshtasticd +if [[ -f "/etc/meshtasticd/config.yaml" ]]; then + cp bin/config-dist.yaml /etc/meshtasticd/config-upgrade.yaml +else + cp bin/config-dist.yaml /etc/meshtasticd/config.yaml +fi +cp bin/meshtasticd.service /usr/lib/systemd/system/meshtasticd.service diff --git a/src/configuration.h b/src/configuration.h index 199880c6be..cb7ee218be 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -57,8 +57,8 @@ along with this program. If not, see . #define REQUIRE_RADIO true // If true, we will fail to start if the radio is not found /// Convert a preprocessor name into a quoted string -#define xstr(s) str(s) -#define str(s) #s +#define xstr(s) ystr(s) +#define ystr(s) #s /// Convert a preprocessor name into a quoted string and if that string is empty use "unset" #define optstr(s) (xstr(s)[0] ? xstr(s) : "unset") @@ -209,4 +209,4 @@ along with this program. If not, see . #ifndef HW_VENDOR #error HW_VENDOR must be defined -#endif +#endif \ No newline at end of file diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 47ba067d2a..af622e3d8b 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -17,6 +17,9 @@ #if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) HardwareSerial *GPS::_serial_gps = &Serial1; +#elif defined(ARCH_RASPBERRY_PI) +// need a translation layer to make _serial_gps work with pigpio https://abyz.me.uk/rpi/pigpio/cif.html#serOpen +HardwareSerial *GPS::_serial_gps = NULL; #else HardwareSerial *GPS::_serial_gps = NULL; #endif diff --git a/src/main.cpp b/src/main.cpp index d5b3895d2e..5c3151fc07 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,6 +69,7 @@ NRF52Bluetooth *nrf52Bluetooth; #ifdef ARCH_RASPBERRY_PI #include "platform/portduino/PiHal.h" +#include "platform/portduino/PortduinoGlue.h" #include #include #include @@ -690,15 +691,32 @@ void setup() #endif #ifdef ARCH_RASPBERRY_PI - PiHal *RadioLibHAL = new PiHal(1); - if (!rIf) { - rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, 21, 16, 18, 20); - if (!rIf->init()) { - LOG_WARN("Failed to find SX1262 radio\n"); - delete rIf; - rIf = NULL; - } else { - LOG_INFO("SX1262 Radio init succeeded, using SX1262 radio\n"); + if (settingsMap[use_sx1262]) { + if (!rIf) { + PiHal *RadioLibHAL = new PiHal(1); + rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[sx126x_cs], settingsMap[sx126x_dio1], + settingsMap[sx126x_reset], settingsMap[sx126x_busy]); + if (!rIf->init()) { + LOG_ERROR("Failed to find SX1262 radio\n"); + delete rIf; + exit(EXIT_FAILURE); + } else { + LOG_INFO("SX1262 Radio init succeeded, using SX1262 radio\n"); + } + } + } else if (settingsMap[use_rf95]) { + if (!rIf) { + PiHal *RadioLibHAL = new PiHal(1); + rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[rf95_nss], settingsMap[rf95_irq], + settingsMap[rf95_reset], settingsMap[rf95_dio1]); + if (!rIf->init()) { + LOG_ERROR("Failed to find RF95 radio\n"); + delete rIf; + rIf = NULL; + exit(EXIT_FAILURE); + } else { + LOG_INFO("RF95 Radio init succeeded, using RF95 radio\n"); + } } } diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 9801079171..ba3f2bc2a8 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -2,6 +2,9 @@ #include "configuration.h" #include "error.h" #include "mesh/NodeDB.h" +#ifdef ARCH_RASPBERRY_PI +#include "PortduinoGlue.h" +#endif // Particular boards might define a different max power based on what their hardware can do, default to max power output if not // specified (may be dangerous if using external PA and SX126x power config forgotten) @@ -74,6 +77,12 @@ template bool SX126xInterface::init() #ifdef SX126X_DIO2_AS_RF_SWITCH LOG_DEBUG("Setting DIO2 as RF switch\n"); bool dio2AsRfSwitch = true; +#elif defined(ARCH_RASPBERRY_PI) + bool dio2AsRfSwitch = false; + if (settingsMap[sx126x_dio2_as_rf_switch]) { + LOG_DEBUG("Setting DIO2 as RF switch\n"); + dio2AsRfSwitch = true; + } #else LOG_DEBUG("Setting DIO2 as not RF switch\n"); bool dio2AsRfSwitch = false; @@ -318,4 +327,4 @@ template bool SX126xInterface::sleep() #endif return true; -} +} \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index fb71a429b2..b3c2dc5f2e 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -9,7 +9,14 @@ #include #ifdef ARCH_RASPBERRY_PI +#include "PortduinoGlue.h" #include "pigpio.h" +#include "yaml-cpp/yaml.h" +#include +#include +#include + +std::map settingsMap; #else #include @@ -27,7 +34,7 @@ void cpuDeepSleep(uint32_t msecs) } void updateBatteryLevel(uint8_t level) NOT_IMPLEMENTED("updateBatteryLevel"); - +#ifndef ARCH_RASPBERRY_PI /** a simulated pin for busted IRQ hardware * Porduino helper class to do this i2c based polling: */ @@ -54,7 +61,7 @@ class PolledIrqPin : public GPIOPin }; static GPIOPin *loraIrq; - +#endif int TCPPort = 4403; static error_t parse_opt(int key, char *arg, struct argp_state *state) @@ -94,6 +101,48 @@ void portduinoSetup() printf("Setting up Meshtastic on Portduino...\n"); #ifdef ARCH_RASPBERRY_PI + YAML::Node yamlConfig; + + if (access("config.yaml", R_OK) == 0) { + try { + yamlConfig = YAML::LoadFile("config.yaml"); + } catch (YAML::Exception e) { + std::cout << "*** Exception " << e.what() << std::endl; + exit(EXIT_FAILURE); + } + } else if (access("/etc/meshtasticd/config.yaml", R_OK) == 0) { + try { + yamlConfig = YAML::LoadFile("/etc/meshtasticd/config.yaml"); + } catch (YAML::Exception e) { + std::cout << "*** Exception " << e.what() << std::endl; + exit(EXIT_FAILURE); + } + } else { + std::cout << "No 'config.yaml' found, exiting." << std::endl; + exit(EXIT_FAILURE); + } + + try { + settingsMap[use_sx1262] = yamlConfig["USE_SX1262"].as(false); + settingsMap[sx126x_dio2_as_rf_switch] = yamlConfig["SX126X_DIO2_AS_RF_SWITCH"].as(false); + settingsMap[sx126x_cs] = yamlConfig["SX126X_CS"].as(RADIOLIB_NC); + settingsMap[sx126x_dio1] = yamlConfig["SX126X_DIO1"].as(RADIOLIB_NC); + settingsMap[sx126x_busy] = yamlConfig["SX126X_BUSY"].as(RADIOLIB_NC); + settingsMap[sx126x_reset] = yamlConfig["SX126X_RESET"].as(RADIOLIB_NC); + settingsMap[use_rf95] = yamlConfig["USE_RF95"].as(false); + settingsMap[rf95_nss] = yamlConfig["RF95_NSS"].as(RADIOLIB_NC); + settingsMap[rf95_irq] = yamlConfig["RF95_IRQ"].as(RADIOLIB_NC); + settingsMap[rf95_reset] = yamlConfig["RF95_RESET"].as(RADIOLIB_NC); + settingsMap[rf95_dio1] = yamlConfig["RF95_DIO1"].as(RADIOLIB_NC); + + } catch (YAML::Exception e) { + std::cout << "*** Exception " << e.what() << std::endl; + exit(EXIT_FAILURE); + } + if (access("/sys/kernel/debug/bluetooth/hci0/identity", R_OK) != 0) { + std::cout << "Cannot read Bluetooth MAC Address. Please run as root" << std::endl; + exit(EXIT_FAILURE); + } return; #endif @@ -121,7 +170,7 @@ void portduinoSetup() gpioBind(loraCs); } else #endif - +#ifndef ARCH_RASPBERRY_PI { // Set the random seed equal to TCPPort to have a different seed per instance randomSeed(TCPPort); @@ -140,4 +189,5 @@ void portduinoSetup() } // gpioBind((new SimGPIOPin(LORA_RESET, "LORA_RESET"))); // gpioBind((new SimGPIOPin(RF95_NSS, "RF95_NSS"))->setSilent()); +#endif } \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h new file mode 100644 index 0000000000..91fc4c2b1d --- /dev/null +++ b/src/platform/portduino/PortduinoGlue.h @@ -0,0 +1,21 @@ +#pragma once +#ifdef ARCH_RASPBERRY_PI +#include + +extern std::map settingsMap; + +enum { + use_sx1262, + sx126x_cs, + sx126x_dio1, + sx126x_busy, + sx126x_reset, + sx126x_dio2_as_rf_switch, + use_rf95, + rf95_nss, + rf95_irq, + rf95_reset, + rf95_dio1 +}; + +#endif \ No newline at end of file diff --git a/variants/portduino/platformio.ini b/variants/portduino/platformio.ini index 323609d0e5..be07bcb15f 100644 --- a/variants/portduino/platformio.ini +++ b/variants/portduino/platformio.ini @@ -16,7 +16,7 @@ build_src_filter = ${portduino_base.build_src_filter} ; The Raspberry Pi actually has accessible SPI and GPIO, so we can support real hardware there. [env:raspbian] extends = portduino_base -build_flags = ${portduino_base.build_flags} -O0 -lgpiod -I variants/portduino -DARCH_RASPBERRY_PI -DRADIOLIB_DEBUG -lpigpio +build_flags = ${portduino_base.build_flags} -O0 -lgpiod -I variants/portduino -DARCH_RASPBERRY_PI -DRADIOLIB_DEBUG -lpigpio -lyaml-cpp board = linux_arm lib_deps = ${portduino_base.lib_deps} build_src_filter = ${portduino_base.build_src_filter} \ No newline at end of file diff --git a/variants/portduino/variant.h b/variants/portduino/variant.h index 2ce871ddc0..46f7d1f0cd 100644 --- a/variants/portduino/variant.h +++ b/variants/portduino/variant.h @@ -1,34 +1,6 @@ #if defined(ARCH_RASPBERRY_PI) -#define HAS_RADIO 1 -#define GPIOD_CHIP_LABEL "pinctrl-bcm2711" - -// define USE_RF95 -#define USE_SX1262 -#define SX126X_TXEN 6 -#define SX126X_DIO2_AS_RF_SWITCH #define NO_SCREEN -#define RF95_SCK 11 -#define RF95_MISO 9 -#define RF95_MOSI 10 -#define RF95_NSS RADIOLIB_NC - -// #define LORA_DIO0 4 // a No connect on the SX1262 module -// #define LORA_DIO0_LABEL "GPIO_GCLK" -#define LORA_RESET 18 -#define LORA_RESET_LABEL "GPIO18" -#define LORA_DIO1 16 // SX1262 IRQ, called DIO0 on pinelora schematic, pin 7 on ch341f "ack" - FIXME, enable hwints in linux -// #define LORA_DIO2 20 // SX1262 BUSY, actually connected to "DIO5" on pinelora schematic, pin 8 on ch341f "slct" -// #define LORA_DIO3 6 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled - -#ifdef USE_SX1262 -#define SX126X_CS 21 -#define SX126X_DIO1 16 -#define SX126X_BUSY 20 -#define SX126X_RESET LORA_RESET -// HOPE RFM90 does not have a TCXO therefore not SX126X_E22 -#endif - #else // Pine64 mode. // Pine64 uses a common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if