diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index ce5b18af8..47ba067d2 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -8,6 +8,7 @@ #ifdef ARCH_PORTDUINO #include "meshUtils.h" +#include #endif #ifndef GPS_RESET_MODE diff --git a/src/main.cpp b/src/main.cpp index a18ee4099..d5b3895d2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -67,6 +67,13 @@ NRF52Bluetooth *nrf52Bluetooth; #include "platform/portduino/SimRadio.h" #endif +#ifdef ARCH_RASPBERRY_PI +#include "platform/portduino/PiHal.h" +#include +#include +#include +#endif + #if HAS_BUTTON #include "ButtonThread.h" #endif @@ -128,12 +135,32 @@ std::pair nodeTelemetrySensorsMap[_meshtastic_TelemetrySenso Router *router = NULL; // Users of router don't care what sort of subclass implements that API +#ifdef ARCH_RASPBERRY_PI +void getPiMacAddr(uint8_t *dmac) +{ + std::fstream macIdentity; + macIdentity.open("/sys/kernel/debug/bluetooth/hci0/identity", std::ios::in); + std::string macLine; + getline(macIdentity, macLine); + macIdentity.close(); + + dmac[0] = strtol(macLine.substr(0, 2).c_str(), NULL, 16); + dmac[1] = strtol(macLine.substr(3, 2).c_str(), NULL, 16); + dmac[2] = strtol(macLine.substr(6, 2).c_str(), NULL, 16); + dmac[3] = strtol(macLine.substr(9, 2).c_str(), NULL, 16); + dmac[4] = strtol(macLine.substr(12, 2).c_str(), NULL, 16); + dmac[5] = strtol(macLine.substr(15, 2).c_str(), NULL, 16); +} +#endif + const char *getDeviceName() { uint8_t dmac[6]; - +#ifdef ARCH_RASPBERRY_PI + getPiMacAddr(dmac); +#else getMacAddr(dmac); - +#endif // Meshtastic_ab3c or Shortname_abcd static char name[20]; snprintf(name, sizeof(name), "%02x%02x", dmac[4], dmac[5]); @@ -662,7 +689,20 @@ void setup() digitalWrite(SX126X_ANT_SW, 1); #endif -#ifdef HW_SPI1_DEVICE +#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"); + } + } + +#elif defined(HW_SPI1_DEVICE) LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI1, spiSettings); #else // HW_SPI1_DEVICE LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); @@ -708,7 +748,7 @@ void setup() } #endif -#if defined(USE_SX1262) +#if defined(USE_SX1262) && !defined(ARCH_RASPBERRY_PI) if (!rIf) { rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { diff --git a/src/main.h b/src/main.h index 3f0120406..5c9de1b81 100644 --- a/src/main.h +++ b/src/main.h @@ -56,6 +56,7 @@ extern graphics::Screen *screen; // Return a human readable string of the form "Meshtastic_ab13" const char *getDeviceName(); +void getPiMacAddr(uint8_t *dmac); extern uint32_t timeLastPowered; diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 151888746..d7fa9e5ac 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -435,7 +435,11 @@ void NodeDB::init() */ void NodeDB::pickNewNodeNum() { +#ifdef ARCH_RASPBERRY_PI + getPiMacAddr(ourMacAddr); // Make sure ourMacAddr is set +#else getMacAddr(ourMacAddr); // Make sure ourMacAddr is set +#endif // Pick an initial nodenum based on the macaddr NodeNum nodeNum = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5]; @@ -447,6 +451,7 @@ void NodeDB::pickNewNodeNum() LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, so trying for 0x%x\n", nodeNum, candidate); nodeNum = candidate; } + LOG_WARN("Using nodenum 0x%x \n", nodeNum); myNodeInfo.my_node_num = nodeNum; } diff --git a/src/platform/portduino/PiHal.h b/src/platform/portduino/PiHal.h new file mode 100644 index 000000000..f10040583 --- /dev/null +++ b/src/platform/portduino/PiHal.h @@ -0,0 +1,155 @@ +#ifndef PI_HAL_H +#define PI_HAL_H + +// include RadioLib +#include + +// include the library for Raspberry GPIO pins +#include "pigpio.h" + +// create a new Raspberry Pi hardware abstraction layer +// using the pigpio library +// the HAL must inherit from the base RadioLibHal class +// and implement all of its virtual methods +class PiHal : public RadioLibHal +{ + public: + // default constructor - initializes the base HAL and any needed private members + PiHal(uint8_t spiChannel, uint32_t spiSpeed = 2000000) + : RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, RISING_EDGE, FALLING_EDGE), _spiChannel(spiChannel), + _spiSpeed(spiSpeed) + { + } + + void init() override + { + // first initialise pigpio library + gpioInitialise(); + + // now the SPI + spiBegin(); + + // Waveshare LoRaWAN Hat also needs pin 18 to be pulled high to enable the radio + // gpioSetMode(18, PI_OUTPUT); + // gpioWrite(18, PI_HIGH); + } + + void term() override + { + // stop the SPI + spiEnd(); + + // pull the enable pin low + // gpioSetMode(18, PI_OUTPUT); + // gpioWrite(18, PI_LOW); + + // finally, stop the pigpio library + gpioTerminate(); + } + + // GPIO-related methods (pinMode, digitalWrite etc.) should check + // RADIOLIB_NC as an alias for non-connected pins + void pinMode(uint32_t pin, uint32_t mode) override + { + if (pin == RADIOLIB_NC) { + return; + } + + gpioSetMode(pin, mode); + } + + void digitalWrite(uint32_t pin, uint32_t value) override + { + if (pin == RADIOLIB_NC) { + return; + } + + gpioWrite(pin, value); + } + + uint32_t digitalRead(uint32_t pin) override + { + if (pin == RADIOLIB_NC) { + return (0); + } + + return (gpioRead(pin)); + } + + void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override + { + if (interruptNum == RADIOLIB_NC) { + return; + } + if (gpioRead(interruptNum) == 1) { + interruptCb(); + } else { + gpioSetAlertFunc(interruptNum, (gpioISRFunc_t)interruptCb); + } + } + + void detachInterrupt(uint32_t interruptNum) override + { + if (interruptNum == RADIOLIB_NC) { + return; + } + + gpioSetAlertFunc(interruptNum, NULL); + } + + void delay(unsigned long ms) override { gpioDelay(ms * 1000); } + + void delayMicroseconds(unsigned long us) override { gpioDelay(us); } + + unsigned long millis() override { return (gpioTick() / 1000); } + + unsigned long micros() override { return (gpioTick()); } + + long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override + { + if (pin == RADIOLIB_NC) { + return (0); + } + + this->pinMode(pin, PI_INPUT); + uint32_t start = this->micros(); + uint32_t curtick = this->micros(); + + while (this->digitalRead(pin) == state) { + if ((this->micros() - curtick) > timeout) { + return (0); + } + } + + return (this->micros() - start); + } + + void spiBegin() + { + if (_spiHandle < 0) { + _spiHandle = spiOpen(_spiChannel, _spiSpeed, 0); + } + } + + void spiBeginTransaction() {} + + void spiTransfer(uint8_t *out, size_t len, uint8_t *in) { spiXfer(_spiHandle, (char *)out, (char *)in, len); } + + void spiEndTransaction() {} + + void spiEnd() + { + if (_spiHandle >= 0) { + spiClose(_spiHandle); + _spiHandle = -1; + } + } + + private: + // the HAL can contain any additional private members + const unsigned int _spiSpeed; + const uint8_t _spiChannel; + int _spiHandle = -1; +}; + +#endif \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 4e957cb1e..fb71a429b 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -7,10 +7,15 @@ #include #include + +#ifdef ARCH_RASPBERRY_PI +#include "pigpio.h" + +#else #include +#endif // FIXME - move setBluetoothEnable into a HALPlatform class - void setBluetoothEnable(bool on) { // not needed @@ -88,7 +93,11 @@ void portduinoSetup() { printf("Setting up Meshtastic on Portduino...\n"); -#ifdef PORTDUINO_LINUX_HARDWARE +#ifdef ARCH_RASPBERRY_PI + return; +#endif + +#ifdef defined(PORTDUINO_LINUX_HARDWARE) SPI.begin(); // We need to create SPI bool usePineLora = !spiChip->isSimulated(); if (usePineLora) { @@ -129,7 +138,6 @@ void portduinoSetup() gpioBind(new SimGPIOPin(SX126X_RESET, "fakeLoraReset")); gpioBind(new SimGPIOPin(LORA_DIO1, "fakeLoraIrq")); } - // gpioBind((new SimGPIOPin(LORA_RESET, "LORA_RESET"))); // gpioBind((new SimGPIOPin(RF95_NSS, "RF95_NSS"))->setSilent()); -} +} \ No newline at end of file diff --git a/src/shutdown.h b/src/shutdown.h index 90fb19d0c..f36a7f8dd 100644 --- a/src/shutdown.h +++ b/src/shutdown.h @@ -14,6 +14,8 @@ void powerCommandsCheck() NVIC_SystemReset(); #elif defined(ARCH_RP2040) rp2040.reboot(); +#elif defined(ARCH_RASPBERRY_PI) + exit(EXIT_SUCCESS); #else rebootAtMsec = -1; LOG_WARN("FIXME implement reboot for this platform. Note that some settings require a restart to be applied.\n"); diff --git a/variants/portduino/platformio.ini b/variants/portduino/platformio.ini index 5bbde2adf..323609d0e 100644 --- a/variants/portduino/platformio.ini +++ b/variants/portduino/platformio.ini @@ -13,10 +13,10 @@ board = linux_hardware lib_deps = ${portduino_base.lib_deps} build_src_filter = ${portduino_base.build_src_filter} -; The Portduino based sim environment on top of a linux OS and touching linux hardware devices -[env:linux-arm] +; 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 +build_flags = ${portduino_base.build_flags} -O0 -lgpiod -I variants/portduino -DARCH_RASPBERRY_PI -DRADIOLIB_DEBUG -lpigpio board = linux_arm lib_deps = ${portduino_base.lib_deps} -build_src_filter = ${portduino_base.build_src_filter} +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 81dfd3885..2ce871ddc 100644 --- a/variants/portduino/variant.h +++ b/variants/portduino/variant.h @@ -1,3 +1,36 @@ +#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 // not found then probe for SX1262. Currently the RF95 code is disabled because I think the RF95 module won't need to ship. // #define USE_RF95 @@ -13,7 +46,7 @@ #define LORA_RESET 14 #define LORA_DIO1 33 // SX1262 IRQ, called DIO0 on pinelora schematic, pin 7 on ch341f "ack" - FIXME, enable hwints in linux #define LORA_DIO2 32 // SX1262 BUSY, actually connected to "DIO5" on pinelora schematic, pin 8 on ch341f "slct" -#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled +#define LORA_DIO3 RADIOLIB_NC // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled #ifdef USE_SX1262 #define SX126X_CS 20 // CS0 on pinelora schematic, hooked to gpio D0 on ch341f @@ -21,5 +54,6 @@ #define SX126X_BUSY LORA_DIO2 #define SX126X_RESET LORA_RESET #define SX126X_DIO2_AS_RF_SWITCH -// HOPE RFM90 does not have a TCXO therefore not SX126X_E22 #endif + +#endif \ No newline at end of file