A fork of https://github.com/crosspoint-reader/crosspoint-reader
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Use InputManager from community-sdk

+133 -162
+1
platformio.ini
··· 34 34 zinggjm/GxEPD2@^1.6.5 35 35 https://github.com/leethomason/tinyxml2.git#11.0.0 36 36 BatteryMonitor=symlink://open-x4-sdk/libs/hardware/BatteryMonitor 37 + InputManager=symlink://open-x4-sdk/libs/hardware/InputManager
-43
src/Input.cpp
··· 1 - #include "Input.h" 2 - 3 - #include <esp32-hal-adc.h> 4 - 5 - void setupInputPinModes() { 6 - pinMode(BTN_GPIO1, INPUT); 7 - pinMode(BTN_GPIO2, INPUT); 8 - pinMode(BTN_GPIO3, INPUT_PULLUP); // Power button 9 - } 10 - 11 - // Get currently pressed button by reading ADC values (and digital for power 12 - // button) 13 - Button getPressedButton() { 14 - // Check BTN_GPIO3 (Power button) - digital read 15 - if (digitalRead(BTN_GPIO3) == LOW) return POWER; 16 - 17 - // Check BTN_GPIO1 (4 buttons on resistor ladder) 18 - const int btn1 = analogRead(BTN_GPIO1); 19 - if (btn1 < BTN_RIGHT_VAL + BTN_THRESHOLD) return RIGHT; 20 - if (btn1 < BTN_LEFT_VAL + BTN_THRESHOLD) return LEFT; 21 - if (btn1 < BTN_CONFIRM_VAL + BTN_THRESHOLD) return CONFIRM; 22 - if (btn1 < BTN_BACK_VAL + BTN_THRESHOLD) return BACK; 23 - 24 - // Check BTN_GPIO2 (2 buttons on resistor ladder) 25 - const int btn2 = analogRead(BTN_GPIO2); 26 - if (btn2 < BTN_VOLUME_DOWN_VAL + BTN_THRESHOLD) return VOLUME_DOWN; 27 - if (btn2 < BTN_VOLUME_UP_VAL + BTN_THRESHOLD) return VOLUME_UP; 28 - 29 - return NONE; 30 - } 31 - 32 - Input getInput(const long maxHoldMs) { 33 - const Button button = getPressedButton(); 34 - if (button == NONE) return {NONE, 0}; 35 - 36 - const auto start = millis(); 37 - unsigned long held = 0; 38 - while (getPressedButton() == button && (maxHoldMs < 0 || held < maxHoldMs)) { 39 - delay(50); 40 - held = millis() - start; 41 - } 42 - return {button, held}; 43 - }
-28
src/Input.h
··· 1 - #pragma once 2 - 3 - // 4 buttons on ADC resistor ladder: Back, Confirm, Left, Right 4 - #define BTN_GPIO1 1 5 - // 2 buttons on ADC resistor ladder: Volume Up, Volume Down 6 - #define BTN_GPIO2 2 7 - // Power button (digital) 8 - #define BTN_GPIO3 3 9 - 10 - // Button ADC thresholds 11 - #define BTN_THRESHOLD 100 // Threshold tolerance 12 - #define BTN_RIGHT_VAL 3 13 - #define BTN_LEFT_VAL 1500 14 - #define BTN_CONFIRM_VAL 2700 15 - #define BTN_BACK_VAL 3550 16 - #define BTN_VOLUME_DOWN_VAL 3 17 - #define BTN_VOLUME_UP_VAL 2305 18 - 19 - enum Button { NONE = 0, RIGHT, LEFT, CONFIRM, BACK, VOLUME_UP, VOLUME_DOWN, POWER }; 20 - 21 - struct Input { 22 - Button button; 23 - unsigned long pressTime; 24 - }; 25 - 26 - void setupInputPinModes(); 27 - Button getPressedButton(); 28 - Input getInput(long maxHoldMs = -1);
+44 -30
src/main.cpp
··· 2 2 #include <EpdRenderer.h> 3 3 #include <Epub.h> 4 4 #include <GxEPD2_BW.h> 5 + #include <InputManager.h> 5 6 #include <SD.h> 6 7 #include <SPI.h> 7 8 8 9 #include "Battery.h" 9 10 #include "CrossPointState.h" 10 - #include "Input.h" 11 11 #include "screens/BootLogoScreen.h" 12 12 #include "screens/EpubReaderScreen.h" 13 13 #include "screens/FileSelectionScreen.h" ··· 30 30 31 31 GxEPD2_BW<GxEPD2_426_GDEQ0426T82, GxEPD2_426_GDEQ0426T82::HEIGHT> display(GxEPD2_426_GDEQ0426T82(EPD_CS, EPD_DC, 32 32 EPD_RST, EPD_BUSY)); 33 + InputManager inputManager; 33 34 auto renderer = new EpdRenderer(&display); 34 35 Screen* currentScreen; 35 36 CrossPointState* appState; ··· 72 73 void verifyWakeupLongPress() { 73 74 // Give the user up to 1000ms to start holding the power button, and must hold for POWER_BUTTON_WAKEUP_MS 74 75 const auto start = millis(); 75 - auto input = getInput(POWER_BUTTON_WAKEUP_MS); 76 - while (input.button != POWER && millis() - start < 1000) { 76 + bool abort = false; 77 + 78 + Serial.println("Verifying power button press"); 79 + inputManager.update(); 80 + while (!inputManager.isPressed(InputManager::BTN_POWER) && millis() - start < 1000) { 77 81 delay(50); 78 - input = getInput(POWER_BUTTON_WAKEUP_MS); 82 + inputManager.update(); 83 + Serial.println("Waiting..."); 84 + } 85 + 86 + Serial.printf("Made it? %s\n", inputManager.isPressed(InputManager::BTN_POWER) ? "yes" : "no"); 87 + if (inputManager.isPressed(InputManager::BTN_POWER)) { 88 + do { 89 + delay(50); 90 + inputManager.update(); 91 + } while (inputManager.isPressed(InputManager::BTN_POWER) && inputManager.getHeldTime() < POWER_BUTTON_WAKEUP_MS); 92 + abort = inputManager.getHeldTime() < POWER_BUTTON_WAKEUP_MS; 93 + } else { 94 + abort = true; 79 95 } 80 96 81 - if (input.button != POWER || input.pressTime < POWER_BUTTON_WAKEUP_MS) { 97 + Serial.printf("held for %lu\n", inputManager.getHeldTime()); 98 + 99 + if (abort) { 82 100 // Button released too early. Returning to sleep. 83 101 // IMPORTANT: Re-arm the wakeup trigger before sleeping again 84 - esp_deep_sleep_enable_gpio_wakeup(1ULL << BTN_GPIO3, ESP_GPIO_WAKEUP_GPIO_LOW); 102 + esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW); 85 103 esp_deep_sleep_start(); 86 104 } 87 105 } 88 106 89 - void waitForNoButton() { 90 - while (getInput().button != NONE) { 107 + void waitForPowerRelease() { 108 + inputManager.update(); 109 + while (inputManager.isPressed(InputManager::BTN_POWER)) { 91 110 delay(50); 111 + inputManager.update(); 92 112 } 93 113 } 94 114 95 115 // Enter deep sleep mode 96 116 void enterDeepSleep() { 97 117 exitScreen(); 98 - enterNewScreen(new SleepScreen(renderer)); 118 + enterNewScreen(new SleepScreen(renderer, inputManager)); 99 119 100 120 Serial.println("Power button released after a long press. Entering deep sleep."); 101 121 delay(1000); // Allow Serial buffer to empty and display to update 102 122 103 123 // Enable Wakeup on LOW (button press) 104 - esp_deep_sleep_enable_gpio_wakeup(1ULL << BTN_GPIO3, ESP_GPIO_WAKEUP_GPIO_LOW); 124 + esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW); 105 125 106 126 display.hibernate(); 107 127 ··· 112 132 void onGoHome(); 113 133 void onSelectEpubFile(const std::string& path) { 114 134 exitScreen(); 115 - enterNewScreen(new FullScreenMessageScreen(renderer, "Loading...")); 135 + enterNewScreen(new FullScreenMessageScreen(renderer, inputManager, "Loading...")); 116 136 117 137 Epub* epub = loadEpub(path); 118 138 if (epub) { 119 139 appState->openEpubPath = path; 120 140 appState->saveToFile(); 121 141 exitScreen(); 122 - enterNewScreen(new EpubReaderScreen(renderer, epub, onGoHome)); 142 + enterNewScreen(new EpubReaderScreen(renderer, inputManager, epub, onGoHome)); 123 143 } else { 124 144 exitScreen(); 125 - enterNewScreen(new FullScreenMessageScreen(renderer, "Failed to load epub", REGULAR, false, false)); 145 + enterNewScreen(new FullScreenMessageScreen(renderer, inputManager, "Failed to load epub", REGULAR, false, false)); 126 146 delay(2000); 127 147 onGoHome(); 128 148 } ··· 130 150 131 151 void onGoHome() { 132 152 exitScreen(); 133 - enterNewScreen(new FileSelectionScreen(renderer, onSelectEpubFile)); 153 + enterNewScreen(new FileSelectionScreen(renderer, inputManager, onSelectEpubFile)); 134 154 } 135 155 136 156 void setup() { 137 - setupInputPinModes(); 157 + inputManager.begin(); 138 158 verifyWakeupLongPress(); 139 159 140 160 // Begin serial only if USB connected ··· 157 177 Serial.println("Display initialized"); 158 178 159 179 exitScreen(); 160 - enterNewScreen(new BootLogoScreen(renderer)); 180 + enterNewScreen(new BootLogoScreen(renderer, inputManager)); 161 181 162 182 // SD Card Initialization 163 183 SD.begin(SD_SPI_CS, SPI, SPI_FQ); ··· 167 187 Epub* epub = loadEpub(appState->openEpubPath); 168 188 if (epub) { 169 189 exitScreen(); 170 - enterNewScreen(new EpubReaderScreen(renderer, epub, onGoHome)); 190 + enterNewScreen(new EpubReaderScreen(renderer, inputManager, epub, onGoHome)); 171 191 // Ensure we're not still holding the power button before leaving setup 172 - waitForNoButton(); 192 + waitForPowerRelease(); 173 193 return; 174 194 } 175 195 } 176 196 177 197 exitScreen(); 178 - enterNewScreen(new FileSelectionScreen(renderer, onSelectEpubFile)); 198 + enterNewScreen(new FileSelectionScreen(renderer, inputManager, onSelectEpubFile)); 179 199 180 200 // Ensure we're not still holding the power button before leaving setup 181 - waitForNoButton(); 201 + waitForPowerRelease(); 182 202 } 183 203 184 204 void loop() { 185 - delay(50); 186 - 187 - const Input input = getInput(); 188 - 189 - if (input.button == NONE) { 190 - return; 191 - } 205 + delay(10); 192 206 193 - if (input.button == POWER && input.pressTime > POWER_BUTTON_SLEEP_MS) { 207 + inputManager.update(); 208 + if (inputManager.wasReleased(InputManager::BTN_POWER) && inputManager.getHeldTime() > POWER_BUTTON_WAKEUP_MS) { 194 209 enterDeepSleep(); 195 210 // This should never be hit as `enterDeepSleep` calls esp_deep_sleep_start 196 - delay(1000); 197 211 return; 198 212 } 199 213 200 214 if (currentScreen) { 201 - currentScreen->handleInput(input); 215 + currentScreen->handleInput(); 202 216 } 203 217 }
+1 -1
src/screens/BootLogoScreen.h
··· 3 3 4 4 class BootLogoScreen final : public Screen { 5 5 public: 6 - explicit BootLogoScreen(EpdRenderer* renderer) : Screen(renderer) {} 6 + explicit BootLogoScreen(EpdRenderer* renderer, InputManager& inputManager) : Screen(renderer, inputManager) {} 7 7 void onEnter() override; 8 8 };
+52 -38
src/screens/EpubReaderScreen.cpp
··· 59 59 epub = nullptr; 60 60 } 61 61 62 - void EpubReaderScreen::handleInput(const Input input) { 63 - if (input.button == VOLUME_UP || input.button == VOLUME_DOWN || input.button == LEFT || input.button == RIGHT) { 64 - const bool skipChapter = input.pressTime > SKIP_CHAPTER_MS; 62 + void EpubReaderScreen::handleInput() { 63 + if (inputManager.wasPressed(InputManager::BTN_BACK)) { 64 + onGoHome(); 65 + return; 66 + } 67 + 68 + const bool prevReleased = 69 + inputManager.wasReleased(InputManager::BTN_UP) || inputManager.wasReleased(InputManager::BTN_LEFT); 70 + const bool nextReleased = 71 + inputManager.wasReleased(InputManager::BTN_DOWN) || inputManager.wasReleased(InputManager::BTN_RIGHT); 72 + 73 + if (!prevReleased && !nextReleased) { 74 + return; 75 + } 76 + 77 + Serial.printf("Prev released: %d, Next released: %d\n", prevReleased, nextReleased); 78 + 79 + const bool skipChapter = inputManager.getHeldTime() > SKIP_CHAPTER_MS; 80 + 81 + if (skipChapter) { 82 + // We don't want to delete the section mid-render, so grab the semaphore 83 + xSemaphoreTake(renderingMutex, portMAX_DELAY); 84 + nextPageNumber = 0; 85 + currentSpineIndex = nextReleased ? currentSpineIndex + 1 : currentSpineIndex - 1; 86 + delete section; 87 + section = nullptr; 88 + xSemaphoreGive(renderingMutex); 89 + updateRequired = true; 90 + return; 91 + } 65 92 66 - // No current section, attempt to rerender the book 67 - if (!section) { 68 - updateRequired = true; 69 - return; 70 - } 93 + // No current section, attempt to rerender the book 94 + if (!section) { 95 + updateRequired = true; 96 + return; 97 + } 71 98 72 - if ((input.button == VOLUME_UP || input.button == LEFT) && skipChapter) { 73 - nextPageNumber = 0; 99 + if (prevReleased) { 100 + if (section->currentPage > 0) { 101 + section->currentPage--; 102 + } else { 103 + // We don't want to delete the section mid-render, so grab the semaphore 104 + xSemaphoreTake(renderingMutex, portMAX_DELAY); 105 + nextPageNumber = UINT16_MAX; 74 106 currentSpineIndex--; 75 107 delete section; 76 108 section = nullptr; 77 - } else if ((input.button == VOLUME_DOWN || input.button == RIGHT) && skipChapter) { 109 + xSemaphoreGive(renderingMutex); 110 + } 111 + updateRequired = true; 112 + } else { 113 + if (section->currentPage < section->pageCount - 1) { 114 + section->currentPage++; 115 + } else { 116 + // We don't want to delete the section mid-render, so grab the semaphore 117 + xSemaphoreTake(renderingMutex, portMAX_DELAY); 78 118 nextPageNumber = 0; 79 119 currentSpineIndex++; 80 120 delete section; 81 121 section = nullptr; 82 - } else if (input.button == VOLUME_UP || input.button == LEFT) { 83 - if (section->currentPage > 0) { 84 - section->currentPage--; 85 - } else { 86 - // We don't want to delete the section mid-render, so grab the semaphore 87 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 88 - nextPageNumber = UINT16_MAX; 89 - currentSpineIndex--; 90 - delete section; 91 - section = nullptr; 92 - xSemaphoreGive(renderingMutex); 93 - } 94 - } else if (input.button == VOLUME_DOWN || input.button == RIGHT) { 95 - if (section->currentPage < section->pageCount - 1) { 96 - section->currentPage++; 97 - } else { 98 - // We don't want to delete the section mid-render, so grab the semaphore 99 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 100 - nextPageNumber = 0; 101 - currentSpineIndex++; 102 - delete section; 103 - section = nullptr; 104 - xSemaphoreGive(renderingMutex); 105 - } 122 + xSemaphoreGive(renderingMutex); 106 123 } 107 - 108 124 updateRequired = true; 109 - } else if (input.button == BACK) { 110 - onGoHome(); 111 125 } 112 126 } 113 127
+4 -3
src/screens/EpubReaderScreen.h
··· 24 24 void renderStatusBar() const; 25 25 26 26 public: 27 - explicit EpubReaderScreen(EpdRenderer* renderer, Epub* epub, const std::function<void()>& onGoHome) 28 - : Screen(renderer), epub(epub), onGoHome(onGoHome) {} 27 + explicit EpubReaderScreen(EpdRenderer* renderer, InputManager& inputManager, Epub* epub, 28 + const std::function<void()>& onGoHome) 29 + : Screen(renderer, inputManager), epub(epub), onGoHome(onGoHome) {} 29 30 void onEnter() override; 30 31 void onExit() override; 31 - void handleInput(Input input) override; 32 + void handleInput() override; 32 33 };
+14 -9
src/screens/FileSelectionScreen.cpp
··· 59 59 files.clear(); 60 60 } 61 61 62 - void FileSelectionScreen::handleInput(const Input input) { 63 - if (input.button == VOLUME_DOWN || input.button == RIGHT) { 64 - selectorIndex = (selectorIndex + 1) % files.size(); 65 - updateRequired = true; 66 - } else if (input.button == VOLUME_UP || input.button == LEFT) { 67 - selectorIndex = (selectorIndex + files.size() - 1) % files.size(); 68 - updateRequired = true; 69 - } else if (input.button == CONFIRM) { 62 + void FileSelectionScreen::handleInput() { 63 + const bool prevPressed = 64 + inputManager.wasPressed(InputManager::BTN_UP) || inputManager.wasPressed(InputManager::BTN_LEFT); 65 + const bool nextPressed = 66 + inputManager.wasPressed(InputManager::BTN_DOWN) || inputManager.wasPressed(InputManager::BTN_RIGHT); 67 + 68 + if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) { 70 69 if (files.empty()) { 71 70 return; 72 71 } ··· 79 78 } else { 80 79 onSelect(basepath + files[selectorIndex]); 81 80 } 82 - } else if (input.button == BACK && basepath != "/") { 81 + } else if (inputManager.wasPressed(InputManager::BTN_BACK) && basepath != "/") { 83 82 basepath = basepath.substr(0, basepath.rfind('/')); 84 83 if (basepath.empty()) basepath = "/"; 85 84 loadFiles(); 85 + updateRequired = true; 86 + } else if (prevPressed) { 87 + selectorIndex = (selectorIndex + files.size() - 1) % files.size(); 88 + updateRequired = true; 89 + } else if (nextPressed) { 90 + selectorIndex = (selectorIndex + 1) % files.size(); 86 91 updateRequired = true; 87 92 } 88 93 }
+4 -3
src/screens/FileSelectionScreen.h
··· 24 24 void loadFiles(); 25 25 26 26 public: 27 - explicit FileSelectionScreen(EpdRenderer* renderer, const std::function<void(const std::string&)>& onSelect) 28 - : Screen(renderer), onSelect(onSelect) {} 27 + explicit FileSelectionScreen(EpdRenderer* renderer, InputManager& inputManager, 28 + const std::function<void(const std::string&)>& onSelect) 29 + : Screen(renderer, inputManager), onSelect(onSelect) {} 29 30 void onEnter() override; 30 31 void onExit() override; 31 - void handleInput(Input input) override; 32 + void handleInput() override; 32 33 };
+8 -3
src/screens/FullScreenMessageScreen.h
··· 12 12 bool partialUpdate; 13 13 14 14 public: 15 - explicit FullScreenMessageScreen(EpdRenderer* renderer, std::string text, const EpdFontStyle style = REGULAR, 16 - const bool invert = false, const bool partialUpdate = true) 17 - : Screen(renderer), text(std::move(text)), style(style), invert(invert), partialUpdate(partialUpdate) {} 15 + explicit FullScreenMessageScreen(EpdRenderer* renderer, InputManager& inputManager, std::string text, 16 + const EpdFontStyle style = REGULAR, const bool invert = false, 17 + const bool partialUpdate = true) 18 + : Screen(renderer, inputManager), 19 + text(std::move(text)), 20 + style(style), 21 + invert(invert), 22 + partialUpdate(partialUpdate) {} 18 23 void onEnter() override; 19 24 };
+4 -3
src/screens/Screen.h
··· 1 1 #pragma once 2 - #include "Input.h" 2 + #include <InputManager.h> 3 3 4 4 class EpdRenderer; 5 5 6 6 class Screen { 7 7 protected: 8 8 EpdRenderer* renderer; 9 + InputManager& inputManager; 9 10 10 11 public: 11 - explicit Screen(EpdRenderer* renderer) : renderer(renderer) {} 12 + explicit Screen(EpdRenderer* renderer, InputManager& inputManager) : renderer(renderer), inputManager(inputManager) {} 12 13 virtual ~Screen() = default; 13 14 virtual void onEnter() {} 14 15 virtual void onExit() {} 15 - virtual void handleInput(Input input) {} 16 + virtual void handleInput() {} 16 17 };
+1 -1
src/screens/SleepScreen.h
··· 3 3 4 4 class SleepScreen final : public Screen { 5 5 public: 6 - explicit SleepScreen(EpdRenderer* renderer) : Screen(renderer) {} 6 + explicit SleepScreen(EpdRenderer* renderer, InputManager& inputManager) : Screen(renderer, inputManager) {} 7 7 void onEnter() override; 8 8 };