diff --git a/lib/BUTTON/button.h b/lib/BUTTON/button.h index a8d3b1de..cb5c0a45 100644 --- a/lib/BUTTON/button.h +++ b/lib/BUTTON/button.h @@ -30,6 +30,8 @@ class Button // Properties uint8_t getCount() const { return _pressCount; } uint8_t getLongCount() const { return _longCount; } + bool isPressed() const { return _state == STATE_HELD; } + bool isIdle() const { return _state == STATE_IDLE; } Button() : _lastCheck(0), _lastFallingEdge(0), _state(STATE_IDLE), diff --git a/platformio.ini b/platformio.ini index 7bad0d8b..396cbdf3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,6 +12,7 @@ extra_configs = targets/fusion.ini targets/hdzero.ini targets/orqa.ini + targets/pedal.ini targets/rapidfire.ini targets/rx5808.ini targets/skyzone.ini diff --git a/src/Vrx_main.cpp b/src/Vrx_main.cpp index e9921275..ee9ae40b 100644 --- a/src/Vrx_main.cpp +++ b/src/Vrx_main.cpp @@ -23,7 +23,9 @@ #include "devWIFI.h" #include "devButton.h" #include "devLED.h" +#if defined(HAS_HEADTRACKING) #include "devHeadTracker.h" +#endif #ifdef RAPIDFIRE_BACKPACK #include "rapidfire.h" @@ -43,6 +45,8 @@ #include "module_aat.h" #elif defined(CROSSBOW_BACKPACK) #include "mfd_crossbow.h" +#elif defined(PEDAL_BACKPACK) + #include "module_pedal.h" #endif /////////// DEFINES /////////// @@ -124,6 +128,8 @@ VrxBackpackConfig config; AatModule vrxModule(Serial); #elif defined(CROSSBOW_BACKPACK) MFDCrossbow vrxModule(&Serial); +#elif defined(PEDAL_BACKPACK) + PedalModule vrxModule; #endif /////////// FUNCTION DEFS /////////// @@ -341,7 +347,7 @@ void SetSoftMACAddress() void RequestVTXPacket() { -#if !defined(AAT_BACKPACK) and !defined(CROSSBOW_BACKPACK) +#if !defined(AAT_BACKPACK) && !defined(CROSSBOW_BACKPACK) && !defined(PEDAL_BACKPACK) mspPacket_t packet; packet.reset(); packet.makeCommand(); diff --git a/src/module_pedal.cpp b/src/module_pedal.cpp new file mode 100644 index 00000000..f7d92b2d --- /dev/null +++ b/src/module_pedal.cpp @@ -0,0 +1,123 @@ +#if defined(PEDAL_BACKPACK) + +#include "module_pedal.h" +#include "common.h" +#include "msptypes.h" +#if defined(PIN_LED) + #include "devLED.h" +#endif + +void sendMSPViaEspnow(mspPacket_t *packet); +bool BindingExpired(uint32_t now); + +PedalModule::PedalModule() + : _lastTxValue(false), _lastNow(0), _lastTxMs(0), _lastPedalChangeMs(0) +{ + _btnBind.OnLongPress = std::bind(&PedalModule::button_OnLongPress, this); +} + +PedalModule::~PedalModule() +{ + detachInterrupt(PIN_BUTTON_PEDAL); +} + +void PedalModule::Init() +{ + _pedalSemaphore = xSemaphoreCreateBinary(); + attachInterruptArg(PIN_BUTTON_PEDAL, &PedalModule::button_interrupt, this, CHANGE); +} + +void PedalModule::Loop(uint32_t now) +{ + // Need to store what the main loop thinks is "now" in case the button + // event is going to use it to set the bindstart time, otherwise millis() + // could be in the future (compared to now) and timeout immediately + _lastNow = now; + if (BindingExpired(_lastNow)) + { + connectionState = running; + } + + // Pause here to lower power usage (C3 drops from 100mA to 90mA) + // If button is currently transitioning or pressed just consume the semaphore, else delay until an interrupt + xSemaphoreTake(_pedalSemaphore, _btnPedal.isIdle() ? pdMS_TO_TICKS(20) : 0); + _btnPedal.update(); + _btnBind.update(); + + // Don't TX if binding/wifi + if (connectionState != running) + return; + // Don't TX on startup + if (_lastTxMs == 0 && _lastNow < STARTUP_MS) + return; + + checkSendPedalPos(); +} + +void PedalModule::checkSendPedalPos() +{ + bool pedalPressed = _btnPedal.isPressed(); + bool pedalChanged = pedalPressed != _lastTxValue; + + if (pedalChanged) + { + _lastPedalChangeMs = _lastNow; + } + + // Send the pedal position frequently after last change, until it is unchanged for one UNCHANGED_MS interval + uint32_t txIntervalMs = (_lastNow - _lastPedalChangeMs < PEDAL_INTERVAL_UNCHANGED_MS) ? PEDAL_INTERVAL_CHANGED_MS : PEDAL_INTERVAL_UNCHANGED_MS; + if (pedalChanged || _lastNow - _lastTxMs > txIntervalMs) + { + _lastTxMs = _lastNow; + _lastTxValue = pedalPressed; + DBGLN("%u pedal %u", _lastNow, pedalPressed); + + // Pedal is 1000us if not pressed, 2000us if pressed + uint16_t pedalState = pedalPressed ? CRSF_CHANNEL_VALUE_2000 : CRSF_CHANNEL_VALUE_1000; + mspPacket_t packet; + packet.reset(); + packet.makeCommand(); + packet.function = MSP_ELRS_BACKPACK_SET_PTR; + packet.addByte(pedalState & 0xFF); // CH0 + packet.addByte(pedalState >> 8); + // ExpressLRS 4.0 supports only sending as many channels as needed, but for 3.x compatibility + // 6 total payload bytes are required. A channel value of 0xffff means "do not update" in 4.0 + packet.addByte(0xff); // CH1 + packet.addByte(0xff); + packet.addByte(0xff); // CH2 + packet.addByte(0xff); + sendMSPViaEspnow(&packet); + yield(); + +#if defined(PIN_LED) + // Flash the LED to indicate the high-speed pedal update mode + if (pedalPressed) + blinkLED(); +#endif + } +} + +void IRAM_ATTR PedalModule::button_interrupt(void *instance) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xSemaphoreGiveFromISR(((PedalModule *)instance)->_pedalSemaphore, &xHigherPriorityTaskWoken); + if (xHigherPriorityTaskWoken) + portYIELD_FROM_ISR(); +} + +void PedalModule::button_OnLongPress() +{ + // Long press of the bind button for 5s takes the pedal into binding mode + if (_btnBind.getLongCount() >= 10 && connectionState == running) + { + bindingStart = _lastNow; + connectionState = binding; + } + // Long press of bind button for 10s goes from binding to wifi + else if (_btnBind.getLongCount() >= 20 && connectionState == binding) + { + connectionState = wifiUpdate; + } +} + +#endif /* PEDAL_BACKPACK */ \ No newline at end of file diff --git a/src/module_pedal.h b/src/module_pedal.h new file mode 100644 index 00000000..316c800a --- /dev/null +++ b/src/module_pedal.h @@ -0,0 +1,41 @@ +#pragma once + +#if defined(PEDAL_BACKPACK) +#include "module_base.h" +#include "logging.h" +#include "button.h" + +// For wake +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +class PedalModule : public ModuleBase +{ +public: + PedalModule(); + ~PedalModule(); + + void Init(); + void Loop(uint32_t now); +private: + static void button_interrupt(void *instance); + + // Don't send updates on for a short time after bootup + static constexpr uint32_t STARTUP_MS = 2000U; + // Transmit interval differs based on how recently the pedal changed position + static constexpr uint32_t PEDAL_INTERVAL_UNCHANGED_MS = 750U; + static constexpr uint32_t PEDAL_INTERVAL_CHANGED_MS = 100U; + + void button_OnLongPress(); + void checkSendPedalPos(); + + Button _btnPedal; + Button _btnBind; + SemaphoreHandle_t _pedalSemaphore; + bool _lastTxValue; + uint32_t _lastNow; + uint32_t _lastTxMs; + uint32_t _lastPedalChangeMs; +}; + +#endif /* PEDAL_BACKPACK */ \ No newline at end of file diff --git a/targets/aat.ini b/targets/aat.ini index 283e5d01..8e51b153 100644 --- a/targets/aat.ini +++ b/targets/aat.ini @@ -21,7 +21,7 @@ build_flags = lib_deps = ${env.lib_deps} adafruit/Adafruit SSD1306 @ 2.5.9 -build_src_filter = ${common_env_data.build_src_filter} - - - - - - +build_src_filter = ${common_env_data.build_src_filter} - - - - - - - - - [env:AAT_ESP_Backpack_via_WIFI] extends = env:AAT_ESP_Backpack_via_UART diff --git a/targets/common.ini b/targets/common.ini index c8b200bd..5c9e699a 100644 --- a/targets/common.ini +++ b/targets/common.ini @@ -10,7 +10,6 @@ lib_deps = ottowinter/ESPAsyncWebServer-esphome @ 3.2.2 esphome/AsyncTCP-esphome @ 2.1.3 # use specific version - an update to this library breaks the build bblanchon/ArduinoJson @ 7.1.0 -monitor_filters = esp8266_exception_decoder [common_env_data] @@ -26,6 +25,7 @@ board_build.ldscript = eagle.flash.1m.ld upload_speed = 460800 monitor_speed = 460800 board_build.f_cpu = 160000000L +monitor_filters = esp8266_exception_decoder build_flags = -D PLATFORM_ESP8266=1 @@ -36,6 +36,7 @@ board_build.ldscript = eagle.flash.1m.ld upload_speed = 921600 monitor_speed = 460800 board_build.f_cpu = 160000000L +monitor_filters = esp8266_exception_decoder build_flags = -D PLATFORM_ESP8266=1 @@ -90,7 +91,7 @@ build_src_filter = - - - - - + - - - - @@ -110,7 +111,7 @@ build_src_filter = ; - - - - - + - - - - @@ -130,7 +131,7 @@ build_src_filter = - ; - - - - + - - - - @@ -150,7 +151,7 @@ build_src_filter = - - ; - - - + - - - - @@ -170,7 +171,7 @@ build_src_filter = - - - - ; - + ; - - - - @@ -190,7 +191,7 @@ build_src_filter = - - - - - + - ; - - - @@ -210,7 +211,7 @@ build_src_filter = - - - - - + - - ; - - @@ -230,7 +231,7 @@ build_src_filter = - - - - - + - - - ; - @@ -249,7 +250,7 @@ build_src_filter = - - - - - + - - - - @@ -269,7 +270,7 @@ build_src_filter = - - - - - + - - - - diff --git a/targets/pedal.ini b/targets/pedal.ini new file mode 100644 index 00000000..1d4be1ab --- /dev/null +++ b/targets/pedal.ini @@ -0,0 +1,64 @@ +# ******************************** +# Pedal VRX backpack -- External pedal trigger using headtracking messages +# +# Connections +# PIN_BUTTON_PEDAL - Pedal input. Connect the NO wire from the pedal and its common wire to GND +# PIN_BUTTON_BIND - Bind button. Behaves differently than the standard button. +# Hold for 5 seconds to enter bind mode +# Hold for 10 seconds to enter wifi mode +# PIN_LED - Standard monochrome LED +# PIN_BUTTON_PEDAL and PIN_BUTTON_BIND can be wired/switched together to make the pedal also work as the bind button +# ******************************** + +[pedal_backpack_common] +board_build.f_cpu = 80000000L +build_flags = + ${common_env_data.build_flags} + -D TARGET_VRX_BACKPACK + -D PEDAL_BACKPACK + -DDEBUG_LOG -DDEBUG_LOG_VERBOSE +build_src_filter = + ${common_env_data.build_src_filter} + - + ; - + - + - + - + - + - + - + - + - + - + - + +[env:Pedal_ESP32_Backpack_via_UART] +extends = env_common_esp32, pedal_backpack_common +build_flags = + ${env_common_esp32.build_flags} + ${pedal_backpack_common.build_flags} + -D PIN_LED=2 + -D PIN_BUTTON_PEDAL=15 + -D PIN_BUTTON_BIND=0 + +[env:Pedal_ESP32C3_Backpack_via_UART] +extends = env_common_esp32c3, pedal_backpack_common +build_flags = + ${env_common_esp32c3.build_flags} + ${pedal_backpack_common.build_flags} + -D ARDUINO_USB_CDC_ON_BOOT=1 + -D PIN_LED=8 + -D LED_INVERTED + -D PIN_BUTTON_PEDAL=4 + -D PIN_BUTTON_BIND=9 + +[env:Pedal_ESP32S3_Backpack_via_UART] +extends = env_common_esp32s3, pedal_backpack_common +board_upload.flash_size = 4MB +build_flags = + ${env_common_esp32s3.build_flags} + ${pedal_backpack_common.build_flags} + -D ARDUINO_USB_CDC_ON_BOOT=1 + -D PIN_LED=48 + -D PIN_BUTTON_PEDAL=13 + -D PIN_BUTTON_BIND=0