Merge branch 'main' into dev

This commit is contained in:
pelgraine
2026-05-05 11:57:18 +10:00
5 changed files with 195 additions and 57 deletions
+20 -4
View File
@@ -39,6 +39,7 @@ A fork created specifically to focus on enabling BLE & WiFi companion firmware f
- [Alarm Clock (Audio only)](#alarm-clock-audio-only)
- [Voice Notes Over LoRa (Audio only)](#voice-notes-over-lora-audio-only)
- [Lock Screen (T-Deck Pro)](#lock-screen-t-deck-pro)
- [Shutdown (T-Deck Pro)](#shutdown-t-deck-pro)
- [Remote Repeater (T-Deck Pro 4G)](#remote-repeater-t-deck-pro-4g)
- [WiFi Repeater](#wifi-repeater)
- [T5S3 E-Paper Pro](#t5s3-e-paper-pro)
@@ -140,6 +141,8 @@ esptool.py --chip esp32s3 --port /dev/ttyACM0 --baud 921600 \
> **Tip:** If you're unsure whether the device already has a bootloader, it's always safe to use the merged file and flash at `0x0` — it will overwrite everything cleanly.
> **First boot:** On a fresh flash, the device will format its internal storage partition. The display shows "Formatting storage... First boot - please wait" — this takes 1-2 minutes and only happens once. If the device was previously running different firmware (e.g. stock LilyGo or Meshtastic), the partition is automatically erased and reformatted to ensure a clean start.
### Launcher
If you're loading firmware from an SD card via the LilyGo Launcher firmware, use the **non-merged** `.bin` file. The Launcher provides its own bootloader and only needs the application image.
@@ -343,6 +346,7 @@ Flood-based hop estimates (`~D`, `~N`) are drawn from a cache of up to 1,000 rec
| Key | Action |
|-----|--------|
| W / S | Scroll up / down through contacts |
| Shift+W / Shift+S | Page up / page down |
| A / D | Cycle filter: All → Chat → Rptr → Room → Sens → Fav |
| Enter | Enter select mode (highlights current contact, enables batch operations) |
| P | Open path editor for the highlighted contact |
@@ -433,6 +437,7 @@ Press **S** from the home screen to open settings. On first boot (when the devic
| Key | Action |
|-----|--------|
| W / S | Navigate up / down through settings |
| Shift+W / Shift+S | Page up / page down |
| Enter | Edit selected setting, or enter a sub-screen |
| Q | Back one level (sub-screen → top level → home screen) |
@@ -491,6 +496,8 @@ Change the font in **Settings → Font** — use A/D to cycle with a live previe
Font styles are available in both Tiny and Larger text size modes. Custom fonts at Tiny size use 7pt glyphs; at Larger size, 9pt — matching the existing FreeSans layout. The font preference is saved and persists across reboots.
**Accented character support (v1.8+):** All three font styles now display accented and diacritical characters (Czech, Polish, French, German, etc.) instead of dropping them. **Noto Sans at Larger text size** renders diacritical marks natively (carons, accents, cedillas). All other font and size combinations fold accented characters to their ASCII base letter (ě→e, ž→z, ñ→n) — the letter is always visible, just without the diacritic mark.
### Compose Mode
| Key | Action |
@@ -527,7 +534,7 @@ Press the **Sym** key then the letter key to enter numbers and symbols:
### Emoji Picker
While in compose mode, press the **$** key to open the emoji picker. A scrollable grid of 76 emoji is displayed in a 5-column layout, with faces and emotions grouped first. Scrolling wraps around — pressing W on the first row goes to the last row and vice versa.
While in compose mode, press the **$** key to open the emoji picker. A scrollable grid of 77 emoji is displayed in a 5-column layout, with faces and emotions grouped first. Scrolling wraps around — pressing W on the first row goes to the last row and vice versa.
| Key | Action |
|-----|--------|
@@ -641,6 +648,10 @@ Double-click the Boot button again to unlock and return to whatever screen you w
An auto-lock timer can be configured in **Settings → Auto Lock** (None / 2 / 5 / 10 / 15 / 30 minutes of idle time).
### Shutdown (T-Deck Pro)
The home screen includes a **Shutdown** page. Selecting it powers the device off completely — the ESP32-S3 enters deep sleep with no wake sources, peripheral power is cut, and the LoRa module is powered down. Only a hardware reset (reset button) or USB power-on will wake the device. This is distinct from the auto-lock hibernate, which maintains wake-on-LoRa capability.
---
## Remote Repeater (T-Deck Pro 4G)
@@ -762,7 +773,7 @@ The virtual keyboard supports:
- QWERTY letter layout with a symbol/number layer (tap the **123** key to switch)
- Shift toggle for uppercase
- Backspace (UTF-8 aware — correctly deletes multi-byte emoji) and Enter keys
- **Emoji picker** — tap the **$** key to open a scrollable 8-column grid of 76 emoji sprites with page indicators. Tap an emoji to insert it inline in your message. Tap **Back** to return to the keyboard. Faces and emotions are grouped first for quick access.
- **Emoji picker** — tap the **$** key to open a scrollable 8-column grid of 77 emoji sprites with page indicators. Tap an emoji to insert it inline in your message. Tap **Back** to return to the keyboard. Faces and emotions are grouped first for quick access.
- Inline emoji rendering — emoji appear as pixel sprites in the text field as you type
- Phantom keystroke prevention (a brief cooldown after the keyboard opens prevents accidental taps)
@@ -1021,7 +1032,7 @@ The companion firmware can be connected to via BLE (T-Deck Pro and T5S3 BLE vari
## 🛠 Hardware Compatibility
MeshCore is designed for devices listed in the [MeshCore Flasher](https://flasher.meshcore.co.uk). Meck specifically targets the LilyGo T-Deck Pro, LilyGo T5S3 E-Paper Pro, Heltec V3 (remote repeater only), and Heltec V4 (remote repeater only).
MeshCore is designed for devices listed in the [MeshCore Flasher](https://flasher.meshcore.io). Meck specifically targets the LilyGo T-Deck Pro, LilyGo T5S3 E-Paper Pro, Heltec V3 (remote repeater only), and Heltec V4 (remote repeater only).
## Contributing
@@ -1068,8 +1079,12 @@ There are a number of fairly major features in the pipeline, with no particular
- [X] Channel picker screen with unread badges
- [X] Region scope (MeshCore v1.15+ compatibility)
- [X] Selectable font styles (Classic, Noto Sans, Montserrat)
- [X] Expanded emoji picker (76 emoji, reordered, wrap scrolling)
- [X] Expanded emoji picker (77 emoji, reordered, wrap scrolling)
- [X] 1,000-entry advert path cache (PSRAM)
- [X] Accented character / diacritics support (Czech, Polish, French, German, Latin Extended)
- [X] Page scroll (Shift+W/S) on all list screens
- [X] True power off (deep sleep, no wake sources)
- [X] BLE 2M PHY, DLE, and faster write interval
- [ ] Fix M4B rendering to enable chaptered audiobook playback
- [ ] Better JPEG and PNG decoding
- [ ] Improve EPUB rendering and EPUB format handling
@@ -1103,6 +1118,7 @@ There are a number of fairly major features in the pipeline, with no particular
- [X] Region scope (MeshCore v1.15+ compatibility)
- [X] Selectable font styles (Classic, Noto Sans, Montserrat)
- [X] Virtual keyboard emoji grid with scrollable pages
- [X] Accented character / diacritics support (Czech, Polish, French, German, Latin Extended)
- [ ] Improve EPUB rendering and EPUB format handling
**Heltec V4:**
+38 -4
View File
@@ -5,6 +5,9 @@
#if defined(ESP32) && defined(MECK_OTA_UPDATE)
#include <esp_ota_ops.h>
#endif
#ifdef ESP32
#include <esp_partition.h>
#endif
#include <Mesh.h>
#include "MyMesh.h"
#include "variant.h" // Board-specific defines (HAS_GPS, etc.)
@@ -1492,7 +1495,11 @@ static void lastHeardToggleContact() {
if (horizontal) {
return (dx < 0) ? (char)KEY_NEXT : (char)KEY_PREV;
}
return (char)KEY_NEXT; // vertical swipe = next (default)
// Shutdown page: vertical swipe toggles hibernate/power off
if (ui_task.isHomeOnShutdownPage()) {
return 'w'; // toggle (direction doesn't matter)
}
return (char)KEY_NEXT; // vertical swipe = next page (default)
}
// Settings: horizontal swipe → a/d for picker/number editing
@@ -1870,7 +1877,7 @@ void setup() {
#elif defined(ESP32)
MESH_DEBUG_PRINTLN("setup() - ESP32 filesystem init - calling SPIFFS.begin()");
if (!SPIFFS.begin(false)) {
// First boot or corrupted partition format required (can take 1-2 minutes)
// First boot or corrupted partition -- format required (can take 1-2 minutes)
Serial.println("SPIFFS mount failed - formatting (this may take 1-2 minutes)...");
if (disp) {
disp->startFrame();
@@ -1882,7 +1889,27 @@ void setup() {
disp->endFrame();
}
if (!SPIFFS.begin(true)) {
Serial.println("SPIFFS format FAILED!");
// Auto-format failed -- partition likely contains non-SPIFFS data from
// a previous firmware. Erase the entire partition and retry.
Serial.println("SPIFFS auto-format failed -- erasing partition and retrying...");
const esp_partition_t* spiffsPart = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
if (spiffsPart) {
Serial.printf("SPIFFS partition: offset=0x%x size=0x%x -- erasing...\n",
spiffsPart->address, spiffsPart->size);
esp_partition_erase_range(spiffsPart, 0, spiffsPart->size);
}
bool spiffsMounted = false;
for (int attempt = 0; attempt < 3 && !spiffsMounted; attempt++) {
if (attempt > 0) { Serial.printf("SPIFFS retry %d/3...\n", attempt + 1); delay(500); }
SPIFFS.format();
spiffsMounted = SPIFFS.begin(false);
}
if (spiffsMounted) {
Serial.println("SPIFFS mounted after explicit format");
} else {
Serial.println("ERROR: SPIFFS mount failed after all retries");
}
} else {
Serial.println("SPIFFS format complete");
}
@@ -3269,7 +3296,13 @@ void loop() {
case 'e': ui_task.gotoTextReader(); break;
case 'n': ui_task.gotoNotesScreen(); break;
#endif
case 's': ui_task.gotoSettingsScreen(); break;
case 's':
if (ui_task.isHomeOnShutdownPage()) {
ui_task.injectKey(ckb);
} else {
ui_task.gotoSettingsScreen();
}
break;
case 'f': ui_task.gotoDiscoveryScreen(); break;
case 'h': ui_task.gotoLastHeardScreen(); break;
case (char)0xF3: ui_task.injectKey(KEY_LEFT); break; // Left arrow → prev page
@@ -4599,6 +4632,7 @@ void handleKeyboardInput() {
#ifdef MECK_AUDIO_VARIANT
|| ui_task.isOnAlarmScreen()
#endif
|| ui_task.isHomeOnShutdownPage()
) {
ui_task.injectKey('s'); // Pass directly for scrolling
} else {
+129 -45
View File
@@ -46,13 +46,7 @@
#endif
#endif
#if UI_HAS_JOYSTICK
#define PRESS_LABEL "press Enter"
#elif defined(LilyGo_T5S3_EPaper_Pro)
#define PRESS_LABEL "long press"
#else
#define PRESS_LABEL "long press"
#endif
#define PRESS_LABEL "long press"
#include "icons.h"
#include "ChannelScreen.h"
@@ -147,6 +141,9 @@ class HomeScreen : public UIScreen {
uint8_t _page;
bool _shutdown_init;
unsigned long _shutdown_at; // earliest time to proceed with shutdown (after e-ink refresh)
bool _poweroff_selected; // true = "power off" highlighted, false = "hibernate"
bool _poweroff_confirm; // true = showing confirmation prompt for power off
bool _poweroff_msg_shown; // true = "powering off..." already displayed once
bool _editing_utc;
int8_t _saved_utc_offset; // for cancel/undo
@@ -310,10 +307,12 @@ void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts,
public:
HomeScreen(UITask* task, mesh::RTCClock* rtc, SensorManager* sensors, NodePrefs* node_prefs)
: _task(task), _rtc(rtc), _sensors(sensors), _node_prefs(node_prefs), _page(0),
_shutdown_init(false), _shutdown_at(0), _editing_utc(false), _saved_utc_offset(0), sensors_lpp(200) { }
_shutdown_init(false), _shutdown_at(0), _poweroff_selected(false), _poweroff_confirm(false),
_poweroff_msg_shown(false), _editing_utc(false), _saved_utc_offset(0), sensors_lpp(200) { }
bool isEditingUTC() const { return _editing_utc; }
bool isOnRecentPage() const { return _page == HomePage::RECENT; }
bool isOnShutdownPage() const { return _page == HomePage::SHUTDOWN; }
void cancelEditing() {
if (_editing_utc) {
_node_prefs->utc_offset_hours = _saved_utc_offset;
@@ -323,6 +322,7 @@ public:
void poll() override {
if (_shutdown_init && millis() >= _shutdown_at && !_task->isButtonPressed()) {
if (_poweroff_selected) _task->setFullPowerOff(true);
_task->shutdown();
}
}
@@ -332,6 +332,27 @@ public:
#if defined(LilyGo_T5S3_EPaper_Pro)
_task->setHomeShowingTiles(false); // Reset — only set true on FIRST page
#endif
// Power off: full-screen message, no header
// First render: "powering off..." + wake instruction
// Second render onward: wake instruction only (persists on e-ink)
if (_shutdown_init && _poweroff_selected) {
#if defined(LilyGo_T5S3_EPaper_Pro)
board.setBacklight(false);
#endif
display.setColor(DisplayDriver::GREEN);
display.setTextSize(1);
if (!_poweroff_msg_shown) {
_poweroff_msg_shown = true;
display.drawTextCentered(display.width() / 2, 30, "powering off...");
display.drawTextCentered(display.width() / 2, 46, "plug in USB-C to turn on");
return 1500;
} else {
display.drawTextCentered(display.width() / 2, 38, "plug in USB-C to turn on");
return 5000;
}
}
// node name (tinyfont to avoid overlapping clock)
display.setTextSize(_node_prefs->smallTextSize());
display.setColor(DisplayDriver::GREEN);
@@ -1000,21 +1021,43 @@ public:
display.setTextSize(1);
if (_shutdown_init) {
#if defined(LilyGo_T5S3_EPaper_Pro)
board.setBacklight(false); // Turn off backlight on hibernate
board.setBacklight(false);
#endif
display.drawTextCentered(display.width() / 2, 34, "hibernating...");
} else {
} else if (_poweroff_confirm) {
// Confirmation prompt for power off
#if defined(LilyGo_T5S3_EPaper_Pro)
display.drawXbm((display.width() - 32) / 2, 28, power_icon, 32, 32);
#else
display.drawXbm((display.width() - 32) / 2, 18, power_icon, 32, 32);
display.drawXbm((display.width() - 32) / 2, 10, power_icon, 32, 32);
#endif
#if defined(LilyGo_T5S3_EPaper_Pro)
display.drawTextCentered(display.width() / 2, 64, "hibernate:" PRESS_LABEL);
display.drawTextCentered(display.width() / 2, 64, "power off device?");
display.drawTextCentered(display.width() / 2, 76, "usb-c to wake");
#else
display.drawTextCentered(display.width() / 2, 57, "hibernate: " PRESS_LABEL);
display.drawTextCentered(display.width() / 2, 67, "or press Enter key");
display.drawTextCentered(display.width() / 2, 50, "power off device?");
display.drawTextCentered(display.width() / 2, 60, "usb-c to wake");
display.drawTextCentered(display.width() / 2, 76, "Enter:yes q:no");
#endif
} else {
// Menu: hibernate / power off
#if defined(LilyGo_T5S3_EPaper_Pro)
display.drawXbm((display.width() - 32) / 2, 20, power_icon, 32, 32);
const int y1 = 58, y2 = 70;
#else
display.drawXbm((display.width() - 32) / 2, 10, power_icon, 32, 32);
const int y1 = 50, y2 = 62;
#endif
char line1[48], line2[48];
#if defined(LilyGo_TDeck_Pro)
snprintf(line1, sizeof(line1), "%shibernate: long press/Enter", _poweroff_selected ? " " : ">");
snprintf(line2, sizeof(line2), "%spower off: long press/Enter", _poweroff_selected ? ">" : " ");
#else
snprintf(line1, sizeof(line1), "%shibernate: " PRESS_LABEL, _poweroff_selected ? " " : ">");
snprintf(line2, sizeof(line2), "%spower off: " PRESS_LABEL, _poweroff_selected ? ">" : " ");
#endif
display.drawTextCentered(display.width() / 2, y1, line1);
display.drawTextCentered(display.width() / 2, y2, line2);
}
}
return _editing_utc ? 700 : 5000;
@@ -1055,11 +1098,46 @@ public:
return true; // Consume all other keys while editing
}
if (c == KEY_LEFT || c == KEY_PREV) {
// SHUTDOWN page -- intercept up/down and Enter before page cycling
if (_page == HomePage::SHUTDOWN) {
if (_poweroff_confirm) {
// Confirmation mode for power off
if (c == KEY_ENTER) {
_shutdown_init = true;
_shutdown_at = millis() + 2500; // extra time for two-phase e-ink update
return true;
}
// Cancel: q, left, prev
if (c == 'q' || c == KEY_LEFT || c == KEY_PREV) {
_poweroff_confirm = false;
return true;
}
return true; // eat all other keys while confirming
}
// Up/down toggles between hibernate and power off
// Only 'w'/'s' (keyboard) — KEY_NEXT/KEY_PREV fall through to page cycling
// so touch swipes and taps can still navigate away from this page.
if (c == 'w' || c == 's') {
_poweroff_selected = !_poweroff_selected;
return true;
}
if (c == KEY_ENTER) {
if (_poweroff_selected) {
_poweroff_confirm = true;
} else {
_shutdown_init = true;
_shutdown_at = millis() + 900;
}
return true;
}
// Left/right fall through to page cycling below
}
if (c == KEY_LEFT || c == KEY_PREV || c == 'a') {
_page = (_page + HomePage::Count - 1) % HomePage::Count;
return true;
}
if (c == KEY_NEXT || c == KEY_RIGHT) {
if (c == KEY_NEXT || c == KEY_RIGHT || c == 'd') {
_page = (_page + 1) % HomePage::Count;
if (_page == HomePage::RECENT) {
_task->showAlert("Recent adverts", 800);
@@ -1103,11 +1181,6 @@ public:
return true;
}
#endif
if (c == KEY_ENTER && _page == HomePage::SHUTDOWN) {
_shutdown_init = true;
_shutdown_at = millis() + 900; // allow e-ink refresh (644ms) before shutdown
return true;
}
return false;
}
};
@@ -1591,6 +1664,36 @@ void UITask::shutdown(bool restart){
// Power off LoRa radio, display, and board
radio_driver.powerOff();
_display->turnOff();
// BQ25896 ship mode: disconnect battery from VSYS entirely.
// Must happen BEFORE _board->powerOff() cuts PIN_PERF_POWERON
// (I2C pull-ups need VDD3V3 to complete the transaction).
// TI recommends: set BATFET_DLY=1 first, then BATFET_DIS=1 as
// the last I2C write to avoid bricking the I2C state machine.
// After tSM_DLY (~10-15s) the BATFET opens during deep sleep.
// Wake: USB-C plug-in only (no reset button -- no power to ESP32).
#ifdef I2C_ADDR_BQ25896
if (_full_poweroff) {
Wire.beginTransmission(I2C_ADDR_BQ25896);
Wire.write(0x09);
Wire.endTransmission(false);
Wire.requestFrom((uint8_t)I2C_ADDR_BQ25896, (uint8_t)1);
uint8_t reg09 = Wire.read();
// Step 1: set BATFET_DLY=1 (bit 3) for safe I2C completion
Wire.beginTransmission(I2C_ADDR_BQ25896);
Wire.write(0x09);
Wire.write(reg09 | 0x08); // BATFET_DLY = bit 3
Wire.endTransmission();
// Step 2: set BATFET_DIS=1 (bit 5) -- MUST be the last I2C write
Wire.beginTransmission(I2C_ADDR_BQ25896);
Wire.write(0x09);
Wire.write(reg09 | 0x28); // BATFET_DIS (0x20) | BATFET_DLY (0x08)
Wire.endTransmission();
}
#endif
_board->powerOff();
}
}
@@ -1605,30 +1708,7 @@ bool UITask::isButtonPressed() const {
void UITask::loop() {
char c = 0;
#if UI_HAS_JOYSTICK
int ev = user_btn.check();
if (ev == BUTTON_EVENT_CLICK) {
c = checkDisplayOn(KEY_ENTER);
} else if (ev == BUTTON_EVENT_LONG_PRESS) {
c = handleLongPress(KEY_ENTER); // REVISIT: could be mapped to different key code
}
ev = joystick_left.check();
if (ev == BUTTON_EVENT_CLICK) {
c = checkDisplayOn(KEY_LEFT);
} else if (ev == BUTTON_EVENT_LONG_PRESS) {
c = handleLongPress(KEY_LEFT);
}
ev = joystick_right.check();
if (ev == BUTTON_EVENT_CLICK) {
c = checkDisplayOn(KEY_RIGHT);
} else if (ev == BUTTON_EVENT_LONG_PRESS) {
c = handleLongPress(KEY_RIGHT);
}
ev = back_btn.check();
if (ev == BUTTON_EVENT_TRIPLE_CLICK) {
c = handleTripleClick(KEY_SELECT);
}
#elif defined(PIN_USER_BTN)
#if defined(PIN_USER_BTN)
int ev = user_btn.check();
if (ev == BUTTON_EVENT_CLICK) {
#if defined(LilyGo_T5S3_EPaper_Pro)
@@ -2586,6 +2666,10 @@ bool UITask::isHomeOnRecentPage() const {
return curr == home && ((HomeScreen *) home)->isOnRecentPage();
}
bool UITask::isHomeOnShutdownPage() const {
return curr == home && ((HomeScreen *) home)->isOnShutdownPage();
}
void UITask::gotoChannelScreen(bool resetDmView) {
ChannelScreen* cs = (ChannelScreen*)channel_screen;
// If currently showing DM view, reset to channel 0 (unless caller opts out)
+4
View File
@@ -66,6 +66,7 @@ class UITask : public AbstractUITask {
int _msgcount;
unsigned long ui_started_at, next_batt_chck;
uint8_t _low_batt_count = 0; // Consecutive low-voltage readings for debounce
bool _full_poweroff = false; // True = BQ25896 BATFET disconnect (USB-C wake only)
int next_backlight_btn_check = 0;
#ifdef PIN_STATUS_LED
int led_state = 0;
@@ -215,6 +216,8 @@ public:
void showBootHint(bool immediate = false); // Show navigation hint overlay on first boot
void dismissBootHint(); // Dismiss hint and save preference
bool isHintActive() const { return _hintActive; }
// BQ25896 BATFET disconnect -- true power off, USB-C required to wake
void setFullPowerOff(bool v) { _full_poweroff = v; }
// Wake display and extend auto-off timer. Call this when handling keys
// outside of injectKey() to prevent display auto-off during direct input.
void keepAlive() {
@@ -291,6 +294,7 @@ public:
bool isEditingHomeScreen() const;
// Check if home screen is showing the Recent Adverts page
bool isHomeOnRecentPage() const;
bool isHomeOnShutdownPage() const;
// Inject a key press from external source (e.g., keyboard)
void injectKey(char c);
+4 -4
View File
@@ -158,7 +158,7 @@ build_flags =
-D MECK_AUDIO_VARIANT
-D MECK_WEB_READER=1
-D MECK_OTA_UPDATE=1
-D FIRMWARE_VERSION='"Meck v1.7.WiFi"'
-D FIRMWARE_VERSION='"Meck v1.8.WiFi"'
build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter}
+<helpers/esp32/*.cpp>
-<helpers/esp32/SerialBLEInterface.cpp>
@@ -225,7 +225,7 @@ build_flags =
-D HAS_4G_MODEM=1
-D MECK_WEB_READER=1
-D MECK_OTA_UPDATE=1
-D FIRMWARE_VERSION='"Meck v1.7.4G"'
-D FIRMWARE_VERSION='"Meck v1.8.4G"'
build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/ui/MomentaryButton.cpp>
@@ -261,7 +261,7 @@ build_flags =
-D HAS_4G_MODEM=1
-D MECK_WEB_READER=1
-D MECK_OTA_UPDATE=1
-D FIRMWARE_VERSION='"Meck v1.7.4G.WiFi"'
-D FIRMWARE_VERSION='"Meck v1.8.4G.WiFi"'
build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter}
+<helpers/esp32/*.cpp>
-<helpers/esp32/SerialBLEInterface.cpp>
@@ -295,7 +295,7 @@ build_flags =
-D HAS_4G_MODEM=1
-D MECK_WEB_READER=1
-D MECK_OTA_UPDATE=1
-D FIRMWARE_VERSION='"Meck v1.7.4G.SA"'
-D FIRMWARE_VERSION='"Meck v1.8.4G.SA"'
build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter}
+<helpers/esp32/*.cpp>
-<helpers/esp32/SerialBLEInterface.cpp>