From 3ab8191d1946bf424865d4e86c2332dcb946463b Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Sat, 14 Feb 2026 00:17:44 +1100 Subject: [PATCH] amened ble connection parameter (battery-saving) fix that was causing performance issues; updated firrmware date in mymesh; ui update for audiobook player; audiobook player now lkeeps playing on exit unless you pause it so background audio is enabled --- examples/companion_radio/MyMesh.h | 2 +- examples/companion_radio/main.cpp | 17 +++++- .../ui-new/Audiobookplayerscreen.h | 22 ++++--- examples/companion_radio/ui-new/M4BMetadata.h | 1 - examples/companion_radio/ui-new/UITask.cpp | 61 ++++++++++++++++++- examples/companion_radio/ui-new/UITask.h | 4 ++ src/helpers/esp32/SerialBLEInterface.cpp | 12 ---- 7 files changed, 93 insertions(+), 26 deletions(-) diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 4a6ac59c..8275a5cd 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -8,7 +8,7 @@ #define FIRMWARE_VER_CODE 8 #ifndef FIRMWARE_BUILD_DATE -#define FIRMWARE_BUILD_DATE "13 Feb 2026" +#define FIRMWARE_BUILD_DATE "14 Feb 2026" #endif #ifndef FIRMWARE_VERSION diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index a4c5d94e..fb6feb25 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -840,11 +840,22 @@ void handleKeyboardInput() { AudiobookPlayerScreen* abPlayer = (AudiobookPlayerScreen*)ui_task.getAudiobookScreen(); - // Q key: if book is open, player handles it (stop & go to file list) - // if on file list, exit player entirely + // Q key: behavior depends on playback state + // - Playing: navigate home, audio continues in background + // - Paused/stopped: close book, return to file list + // - File list: exit player entirely if (key == 'q') { if (abPlayer->isBookOpen()) { - ui_task.injectKey('q'); + if (abPlayer->isAudioActive()) { + // Audio is playing — leave screen, audio continues via audioTick() + Serial.println("Leaving audiobook player (audio continues in background)"); + ui_task.gotoHomeScreen(); + } else { + // Paused or stopped — close book, show file list + abPlayer->closeCurrentBook(); + Serial.println("Closed audiobook (was paused/stopped)"); + // Stay on audiobook screen showing file list + } } else { abPlayer->exitPlayer(); Serial.println("Exiting audiobook player"); diff --git a/examples/companion_radio/ui-new/Audiobookplayerscreen.h b/examples/companion_radio/ui-new/Audiobookplayerscreen.h index fd05ddfe..1ede1d5e 100644 --- a/examples/companion_radio/ui-new/Audiobookplayerscreen.h +++ b/examples/companion_radio/ui-new/Audiobookplayerscreen.h @@ -17,7 +17,8 @@ // FILE_LIST mode: W/S = scroll, Enter = open, Q = exit // PLAYER mode: Enter = play/pause, A = -30s, D = +30s, // W = volume up, S = volume down, -// [ = prev chapter, ] = next chapter, Q = stop & exit +// [ = prev chapter, ] = next chapter, +// Q = leave (audio continues) / close book (if paused) // // Library dependencies (add to platformio.ini lib_deps): // https://github.com/schreibfaul1/ESP32-audioI2S.git#2.0.6 @@ -820,7 +821,7 @@ private: // Transport controls drawn — footer is at fixed position below // ---- Footer Nav Bar ---- - drawFooter(display, "A/D:Seek W/S:Vol", "Q:Back"); + drawFooter(display, "A/D:Seek W/S:Vol", (_isPlaying && !_isPaused) ? "Q:Leave" : "Q:Close"); } public: @@ -889,6 +890,16 @@ public: } bool isAudioActive() const { return _isPlaying && !_isPaused; } + bool isPaused() const { return _isPaused; } + bool isBookOpenAndPaused() const { return _bookOpen && (_isPaused || !_isPlaying); } + + // Public method to close the current book (stops playback, saves bookmark, + // returns to file list). Used by main.cpp when user presses Q while paused. + void closeCurrentBook() { + if (_bookOpen) { + closeBook(); + } + } void enter(DisplayDriver& display) { if (!_bookOpen) { @@ -1036,11 +1047,8 @@ public: return false; } - // Q - stop and exit to file list - if (c == 'q') { - closeBook(); - return true; - } + // Q - handled by main.cpp (leave screen or close book depending on play state) + // Not handled here - main.cpp intercepts Q before it reaches the player return false; } diff --git a/examples/companion_radio/ui-new/M4BMetadata.h b/examples/companion_radio/ui-new/M4BMetadata.h index e41c0c7d..a29426a1 100644 --- a/examples/companion_radio/ui-new/M4BMetadata.h +++ b/examples/companion_radio/ui-new/M4BMetadata.h @@ -450,7 +450,6 @@ public: } if (frameSize == 0 || pos + 10 + frameSize > tagEnd) break; - } uint32_t dataStart = pos + 10; diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 42c55496..354c0697 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -111,7 +111,7 @@ class HomeScreen : public UIScreen { AdvertPath recent[UI_RECENT_LIST_SIZE]; -void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) { +void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts, int* outIconX = nullptr) { // Use the BQ27220 fuel gauge SOC register for accurate percentage. // Falls back to voltage estimation if the fuel gauge returns 0. uint8_t batteryPercentage = board.getBatteryPercent(); @@ -141,6 +141,8 @@ void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) int iconX = display.width() - totalWidth; int iconY = 0; // vertically align with node name text + if (outIconX) *outIconX = iconX; + // battery outline display.drawRect(iconX, iconY, iconWidth, iconHeight); @@ -160,6 +162,45 @@ void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) display.setTextSize(1); // restore default text size } + // ---- Audio background playback indicator ---- + // Draws a small music note (♪) to the left of the battery icon when an + // audiobook is playing or paused in the background. + void renderAudioIndicator(DisplayDriver& display, int batteryLeftX) { + bool playing = _task->isAudioPlayingInBackground(); + bool paused = _task->isAudioPausedInBackground(); + if (!playing && !paused) return; + + display.setColor(DisplayDriver::GREEN); + + // Position: just to the left of the battery icon with a 3px gap + // Note is 5px wide x 8px tall + int x = batteryLeftX - 8; + int y = -1; + + if (playing) { + // Draw a ♪ eighth note: + // Stem: vertical line on the right side + display.fillRect(x + 4, y + 0, 1, 7); // stem + // Flag: two pixels angling off the top of the stem + display.fillRect(x + 5, y + 1, 1, 1); + display.fillRect(x + 5, y + 2, 1, 1); + // Note head: filled oval at bottom of stem + display.fillRect(x + 1, y + 5, 3, 2); // head body + display.fillRect(x + 0, y + 6, 1, 1); // head left + } else { + // Paused: draw the same note but with pause bars overlaid + // Note shape (same as above) + display.fillRect(x + 4, y + 0, 1, 7); + display.fillRect(x + 5, y + 1, 1, 1); + display.fillRect(x + 5, y + 2, 1, 1); + display.fillRect(x + 1, y + 5, 3, 2); + display.fillRect(x + 0, y + 6, 1, 1); + // Small pause indicator: two dots to the left of the note + display.fillRect(x - 2, y + 2, 1, 3); + display.fillRect(x - 4, y + 2, 1, 3); + } + } + CayenneLPP sensors_lpp; int sensors_nb = 0; bool sensors_scroll = false; @@ -217,7 +258,11 @@ public: display.print(filtered_name); // battery voltage - renderBatteryIndicator(display, _task->getBattMilliVolts()); + int battLeftX = display.width(); // default if battery doesn't render + renderBatteryIndicator(display, _task->getBattMilliVolts(), &battLeftX); + + // audio background playback indicator (♪ icon next to battery) + renderAudioIndicator(display, battLeftX); // centered clock (tinyfont) - only show when time is valid { @@ -1247,4 +1292,16 @@ void UITask::addSentChannelMessage(uint8_t channel_idx, const char* sender, cons // Add to channel history with path_len=0 (local message) ((ChannelScreen *) channel_screen)->addMessage(channel_idx, 0, sender, formattedMsg); +} + +bool UITask::isAudioPlayingInBackground() const { + if (!audiobook_screen) return false; + AudiobookPlayerScreen* player = (AudiobookPlayerScreen*)audiobook_screen; + return player->isAudioActive(); +} + +bool UITask::isAudioPausedInBackground() const { + if (!audiobook_screen) return false; + AudiobookPlayerScreen* player = (AudiobookPlayerScreen*)audiobook_screen; + return player->isBookOpen() && !player->isAudioActive(); } \ No newline at end of file diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index d2da8c49..8ff8b703 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -97,6 +97,10 @@ public: bool isOnNotesScreen() const { return curr == notes_screen; } bool isOnSettingsScreen() const { return curr == settings_screen; } bool isOnAudiobookPlayer() const { return curr == audiobook_screen; } + + // Check if audio is playing/paused in the background (for status indicators) + bool isAudioPlayingInBackground() const; + bool isAudioPausedInBackground() const; uint8_t getChannelScreenViewIdx() const; void toggleBuzzer(); diff --git a/src/helpers/esp32/SerialBLEInterface.cpp b/src/helpers/esp32/SerialBLEInterface.cpp index 0adcc36e..c5837d0f 100644 --- a/src/helpers/esp32/SerialBLEInterface.cpp +++ b/src/helpers/esp32/SerialBLEInterface.cpp @@ -133,18 +133,6 @@ void SerialBLEInterface::onConnect(BLEServer* pServer) { void SerialBLEInterface::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) { BLE_DEBUG_PRINTLN("onConnect(), conn_id=%d, mtu=%d", param->connect.conn_id, pServer->getPeerMTU(param->connect.conn_id)); last_conn_id = param->connect.conn_id; - - // Request relaxed connection parameters to reduce BLE radio wake-ups. - // Slave latency of 4 lets the ESP32 skip up to 4 intervals when idle, - // effectively sleeping ~800ms between radio events. - esp_ble_conn_update_params_t conn_params; - memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t)); - conn_params.min_int = 0x30; // 48 * 1.25ms = 60ms - conn_params.max_int = 0xA0; // 160 * 1.25ms = 200ms - conn_params.latency = 4; // skip up to 4 intervals when idle - conn_params.timeout = 400; // 400 * 10ms = 4s supervision timeout - esp_ble_gap_update_conn_params(&conn_params); - BLE_DEBUG_PRINTLN("Requested conn params: interval=60-200ms, latency=4, timeout=4s"); } void SerialBLEInterface::onMtuChanged(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) {