codebase into one branch consolidation

This commit is contained in:
pelgraine
2026-02-20 06:23:59 +11:00
parent 5cc9feb3e9
commit 2576a6590b
4 changed files with 85 additions and 74 deletions

View File

@@ -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

View File

@@ -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();
}
}
#endif

View File

@@ -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();

View File

@@ -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}
+<helpers/esp32/*.cpp>
+<helpers/ui/MomentaryButton.cpp>
@@ -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}
+<helpers/esp32/*.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
+<helpers/ui/GxEPDDisplay.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}
+<helpers/esp32/*.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
+<helpers/ui/GxEPDDisplay.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>
+<helpers/ui/GxEPDDisplay.cpp>
lib_deps =
${LilyGo_TDeck_Pro.lib_deps}
me-no-dev/AsyncTCP @ ^1.1.1
me-no-dev/ESPAsyncWebServer @ ^1.2.3