mirror of
https://github.com/pelgraine/Meck.git
synced 2026-05-05 04:52:36 +02:00
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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -450,7 +450,6 @@ public:
|
||||
}
|
||||
|
||||
if (frameSize == 0 || pos + 10 + frameSize > tagEnd) break;
|
||||
}
|
||||
|
||||
uint32_t dataStart = pos + 10;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user