From 102efd49548e5c1c6673da2c4b462692544ebee2 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 26 Nov 2023 15:08:20 -0600 Subject: [PATCH] Move to portduino GPIO, adding user button support --- bin/config-dist.yaml | 5 +++ src/ButtonThread.h | 28 ++++++++++++- src/main.cpp | 8 ++-- src/platform/portduino/PiHal.h | 31 ++++---------- src/platform/portduino/PortduinoGlue.cpp | 52 +++++++++++++++++++++++- src/platform/portduino/PortduinoGlue.h | 3 +- 6 files changed, 96 insertions(+), 31 deletions(-) diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index 6c8f1946f..d6cf8f878 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -24,3 +24,8 @@ Lora: # Reset: 22 # CS: 7 # IRQ: 25 + +# Define GPIO buttons here: + +GPIO: +# User: 6 diff --git a/src/ButtonThread.h b/src/ButtonThread.h index a8a89e82e..2ca5d4c28 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -36,6 +36,9 @@ class ButtonThread : public concurrency::OSThread #endif #ifdef BUTTON_PIN_TOUCH OneButton userButtonTouch; +#endif +#if defined(ARCH_RASPBERRY_PI) + OneButton userButton; #endif static bool shutdown_on_long_stop; @@ -45,8 +48,13 @@ class ButtonThread : public concurrency::OSThread // callback returns the period for the next callback invocation (or 0 if we should no longer be called) ButtonThread() : OSThread("Button") { -#ifdef BUTTON_PIN +#if defined(ARCH_RASPBERRY_PI) + if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) + userButton = OneButton(settingsMap[user], true, true); +#elif defined(BUTTON_PIN) + userButton = OneButton(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, true, true); +#endif #ifdef INPUT_PULLUP_SENSE // Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT_PULLUP_SENSE); @@ -58,6 +66,10 @@ class ButtonThread : public concurrency::OSThread userButton.attachMultiClick(userButtonMultiPressed); userButton.attachLongPressStart(userButtonPressedLongStart); userButton.attachLongPressStop(userButtonPressedLongStop); +#if defined(ARCH_RASPBERRY_PI) + if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) + wakeOnIrq(settingsMap[user], FALLING); +#else wakeOnIrq(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, FALLING); #endif #ifdef BUTTON_PIN_ALT @@ -87,9 +99,14 @@ class ButtonThread : public concurrency::OSThread { canSleep = true; // Assume we should not keep the board awake -#ifdef BUTTON_PIN +#if defined(BUTTON_PIN) userButton.tick(); canSleep &= userButton.isIdle(); +#elif defined(ARCH_RASPBERRY_PI) + if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) { + userButton.tick(); + canSleep &= userButton.isIdle(); + } #endif #ifdef BUTTON_PIN_ALT userButtonAlt.tick(); @@ -121,6 +138,13 @@ class ButtonThread : public concurrency::OSThread !moduleConfig.canned_message.enabled) { powerFSM.trigger(EVENT_PRESS); } +#endif +#if defined(ARCH_RASPBERRY_PI) + if ((settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) && + (settingsMap[user] != moduleConfig.canned_message.inputbroker_pin_press) || + !moduleConfig.canned_message.enabled) { + powerFSM.trigger(EVENT_PRESS); + } #endif } static void userButtonPressedLong() diff --git a/src/main.cpp b/src/main.cpp index 4913f1091..b8432c5ad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,7 +75,7 @@ NRF52Bluetooth *nrf52Bluetooth; #include #endif -#if HAS_BUTTON +#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI) #include "ButtonThread.h" #endif #include "PowerFSMThread.h" @@ -206,13 +206,13 @@ static int32_t ledBlinker() uint32_t timeLastPowered = 0; -#if HAS_BUTTON +#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI) bool ButtonThread::shutdown_on_long_stop = false; #endif static Periodic *ledPeriodic; static OSThread *powerFSMthread; -#if HAS_BUTTON +#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI) static OSThread *buttonThread; uint32_t ButtonThread::longPressTime = 0; #endif @@ -583,7 +583,7 @@ void setup() else router = new ReliableRouter(); -#if HAS_BUTTON +#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI) // Buttons. Moved here cause we need NodeDB to be initialized buttonThread = new ButtonThread(); #endif diff --git a/src/platform/portduino/PiHal.h b/src/platform/portduino/PiHal.h index f10040583..d80009450 100644 --- a/src/platform/portduino/PiHal.h +++ b/src/platform/portduino/PiHal.h @@ -7,6 +7,8 @@ // include the library for Raspberry GPIO pins #include "pigpio.h" +#include "linux/gpio/LinuxGPIOPin.h" + // create a new Raspberry Pi hardware abstraction layer // using the pigpio library // the HAL must inherit from the base RadioLibHal class @@ -54,8 +56,7 @@ class PiHal : public RadioLibHal if (pin == RADIOLIB_NC) { return; } - - gpioSetMode(pin, mode); + ::pinMode(pin, RADIOLIB_ARDUINOHAL_PIN_MODE_CAST mode); } void digitalWrite(uint32_t pin, uint32_t value) override @@ -63,8 +64,7 @@ class PiHal : public RadioLibHal if (pin == RADIOLIB_NC) { return; } - - gpioWrite(pin, value); + ::digitalWrite(pin, RADIOLIB_ARDUINOHAL_PIN_STATUS_CAST value); } uint32_t digitalRead(uint32_t pin) override @@ -73,7 +73,7 @@ class PiHal : public RadioLibHal return (0); } - return (gpioRead(pin)); + return (::digitalRead(pin)); } void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override @@ -81,11 +81,7 @@ class PiHal : public RadioLibHal if (interruptNum == RADIOLIB_NC) { return; } - if (gpioRead(interruptNum) == 1) { - interruptCb(); - } else { - gpioSetAlertFunc(interruptNum, (gpioISRFunc_t)interruptCb); - } + ::attachInterrupt(interruptNum, interruptCb, RADIOLIB_ARDUINOHAL_INTERRUPT_MODE_CAST mode); } void detachInterrupt(uint32_t interruptNum) override @@ -94,7 +90,7 @@ class PiHal : public RadioLibHal return; } - gpioSetAlertFunc(interruptNum, NULL); + ::detachInterrupt(interruptNum); } void delay(unsigned long ms) override { gpioDelay(ms * 1000); } @@ -111,19 +107,8 @@ class PiHal : public RadioLibHal 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); + return (::pulseIn(pin, state, timeout)); } - void spiBegin() { if (_spiHandle < 0) { diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index d2a00e1e1..f7389431b 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -10,6 +10,7 @@ #ifdef ARCH_RASPBERRY_PI #include "PortduinoGlue.h" +#include "linux/gpio/LinuxGPIOPin.h" #include "pigpio.h" #include "yaml-cpp/yaml.h" #include @@ -101,6 +102,7 @@ void portduinoSetup() printf("Setting up Meshtastic on Portduino...\n"); #ifdef ARCH_RASPBERRY_PI + gpioInit(); YAML::Node yamlConfig; if (access("config.yaml", R_OK) == 0) { @@ -138,6 +140,9 @@ void portduinoSetup() settingsMap[busy] = yamlConfig["Lora"]["Busy"].as(RADIOLIB_NC); settingsMap[reset] = yamlConfig["Lora"]["Reset"].as(RADIOLIB_NC); } + if (yamlConfig["GPIO"]) { + settingsMap[user] = yamlConfig["GPIO"]["User"].as(RADIOLIB_NC); + } } catch (YAML::Exception e) { std::cout << "*** Exception " << e.what() << std::endl; @@ -147,6 +152,34 @@ void portduinoSetup() std::cout << "Cannot read Bluetooth MAC Address. Please run as root" << std::endl; exit(EXIT_FAILURE); } + + // Need to bind all the configured GPIO pins so they're not simulated + if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[cs]) != ERRNO_OK) { + settingsMap[cs] = RADIOLIB_NC; + } + } + if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[irq]) != ERRNO_OK) { + settingsMap[irq] = RADIOLIB_NC; + } + } + if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[busy]) != ERRNO_OK) { + settingsMap[busy] = RADIOLIB_NC; + } + } + if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[reset]) != ERRNO_OK) { + settingsMap[reset] = RADIOLIB_NC; + } + } + if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[user]) != ERRNO_OK) { + settingsMap[user] = RADIOLIB_NC; + } + } + return; #endif @@ -194,4 +227,21 @@ void portduinoSetup() // gpioBind((new SimGPIOPin(LORA_RESET, "LORA_RESET"))); // gpioBind((new SimGPIOPin(LORA_CS, "LORA_CS"))->setSilent()); #endif -} \ No newline at end of file +} + +#ifdef ARCH_RASPBERRY_PI +int initGPIOPin(int pinNum) +{ + std::string gpio_name = "GPIO" + std::to_string(pinNum); + try { + GPIOPin *csPin; + csPin = new LinuxGPIOPin(pinNum, "gpiochip0", pinNum, gpio_name.c_str()); + csPin->setSilent(); + gpioBind(csPin); + return ERRNO_OK; + } catch (std::invalid_argument &e) { + std::cout << "Warning, cannot claim pin" << gpio_name << std::endl; + return ERRNO_DISABLED; + } +} +#endif \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 7dc563038..7544a0853 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -4,6 +4,7 @@ extern std::map settingsMap; -enum { use_sx1262, cs, irq, busy, reset, dio2_as_rf_switch, use_rf95 }; +enum { use_sx1262, cs, irq, busy, reset, dio2_as_rf_switch, use_rf95, user }; +int initGPIOPin(int pinNum); #endif \ No newline at end of file