diff --git a/open-x4-sdk b/open-x4-sdk index be0cb2b..8224d27 160000 --- a/open-x4-sdk +++ b/open-x4-sdk @@ -1 +1 @@ -Subproject commit be0cb2bb34eae837d6cb36d04a81cb20399366d0 +Subproject commit 8224d278c58e76abf781c2e015f28a09419f27b2 diff --git a/platformio.ini b/platformio.ini index 43aa895..5632a69 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,6 +17,7 @@ board_build.partitions = default_16MB.csv lib_deps = zinggjm/GxEPD2@^1.5.9 BatteryMonitor=symlink://open-x4-sdk/libs/hardware/BatteryMonitor + InputManager=symlink://open-x4-sdk/libs/hardware/InputManager ; Build flags build_flags = diff --git a/src/main.cpp b/src/main.cpp index a176ca9..57360cd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,11 +5,10 @@ #include #include #include -#include #include "image.h" #include "BatteryMonitor.h" - +#include "InputManager.h" #define SPI_FQ 40000000 // Display SPI pins (custom pins for XteinkX4, not hardware SPI defaults) @@ -20,11 +19,6 @@ #define EPD_RST 5 // Reset #define EPD_BUSY 6 // Busy -// Button pins -#define BTN_GPIO1 1 // 4 buttons on ADC resistor ladder: Back, Confirm, Left, Right -#define BTN_GPIO2 2 // 2 buttons on ADC resistor ladder: Volume Up, Volume Down -#define BTN_GPIO3 3 // Power button (digital) - #define UART0_RXD 20 // Used for USB connection detection #define BAT_GPIO0 0 // Battery voltage @@ -32,10 +26,9 @@ #define SD_SPI_MISO 7 static bool g_sdReady = false; - -static BatteryMonitor g_battery(BAT_GPIO0); - static int rawBat = 0; +static BatteryMonitor g_battery(BAT_GPIO0); +static InputManager input_manager; // Display command enum enum DisplayCommand @@ -57,104 +50,10 @@ GxEPD2_BW display( // FreeRTOS task for non-blocking display updates TaskHandle_t displayTaskHandle = NULL; -// Button ADC thresholds -const int BTN_THRESHOLD = 100; // Threshold tolerance -const int BTN_RIGHT_VAL = 3; -const int BTN_LEFT_VAL = 1470; -const int BTN_CONFIRM_VAL = 2655; -const int BTN_BACK_VAL = 3470; -const int BTN_VOLUME_DOWN_VAL = 3; -const int BTN_VOLUME_UP_VAL = 2205; - -// Button enum -enum Button -{ - NONE = 0, - RIGHT, - LEFT, - CONFIRM, - BACK, - VOLUME_UP, - VOLUME_DOWN, - POWER -}; - -static Button lastButton = NONE; -volatile Button currentPressedButton = NONE; - // Power button timing const unsigned long POWER_BUTTON_WAKEUP_MS = 1000; // Time required to confirm boot from sleep const unsigned long POWER_BUTTON_SLEEP_MS = 1000; // Time required to enter sleep mode - -// Get button name as string -const char *getButtonName(Button btn) -{ - switch (btn) - { - case NONE: - return "Press any button"; - case RIGHT: - return "RIGHT pressed!"; - case LEFT: - return "LEFT pressed!"; - case CONFIRM: - return "CONFIRM pressed!"; - case BACK: - return "BACK pressed!"; - case VOLUME_UP: - return "VOLUME UP pressed!"; - case VOLUME_DOWN: - return "VOLUME DOWN pressed!"; - case POWER: - return "POWER pressed!"; - default: - return ""; - } -} - -// Get currently pressed button by reading ADC values (and digital for power button) -Button GetPressedButton() -{ - int btn1 = analogRead(BTN_GPIO1); - int btn2 = analogRead(BTN_GPIO2); - - // Check BTN_GPIO3 (Power button) - digital read - if (digitalRead(BTN_GPIO3) == LOW) - { - return POWER; - } - // Check BTN_GPIO1 (4 buttons on resistor ladder) - if (btn1 < BTN_RIGHT_VAL + BTN_THRESHOLD) - { - return RIGHT; - } - else if (btn1 < BTN_LEFT_VAL + BTN_THRESHOLD) - { - return LEFT; - } - else if (btn1 < BTN_CONFIRM_VAL + BTN_THRESHOLD) - { - return CONFIRM; - } - else if (btn1 < BTN_BACK_VAL + BTN_THRESHOLD) - { - return BACK; - } - - // Check BTN_GPIO2 (2 buttons on resistor ladder) - if (btn2 < BTN_VOLUME_DOWN_VAL + BTN_THRESHOLD) - { - return VOLUME_DOWN; - } - else if (btn2 < BTN_VOLUME_UP_VAL + BTN_THRESHOLD) - { - return VOLUME_UP; - } - - return NONE; -} - // Check if charging bool isCharging() { @@ -285,7 +184,24 @@ void displayUpdateTask(void *parameter) // Button text with smaller font display.setFont(&FreeMonoBold12pt7b); display.setCursor(20, 100); - display.print(getButtonName(currentPressedButton)); + bool anyPressed = false; + for (int i = 0; i <= 6; i++) + { + if (input_manager.isPressed(i)) + { + if (!anyPressed) + { + display.print("Pressing:"); + anyPressed = true; + } + display.print(" "); + display.print(InputManager::getButtonName(i)); + } + } + if (!anyPressed) + { + display.print("Press any button"); + } // Draw battery information drawBatteryInfo(); @@ -311,7 +227,24 @@ void displayUpdateTask(void *parameter) display.fillScreen(GxEPD_WHITE); display.setFont(&FreeMonoBold12pt7b); display.setCursor(20, 100); - display.print(getButtonName(currentPressedButton)); + bool anyPressed = false; + for (int i = 0; i <= 6; i++) + { + if (input_manager.isPressed(i)) + { + if (!anyPressed) + { + display.print("Pressing:"); + anyPressed = true; + } + display.print(" "); + display.print(InputManager::getButtonName(i)); + } + } + if (!anyPressed) + { + display.print("Press any button"); + } drawBatteryInfo(); } while (display.nextPage()); } @@ -348,46 +281,37 @@ void displayUpdateTask(void *parameter) // Verify long press on wake-up from deep sleep void verifyWakeupLongPress() { - // Temporarily configure pin as digital input to check state - pinMode(BTN_GPIO3, INPUT_PULLUP); - - unsigned long pressStart = millis(); bool abortBoot = false; + input_manager.update(); - // Monitor button state for the duration - while (millis() - pressStart < POWER_BUTTON_WAKEUP_MS) + while (input_manager.getHeldTime() < POWER_BUTTON_WAKEUP_MS) { - // If button reads HIGH (released) before time is up - if (digitalRead(BTN_GPIO3) == HIGH) + delay(10); + input_manager.update(); + if (!input_manager.isPressed(InputManager::BTN_POWER)) { abortBoot = true; break; } - delay(10); } if (abortBoot) { // Button released too early. Returning to sleep. // IMPORTANT: Re-arm the wakeup trigger before sleeping again - esp_deep_sleep_enable_gpio_wakeup(1ULL << BTN_GPIO3, ESP_GPIO_WAKEUP_GPIO_LOW); + esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW); esp_deep_sleep_start(); } - else - { - lastButton = POWER; - } } // Enter deep sleep mode void enterDeepSleep() { displayCommand = DISPLAY_SLEEP; - Serial.println("Power button released after a long press. Entering deep sleep."); delay(2000); // Allow Serial buffer to empty and display to update // Enable Wakeup on LOW (button press) - esp_deep_sleep_enable_gpio_wakeup(1ULL << BTN_GPIO3, ESP_GPIO_WAKEUP_GPIO_LOW); + esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW); // Enter Deep Sleep esp_deep_sleep_start(); @@ -395,6 +319,9 @@ void enterDeepSleep() void setup() { + // Initialize inputs + input_manager.begin(); + // Check if boot was triggered by the Power Button (Deep Sleep Wakeup) // If triggered by RST pin or Battery insertion, this will be false, allowing normal boot. if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_GPIO) @@ -423,12 +350,6 @@ void setup() Serial.println("================================="); Serial.println(); - // Initialize button pins - pinMode(BAT_GPIO0, INPUT); - pinMode(BTN_GPIO1, INPUT); - pinMode(BTN_GPIO2, INPUT); - pinMode(BTN_GPIO3, INPUT_PULLUP); // Power button - // Initialize SPI with custom pins SPI.begin(EPD_SCLK,SD_SPI_MISO, EPD_MOSI, EPD_CS); // Initialize display @@ -454,7 +375,6 @@ void setup() // Draw initial welcome screen - currentPressedButton = NONE; displayCommand = DISPLAY_INITIAL; // Create display update task on core 0 (main loop runs on core 1) @@ -469,25 +389,33 @@ void setup() Serial.println("Display task created"); Serial.println("Setup complete!\n"); + + // Avoid entering main loop while still holding power on button + while (input_manager.isPressed(InputManager::BTN_POWER)) + { + delay(10); + input_manager.update(); + } } #ifdef DEBUG_IO void debugIO() { - // Log raw analog levels of BTN1 and BTN2 not more often than once per second - rawBat = analogRead(BAT_GPIO0); - int rawBtn1 = analogRead(BTN_GPIO1); - int rawBtn2 = analogRead(BTN_GPIO2); - int rawBtn3 = digitalRead(BTN_GPIO3); - Serial.print("ADC BTN1="); - Serial.print(rawBtn1); - Serial.print(" BTN2="); - Serial.print(rawBtn2); - Serial.print(" BTN3="); - Serial.print(rawBtn3); - Serial.println(""); + // log each button + Serial.println("== Buttons =="); + for (int i = 0; i <= 6; i++) + { + Serial.printf( + "%s - wasPressed: %s, wasReleased: %s, isPressed: %s\n", + InputManager::getButtonName(i), + input_manager.wasPressed(i) ? "yes" : "no", + input_manager.wasReleased(i) ? "yes" : "no", + input_manager.isPressed(i) ? "yes" : "no" + ); + } // log battery info + rawBat = analogRead(BAT_GPIO0); Serial.printf("== Battery (charging: %s) ==\n", isCharging() ? "yes" : "no"); Serial.print("Value from pin (raw/calibrated): "); Serial.print(rawBat); @@ -506,36 +434,24 @@ void debugIO() void loop() { - Button currentButton = GetPressedButton(); + input_manager.update(); - // Detect button press (transition from NONE to a button) - if (currentButton != NONE && lastButton == NONE) + if (input_manager.wasAnyPressed() || input_manager.wasAnyReleased()) { - Serial.print("Button: "); - Serial.println(getButtonName(currentButton)); - - currentPressedButton = currentButton; displayCommand = DISPLAY_TEXT; #ifdef DEBUG_IO debugIO(); #endif - if (currentButton == POWER) - { - unsigned long startTime = millis(); - // Wait for button release - while (digitalRead(BTN_GPIO3) == LOW) - delay(50); - - unsigned long currentTime = millis(); + if (input_manager.wasReleased(InputManager::BTN_POWER)) { // Power button long pressed => go to sleep - if (currentTime - startTime > POWER_BUTTON_SLEEP_MS) + if (input_manager.getHeldTime() > POWER_BUTTON_SLEEP_MS) { + Serial.printf("Power button released after %lums. Entering deep sleep.\n", input_manager.getHeldTime()); enterDeepSleep(); + } } } - lastButton = currentButton; - delay(50); }