From 2576a6590b07fb0ed5cc59b49f5fa1d1cbbc5c99 Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Fri, 20 Feb 2026 06:23:59 +1100 Subject: [PATCH] codebase into one branch consolidation --- examples/companion_radio/main.cpp | 41 ++++++---- examples/companion_radio/ui-new/UITask.cpp | 24 +++++- examples/companion_radio/ui-new/UITask.h | 2 + variants/lilygo_tdeck_pro/platformio.ini | 92 +++++++++------------- 4 files changed, 85 insertions(+), 74 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index bcf6a30..f6c185f 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -48,12 +48,14 @@ // Notes mode state static bool notesMode = false; - // Audiobook player — Audio object is heap-allocated on first use to avoid + // Audiobook player — Audio object is heap-allocated on first use to avoid // consuming ~40KB of DMA/decode buffers at boot (starves BLE stack). + #ifdef MECK_AUDIO_VARIANT #include "AudiobookPlayerScreen.h" #include "Audio.h" Audio* audio = nullptr; static bool audiobookMode = false; + #endif // Power management #if HAS_GPS @@ -328,7 +330,7 @@ void setup() { } MESH_DEBUG_PRINTLN("setup() - radio_init() done"); - // CPU frequency scaling — drop to 80 MHz for idle mesh listening + // CPU frequency scaling — drop to 80 MHz for idle mesh listening cpuPower.begin(); MESH_DEBUG_PRINTLN("setup() - about to call fast_rng.begin()"); @@ -415,7 +417,7 @@ void setup() { MESH_DEBUG_PRINTLN("setup() - SPIFFS.begin() done"); // --------------------------------------------------------------------------- - // Early SD card init — needed BEFORE the_mesh.begin() so we can restore + // Early SD card init — needed BEFORE the_mesh.begin() so we can restore // settings from a previous firmware flash. The display SPI bus is already // up (display.begin() ran earlier), so SD can share it now. // --------------------------------------------------------------------------- @@ -556,7 +558,7 @@ void setup() { } #endif - // GPS duty cycle — honour saved pref, default to enabled on first boot + // GPS duty cycle — honour saved pref, default to enabled on first boot #if HAS_GPS { bool gps_wanted = the_mesh.getNodePrefs()->gps_enabled; @@ -578,7 +580,7 @@ void setup() { MESH_DEBUG_PRINTLN("setup() - BLE disabled at boot (standalone mode)"); #endif - Serial.printf("setup() complete — free heap: %d, largest block: %d\n", + Serial.printf("setup() complete — free heap: %d, largest block: %d\n", ESP.getFreeHeap(), ESP.getMaxAllocHeap()); MESH_DEBUG_PRINTLN("=== setup() - COMPLETE ==="); } @@ -586,7 +588,7 @@ void setup() { void loop() { the_mesh.loop(); - // GPS duty cycle — check for fix and manage power state + // GPS duty cycle — check for fix and manage power state #if HAS_GPS { bool gps_hw_on = gpsDuty.loop(); @@ -605,6 +607,7 @@ void loop() { cpuPower.loop(); // Audiobook: service audio decode regardless of which screen is active +#ifdef MECK_AUDIO_VARIANT { AudiobookPlayerScreen* abPlayer = (AudiobookPlayerScreen*)ui_task.getAudiobookScreen(); @@ -616,6 +619,7 @@ void loop() { } } } +#endif #ifdef DISPLAY_CLASS // Skip UITask rendering when in compose mode to prevent flickering #if defined(LilyGo_TDeck_Pro) @@ -646,7 +650,9 @@ void loop() { // Track reader/notes/audiobook mode state for key routing readerMode = ui_task.isOnTextReader(); notesMode = ui_task.isOnNotesScreen(); + #ifdef MECK_AUDIO_VARIANT audiobookMode = ui_task.isOnAudiobookPlayer(); + #endif #else ui_task.loop(); #endif @@ -837,6 +843,7 @@ void handleKeyboardInput() { } // *** AUDIOBOOK MODE *** +#ifdef MECK_AUDIO_VARIANT if (audiobookMode) { AudiobookPlayerScreen* abPlayer = (AudiobookPlayerScreen*)ui_task.getAudiobookScreen(); @@ -848,11 +855,11 @@ void handleKeyboardInput() { if (key == 'q') { if (abPlayer->isBookOpen()) { if (abPlayer->isAudioActive()) { - // Audio is playing — leave screen, audio continues via audioTick() + // 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 + // Paused or stopped — close book, show file list abPlayer->closeCurrentBook(); Serial.println("Closed audiobook (was paused/stopped)"); // Stay on audiobook screen showing file list @@ -869,6 +876,7 @@ void handleKeyboardInput() { ui_task.injectKey(key); return; } +#endif // MECK_AUDIO_VARIANT // *** TEXT READER MODE *** if (readerMode) { @@ -1045,7 +1053,7 @@ void handleKeyboardInput() { return; } - // All other keys → settings screen via injectKey + // All other keys → settings screen via injectKey ui_task.injectKey(key); return; } @@ -1121,18 +1129,23 @@ void handleKeyboardInput() { break; case 'p': - // Open audiobook player — lazy-init Audio + screen on first use +#ifdef MECK_AUDIO_VARIANT + // Open audiobook player -- lazy-init Audio + screen on first use Serial.println("Opening audiobook player"); if (!ui_task.getAudiobookScreen()) { - Serial.printf("Audiobook: lazy init — free heap: %d, largest block: %d\n", + Serial.printf("Audiobook: lazy init -- free heap: %d, largest block: %d\n", ESP.getFreeHeap(), ESP.getMaxAllocHeap()); audio = new Audio(); AudiobookPlayerScreen* abScreen = new AudiobookPlayerScreen(&ui_task, audio); abScreen->setSDReady(sdCardReady); ui_task.setAudiobookScreen(abScreen); - Serial.printf("Audiobook: init complete — free heap: %d\n", ESP.getFreeHeap()); + Serial.printf("Audiobook: init complete -- free heap: %d\n", ESP.getFreeHeap()); } ui_task.gotoAudiobookPlayer(); +#else + Serial.println("Audio not available on this build variant"); + ui_task.showAlert("No audio hardware", 1500); +#endif break; case 'n': @@ -1467,8 +1480,7 @@ void sendComposedMessage() { // ============================================================================ // ESP32-audioI2S CALLBACKS // ============================================================================ -// The audio library calls these global functions — must be defined at file scope. - +#ifdef MECK_AUDIO_VARIANT void audio_info(const char *info) { Serial.printf("Audio: %s\n", info); } @@ -1482,5 +1494,6 @@ void audio_eof_mp3(const char *info) { abPlayer->onEOF(); } } +#endif // MECK_AUDIO_VARIANT #endif // LilyGo_TDeck_Pro \ No newline at end of file diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index aed39c7..ec30f16 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -37,7 +37,9 @@ #include "ContactsScreen.h" #include "TextReaderScreen.h" #include "SettingsScreen.h" +#ifdef MECK_AUDIO_VARIANT #include "AudiobookPlayerScreen.h" +#endif class SplashScreen : public UIScreen { UITask* _task; @@ -167,6 +169,7 @@ void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts, display.setTextSize(1); // restore default text size } +#ifdef MECK_AUDIO_VARIANT // ---- Audio background playback indicator ---- // Shows a small play symbol to the left of the battery icon when an // audiobook is actively playing in the background. @@ -182,6 +185,7 @@ void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts, display.print(">>"); display.setTextSize(1); // restore } +#endif CayenneLPP sensors_lpp; int sensors_nb = 0; @@ -240,11 +244,15 @@ public: display.print(filtered_name); // battery voltage +#ifdef MECK_AUDIO_VARIANT 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); +#else + renderBatteryIndicator(display, _task->getBattMilliVolts()); +#endif // centered clock (tinyfont) - only show when time is valid { @@ -322,7 +330,11 @@ public: y += 10; display.drawTextCentered(display.width() / 2, y, "[N] Notes [S] Settings "); y += 10; +#ifdef MECK_AUDIO_VARIANT display.drawTextCentered(display.width() / 2, y, "[E] Reader [P] Audiobooks"); +#else + display.drawTextCentered(display.width() / 2, y, "[E] Reader "); +#endif y += 14; // Nav hint @@ -431,7 +443,7 @@ public: display.drawTextRightAlign(display.width()-1, y, buf); y = y + 12; - // NMEA sentence counter — confirms baud rate and data flow + // NMEA sentence counter — confirms baud rate and data flow display.drawTextLeftAlign(0, y, "sentences"); if (gpsDuty.isHardwareOn()) { uint16_t sps = gpsStream.getSentencesPerSec(); @@ -1221,13 +1233,13 @@ void UITask::toggleGPS() { if (_sensors != NULL) { if (_node_prefs->gps_enabled) { - // Disable GPS — cut hardware power + // Disable GPS — cut hardware power _sensors->setSettingValue("gps", "0"); _node_prefs->gps_enabled = 0; gpsDuty.disable(); notify(UIEventType::ack); } else { - // Enable GPS — start duty cycle + // Enable GPS — start duty cycle _sensors->setSettingValue("gps", "1"); _node_prefs->gps_enabled = 1; gpsDuty.enable(); @@ -1359,6 +1371,7 @@ void UITask::gotoOnboarding() { } void UITask::gotoAudiobookPlayer() { +#ifdef MECK_AUDIO_VARIANT if (audiobook_screen == nullptr) return; // No audio hardware AudiobookPlayerScreen* abPlayer = (AudiobookPlayerScreen*)audiobook_screen; if (_display != NULL) { @@ -1370,6 +1383,7 @@ void UITask::gotoAudiobookPlayer() { } _auto_off = millis() + AUTO_OFF_MILLIS; _next_refresh = 100; +#endif } uint8_t UITask::getChannelScreenViewIdx() const { @@ -1424,6 +1438,7 @@ void UITask::onAdminCliResponse(const char* from_name, const char* text) { } } +#ifdef MECK_AUDIO_VARIANT bool UITask::isAudioPlayingInBackground() const { if (!audiobook_screen) return false; AudiobookPlayerScreen* player = (AudiobookPlayerScreen*)audiobook_screen; @@ -1434,4 +1449,5 @@ 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 +} +#endif \ 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 1471e8d..e20dbff 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -103,9 +103,11 @@ public: bool isOnAudiobookPlayer() const { return curr == audiobook_screen; } bool isOnRepeaterAdmin() const { return curr == repeater_admin; } +#ifdef MECK_AUDIO_VARIANT // Check if audio is playing/paused in the background (for status indicators) bool isAudioPlayingInBackground() const; bool isAudioPausedInBackground() const; +#endif uint8_t getChannelScreenViewIdx() const; void toggleBuzzer(); diff --git a/variants/lilygo_tdeck_pro/platformio.ini b/variants/lilygo_tdeck_pro/platformio.ini index 4ab9a15..89ad476 100644 --- a/variants/lilygo_tdeck_pro/platformio.ini +++ b/variants/lilygo_tdeck_pro/platformio.ini @@ -92,14 +92,21 @@ lib_deps = adafruit/Adafruit GFX Library@^1.11.0 bakercp/CRC32@^2.0.0 -[env:LilyGo_TDeck_Pro_companion_radio_usb] +; --------------------------------------------------------------------------- +; Meck unified builds — one codebase, three variants via build flags +; --------------------------------------------------------------------------- + +; Audio + BLE companion (audio-player hardware with BLE phone bridging) +[env:meck_audio_ble] extends = LilyGo_TDeck_Pro build_flags = ${LilyGo_TDeck_Pro.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=350 - -D MAX_GROUP_CHANNELS=40 + -D MAX_CONTACTS=400 + -D MAX_GROUP_CHANNELS=20 + -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 + -D MECK_AUDIO_VARIANT build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter} + + @@ -109,8 +116,33 @@ build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter} lib_deps = ${LilyGo_TDeck_Pro.lib_deps} densaugeo/base64 @ ~1.4.0 + https://github.com/schreibfaul1/ESP32-audioI2S.git#2.0.6 + bitbank2/JPEGDEC -[env:LilyGo_TDeck_Pro_companion_radio_ble] +; Audio standalone (audio-player hardware, no BLE/WiFi — maximum battery life) +[env:meck_audio_standalone] +extends = LilyGo_TDeck_Pro +build_flags = + ${LilyGo_TDeck_Pro.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 + -D MECK_AUDIO_VARIANT +build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> + + +lib_deps = + ${LilyGo_TDeck_Pro.lib_deps} + densaugeo/base64 @ ~1.4.0 + https://github.com/schreibfaul1/ESP32-audioI2S.git#2.0.6 + bitbank2/JPEGDEC + +; 4G + BLE companion (4G modem hardware, no audio — GPIO conflict with PCM5102A) +[env:meck_4g_ble] extends = LilyGo_TDeck_Pro build_flags = ${LilyGo_TDeck_Pro.build_flags} @@ -128,55 +160,3 @@ build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter} lib_deps = ${LilyGo_TDeck_Pro.lib_deps} densaugeo/base64 @ ~1.4.0 - https://github.com/schreibfaul1/ESP32-audioI2S.git#2.0.6 - bitbank2/JPEGDEC - -[env:LilyGo_TDeck_Pro_standalone] -extends = LilyGo_TDeck_Pro -build_flags = - ${LilyGo_TDeck_Pro.build_flags} - -I examples/companion_radio/ui-new - -D MAX_CONTACTS=700 - -D MAX_GROUP_CHANNELS=20 - -D OFFLINE_QUEUE_SIZE=256 - -D NO_OTA=1 - -D FIRMWARE_VERSION='"Meck v0.9.1A-NB"' -; === NO BLE_PIN_CODE, NO WIFI_SSID === -; By omitting these, the preprocessor selects ArduinoSerialInterface (USB only). -; The BLE stack is never initialized, never linked, and the ESP32-S3 BT/WiFi -; radio hardware stays in its boot-default OFF state — zero RF power draw. -; This is the ONLY reliable way to fully disable BLE on the ESP32-S3. -; Calling esp_bt_controller_disable() after init does NOT fully power off the radio. -build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter} - + - + - +<../examples/companion_radio/*.cpp> - +<../examples/companion_radio/ui-new/*.cpp> - + -lib_deps = - ${LilyGo_TDeck_Pro.lib_deps} - densaugeo/base64 @ ~1.4.0 - https://github.com/schreibfaul1/ESP32-audioI2S.git#2.0.6 - bitbank2/JPEGDEC -; Audio libs included because AudiobookPlayerScreen.h unconditionally #includes -; Audio.h when LilyGo_TDeck_Pro is defined. No power cost — the Audio object is -; only heap-allocated when user presses 'P'. Without BLE competing for heap, -; audiobook playback actually works better in standalone mode. - -[env:LilyGo_TDeck_Pro_repeater] -extends = LilyGo_TDeck_Pro -build_flags = - ${LilyGo_TDeck_Pro.build_flags} - -D ADVERT_NAME='"TDeck Pro Repeater"' - -D ADVERT_LAT=0.0 - -D ADVERT_LON=0.0 - -D ADMIN_PASSWORD='"password"' - -D MAX_NEIGHBOURS=50 - -D NO_OTA=1 -build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter} - +<../examples/simple_repeater> - + -lib_deps = - ${LilyGo_TDeck_Pro.lib_deps} - me-no-dev/AsyncTCP @ ^1.1.1 - me-no-dev/ESPAsyncWebServer @ ^1.2.3 \ No newline at end of file