SOUND (Max, ES8311; Pro stays silent)
- minigb_apu vendored from Meck-P4 (MIT), run at 22050 Hz: at 44100 it
cost 3.6-4.0 ms of the 16.7 ms frame and the game fell to 0.98x, which
drained the DMA queue and glitched. Per-frame timings (core / apu /
i2s wait) now print with the speed line.
- Bring-up reuses the notification-tone path: shared Audio object owns
I2S port 0 (with MCLK), XL9555 routing and amp, then codec init after
the clocks are running. Raw PCM written per frame; a repeated sample
every sixth frame keeps production a hair above the DAC so its clock
paces the game instead of the queue running dry.
- Software gain 1/8 (codec DAC sits at 0 dB); GBC_AUDIO_SHIFT to tune.
- 950 ms delay line (PSRAM) so sound lands with the picture, which
reaches the glass ~1 s after its frame; GBC_AUDIO_DELAY_MS to tune.
- Games start muted; mic key toggles the codec mute in both keyboard
drivers' raw mode; footer shows the current hint. Amp off on quit.
- Notification tones skipped while a game runs (game owns the codec).
MESH LIVE DURING PLAY
- The OTA-style radio pause is gone (GBC_PAUSE_RADIO=1 restores it):
the mesh runs on the other core and already tolerates the panel
blocking the loop. Game header now matches the home screen's -- the
cumulative unread channel count where the node name goes, clock,
battery percent. "New:" toasts suppressed only while a ROM is running
(Snake and Minesweeper keep them).
DISPLAY
- Fast register waveform set to 12 frames per push (FAST_LUT_FRAMES):
judged the best balance of UI speed and contrast. Interface only; the
game keeps the factory waveform.
- Windowed game refresh dropped: measured to save nothing, and it left
the footer row unrefreshed. Normal partial refresh, change-detected.
- Adafruit GFX auto-wrap turned off in the e-ink driver (all e-ink
builds): it dropped a glyph onto the next line's margin when a line
ran a few pixels past the edge (seen with Larger Noto in compose).
Compose wrap width also allows for the driver's 2-unit x offset.
- Game start does one full refresh (tried as a ghosting fix; NPC
ghosting in Pokemon Gold is the game + dither + factory partial
waveform and shows on the stock build too).
Files: examples/companion_radio/{main.cpp, ui-new/GBCEmulatorScreen.{h,cpp},
ui-new/UITask.cpp, ui-new/minigb_apu.{c,h}}, src/helpers/ui/GxEPDDisplay.cpp,
src/helpers/ui/GxEPD2_310_GDEQ031T10_FAST.cpp,
variants/lilygo_tdeck_{max,pro}/TCA8418Keyboard.h
EXPERIMENT (Max only, env meck_max_ble_fastlut; all other builds unchanged)
- New vendored driver src/helpers/ui/GxEPD2_310_GDEQ031T10_FAST.{h,cpp}:
the GxEPD2 GDEQ031T10 driver with a selectable partial-refresh
waveform. Default is "LUT from register" loaded with the register
tables bitbank2/bb_epaper uses for this panel on the T-Deck Pro (one
9-frame push per pixel); the factory OTP waveform stays available.
Full refreshes unchanged. Measured on the Max: partial refresh 244 ms
(652 ms on the factory waveform) on home, menus, ROM list and typing.
Cost: one short push per pixel gives weaker blacks and half-develops
dithered stipple, so it suits text and does not suit the game.
- GxEPDDisplay: setFastWaveform() and fastPartialsSinceFull(), no-ops
outside the experiment env. Switching back to the factory waveform
hibernates the panel so the next refresh goes through a hardware
reset (tested as a ghosting fix; it was not one, kept as a clean
hand-over).
- UITask::setCurrScreen: after EINK_FAST_FULL_EVERY (20) fast partials
the redraw at the next screen change is a full refresh, so the
register waveform's ink imbalance is cleared where a redraw is
happening anyway rather than mid-typing.
- GBC emulator switches the driver to the factory waveform while a
game runs (its dithered picture needs the longer drive) and back to
fast on quit.
- Finding, for the record: moving-sprite ghosting in Pokemon Gold
appears on the stock build too, so it is the game plus dither plus
the factory partial waveform, not the experiment.
GBC EMULATOR (all T-Deck builds)
- "Loading..." alert box the moment Enter is pressed on a ROM row; the
multi-second SD read now runs on the next loop pass, after the box is
on the panel, instead of inside the key handler.
- Games start with a full refresh (clean panel; added during the
ghosting investigation, kept pending review).
- Save writes are timed and the durations logged (remove/open/write/
close), after one unexplained ~45 s UI stall coincided with a save.
Files: examples/companion_radio/ui-new/GBCEmulatorScreen.{h,cpp},
examples/companion_radio/ui-new/UITask.cpp, src/helpers/ui/GxEPDDisplay.h,
src/helpers/ui/GxEPD2_310_GDEQ031T10_FAST.{h,cpp},
variants/lilygo_tdeck_max/platformio.ini
PRO PORT
- The emulator now builds for every T-Deck Pro companion env as well as
the Max (guard moved from LilyGo_TDeck_Pro_Max to LilyGo_TDeck_Pro in
main.cpp, UITask.{h,cpp}, GamesMenuScreen.h and the emulator files;
the T5S3 stays excluded). Pro keyboard driver gains the same raw
joypad mode as the Max's, minus the both-shifts backlight chord, which
stays Max-only along with toggleKeyboardBacklight().
- Same chip, panel, keyboard controller and SD layout as the Max, so
the speed and memory behaviour carries over; the Pro audio build's
free internal RAM after Bluetooth is the one thing not yet measured.
- HYN-touch Pro envs are untested (no v1.1/CST3530 hardware on hand);
to be marked experimental in the release notes.
Files: examples/companion_radio/{main.cpp, ui-new/GBCEmulatorScreen.{h,cpp},
ui-new/GamesMenuScreen.h, ui-new/UITask.{h,cpp}}, variants/lilygo_tdeck_pro/TCA8418Keyboard.h
Follow-up to the v1.14 emulator commits. T-Deck Max unless noted.
MEMORY AND STABILITY (GBCEmulatorScreen.cpp)
- Emulator task stack is now 8 KB, static, reserved at boot. Once
Bluetooth has been switched on in a boot its stack stays resident
(SerialBLEInterface::disable() never releases it) and a later game
launch could not find 12 KB of internal RAM in one piece. Measured
use is about 2 KB.
- Core context released on every failed launch, not just on quit; a
failure no longer holds 50 KB.
- No periodic full refresh during play (too distracting). The full
refresh on quit stays, to clear ghosting before the ROM list.
SAVES
- .sav written in the background: cart-RAM writes mark it dirty and
the UI task writes the file once the game has left its cart RAM
alone for 2 s, i.e. shortly after the user saves in-game. Quit
writes only if still dirty, and no longer draws and refreshes an
intermediate "Saving..." frame (that frame cost 650 ms by itself).
INPUT
- Both-shifts keyboard-backlight chord works mid-game: it is the one
key raw joypad mode lets through. The toggle body moved from
handleKeyboardInput() into toggleKeyboardBacklight() (main.cpp) so
the game and the normal path share one on/off state.
- On quit the keyboard stays in raw mode, still drained through the
panel's busy wait, until the ROM list has been drawn. Keys pressed
during the quit flash used to queue in the TCA8418 and replay as
navigation afterwards (one held Q ran four screens back).
PICTURE
- Game pictures after the first refresh only the 216-line game window
(GxEPDDisplay::requestWindowRefresh, consumed by endFrame). Measured
at 652 ms either way, the same as full-panel: the panel's waveform
time is the floor, so this saves data transfer only.
TYPING (main.cpp, all e-ink builds)
- COMPOSE_REFRESH_INTERVAL 700 -> 0. lastComposeRefresh is stamped
after the blocking e-ink refresh completes, so the interval only
added dead time on top of it. Worst-case keystroke-to-screen drops
from about 1.6 s to about 0.9 s. COMPOSE_TYPING_PAUSE unchanged.
GAMES MENUS
- Games menu and Game Boy ROM list rows follow the user's font size
and style via the NodePrefs helpers (as the channel picker does);
Max touch hit-test updated to match. Footers read Q:Back.
BUILD ENVS (Pro)
- meck_audio_wifi_hyn and meck_audio_standalone_hyn, the HYN-touch
counterparts of the existing meck_audio_ble_hyn.
Files: examples/companion_radio/{main.cpp, ui-new/GBCEmulatorScreen.{h,cpp},
ui-new/GamesMenuScreen.h}, src/helpers/ui/GxEPDDisplay.{h,cpp},
variants/lilygo_tdeck_max/TCA8418Keyboard.h,
variants/lilygo_tdeck_pro/platformio.ini
EMULATOR (T-Deck Max)
- Q now quits a running game, matching the back key used across the
rest of the interface. Shift+Backspace still works. Added as a second
exit latch in the keyboard driver's raw joypad mode; the on-screen
hint reads "Q: Quit".
variants/lilygo_tdeck_max/TCA8418Keyboard.h
examples/companion_radio/ui-new/GBCEmulatorScreen.{h,cpp}
README
- New "Game Boy / Game Boy Color Emulator (T-Deck Max, v1.14+)"
subsection under Games: what it is, where to get free homebrew ROMs
(uCity, Halo: Combat Devolved), where to put them (/roms, .gb/.gbc,
32 files, names under 48 characters), how to launch and play (key
table, radio paused and CPU at 240 MHz while a game runs), what the
picture looks like and why it updates about 1.4 times a second,
saves (.sav on quit, no RTC state), and the release's limits.
- Contents entry for the new subsection; Games intro mentions the
Games tile and the Max-only emulator.
- SD Card Requirements lists Game Boy ROMs and saves.
- T-Deck Max intro cross-references the emulator.
- Road-Map: emulator done on the Max; Pro support, ES8311 sound with
mic-key mute, and RTC save state listed as to-do.
- Third-Party Libraries: Peanut-GB (MIT, Mahyar Koshkouei) added, with
a note that no Game Boy software is bundled.
Version tags on the new items are v1.14; the global version bump
follows in the next commit.
MAPS FIX (all T-Deck Pro and Max builds)
Opening Maps from the home tile showed "SD card not found" until the G key
had been pressed once that boot. The map screen's SD flag was only set by
the G key handler; the touch tile and CardKB routes opened the screen
without it. The G handler's setup (SD flag, GPS position, contact markers,
CPU boost) is now openMapScreen() in main.cpp and all three routes call
it. G on the map (re-centre) is unchanged.
PLATFORMIO (variants/lilygo_tdeck_max)
- New meck_max_ble_garmin env for the Garmin watch companion: identical to
meck_max_ble with BLE_DEBUG_LOGGING, MECK_BLE_JUST_WORKS (no-passkey
pairing), MECK_BLE_SMALL_MTU_SPLIT (23-byte ATT frame slicing),
MECK_WATCH_EXT (watch extension commands) and MECK_BLE_SLOW_INTERVAL
switched on.
- meck_max_ble now has those five flags commented out, so the standard
phone-app build is isolated from the watch experiments.
GAME BOY / GAME BOY COLOR EMULATOR (T-Deck Max only in this build)
New "Game Boy" entry in the Games menu. Lists .gb/.gbc ROMs in /roms on
the SD card and runs them with the Peanut-GB core (same patched copy as
Meck-P4) in its own task on core 0 at the real 59.73 fps, drawing a
black-and-white snapshot to the e-ink at the panel's refresh rate.
- Keys: W/A/S/D d-pad, K = A, J = B, Enter = Start, Space = Select,
Shift+Backspace = quit (save written). No sound yet.
- Saves: .sav next to the ROM, written on quit.
- LoRa radio in standby and mesh loop paused while a game runs (reuses
the OTA pause). CPU held at 240 MHz.
- Keyboard polled through the e-ink busy wait so input is not delayed by
the ~650 ms refresh; the 800 ms refresh floor is lifted during play.
- Speed line printed to serial every 5 s.
Files:
- ui-new/GBCEmulatorScreen.{h,cpp} (new), ui-new/peanut_gb.h (new)
- ui-new/GamesMenuScreen.h: GAME_GBC entry
- ui-new/UITask.{h,cpp}: gbc_screen, goto/is/get, resetIdleTimer(),
no refresh floor while a game runs
- main.cpp: launch, exit and key routing
- variants/lilygo_tdeck_max/TCA8418Keyboard.h: raw joypad mode
- src/helpers/ui/GxEPDDisplay.h: setBusyCallback()
Pro builds compile unchanged (all emulator code is guarded).
- ChannelDetails gains scope_key[16]; MyMesh::setChannel derives it from
scope_name on every write (screens, serial, phone sync, boot load), so
sending copies bytes instead of doing a name lookup and derivation per
message. Port of the Meck Watch regions fix.
- getChannelScopeName now copies out instead of returning a pointer into
a local (dangling; caused intermittent unscoped sends).
- BLE small-MTU slices become [tag][len][payload] (18 payload bytes) so
stream transports (nRF52) can find slice boundaries; same format on
ESP32 for one dialect across nodes and the Garmin app.
- New MECK_BLE_SLOW_INTERVAL flag: request a 100-200 ms connection
interval instead of 15-20 ms for the watch build.
Canned messages (T-Deck Max only):
- NodePrefs gains canned_msgs[10][134] as the final field; DataStore load is short-read tolerant (older prefs upgrade to empty slots), NUL-forced, appended in savePrefs. Storage is shared across boards so the prefs layout stays uniform; the Settings entry point is gated to the Max.
- Settings -> Canned Messages: ten slots, full-length inline editor (tail + n/133 counter); Enter commits, empty clears, Sh+Del cancels. T5S3 VKB path exists behind the same gate.
- Channel screen overlay: the speech-bubble pad toggles the list on a channel or DM conversation; tapping a row sends via sendComposedMessage() (channel -> that channel, DM conversation -> DM to that contact); all-empty shows an alert. Other screens keep the channel-picker action.
- Post-test fixes: tap intercept moved ahead of the vy<18 go-home rule that ate row taps and left the overlay stuck; any keyboard key closes the overlay in handleKeyboardInput (keys never reach ChannelScreen::handleInput on Pro/Max); setViewChannelIdx resets overlay state like the path overlay.
40 MHz extreme-battery test builds: meck_max_standalone_40mhz, meck_audio_standalone_40mhz, meck_4g_standalone_40mhz. CPU idle/boost/low-power all 40 MHz; MECK_40MHZ_TEST gates the modem (forced off, config kept), GPS (rail off, page hidden), browser/OTA, alarms, audio features and notification MP3s; gated tiles dither and go inert; MESH_DEBUG=1. The Pro envs deliberately re-run the March 80->40 TCA8418 config (noted in env comments).
T-Deck Pro v1.1 HYN touch (meck_audio_ble_hyn, Meck v1.12.6.HYN): opt-in MECK_PRO_HYN_TOUCH switches touch to the vendored Hynitron driver, auto-detecting CST3530/CST328 at 0x1A. Seven driver guards widened; XL9555 callbacks stay Max-only; explicit config SDA13/SCL14/RST38/INT12; the manual GPIO38 reset pulse is suppressed (driver self-resets); readTouch and the 4G dialer dispatch widened. Link fix: ui-new/*.c added to the env src filter (the cst66xx/cst3xx/cst226se providers were not compiled). Confirmed working by an affected v1.1 (CST3530) user.
Pro audio builds: the full-width Phone tile is no longer rendered (no modem; its tap was already inert).
Applied the same memw barrier + identical comment to twatchs3plusboard.cpp, hardening the Plus against the same latent race it was previously surviving only by luck
T-Watch:
Disabled rx boosted gain & removed meck rx duty cycle flag for S3 and S3 Plus.
Bumped charge rate on S3 to 150MA.
Added watch custom incoming path/hops view in channel message view mode.
Added diagnostic pmubtn build to S3 plus to iron out source of power drain in S3 firmware.
All boards:
ChannelScreen.h -- delivered DMs now render Delivered in the prefix position, exactly where Sending x/N and Failed sit, and the drawn tick is gone entirely (the branch, the reserved gap and the drawDeliveredTick helper -- zero references remain). For what it's worth, the photo vindicates the change: those stray pixels below the line next to "2s" are the tick misplaced, because the GFX fonts position from the baseline rather than the glyph top, so the hand-drawn glyph landed too low. Text is the robust answer.
UITask.cpp -- the toast persistence is a scheduling starvation, and the status pushes made it visible. Dismissal works by scheduling a render at _alert_expiry; but after every frame the e-ink throttle applies an 800ms floor (minNext, UITask.cpp:2856-2860), and the ~644ms partial-refresh block inside endFrame() shifts millis() before the clamp runs. So any render landing inside the alert window -- which a delivered/attempt push now triggers via forceRefresh() -- redraws the overlay and then gets its expiry wake clamped past the expiry. Your user's photo is precisely that frame: tick and toast on screen together, with the clearing render deferred. The fix is a single insertion after the shared clamp: if an alert is still active and lapses before the next scheduled render, wake at expiry instead. One bounded extra render per toast, both display branches covered, and the toast now clears on time no matter what the retry engine pushes mid-window.
The SettingsScreen SUB_CHANNELS sub-screen drives every per-channel
action from physical-keyboard hotkeys (Ent: scope, N: notif, T: tone,
X/Hold: delete), so on the touch-only watches the channel list rendered
but nothing was actionable. Replace it on MECK_TWATCH builds with a
standalone WatchChannelConfigScreen following the WatchAlarmScreen /
WatchNotesScreen conventions. Desktop builds are unchanged.
LIST shows each channel with its region scope tag and notification
state (tap to select, tap again or button to open, paged like Notes).
DETAIL offers: Region -- opens the watch keyboard via a new TWKB_SCOPE
purpose, pre-filled with the current scope, committed through the same
setChannel + saveChannels path as desktop (empty submission clears);
Clear region -- reverts to the device default directly; Notify --
tap-cycles All -> Mentions -> Off with savePrefs, matching the desktop
N-key order. Delete is button-driven rather than touch: a short press
(PWR on the S3, boot button on the Plus) arms it, a second press
within 3s confirms. Channel 0 remains protected. The tone picker is
deliberately absent -- the watch has no audio path.
Settings hands off via a _wantsWatchChannels flag polled by
UITask::loop (mirrors the _needsTextVKB handshake); Back returns to
Settings via gotoSettingsScreen. Keyboard results route through the
existing consumeSendRequest purpose dispatch.
Note: deleteChannel replicates SettingsScreen's compaction verbatim,
including its existing quirk of not shifting channel_notif[] -- kept
faithful rather than silently diverging; fix both together if desired.
Files: WatchChannelConfigScreen.h (new, both variant dirs),
TWatchComposeScreens.h (both variant dirs, TWKB_SCOPE),
SettingsScreen.h, UITask.h, UITask.cpp, main.cpp
S3 fix (CHG_VOL_4V2 → CHG_VOL_4V35 in TWatchS3Board.cpp
Notes screen:
WatchNotesScreen.h → both variants/lilygo_twatch_s3/ and variants/lilygo_twatch_s3_plus/ (byte-identical copies, like TWatchComposeScreens.h).
TWatchComposeScreens.h → both variant dirs. Adds TWKB_NOTE to the Purpose enum and a setInitialText() (clamped at the 133-char MAXLEN) for prefill editing.
UITask.h → watch_notes_screen member, isOnWatchNotesScreen(), getWatchNotesScreen(), all MECK_TWATCH-gated, mirroring the alarm-screen pattern exactly.
UITask.cpp → include, construction in begin() (after the LittleFS mount, like the alarm), gotoNotesScreen() now routes to the watch screen on MECK_TWATCH (T-Deck/T5S3 behaviour untouched — the SD NotesScreen path below is unreached on watches), the TWKB_NOTE result case (save via applyComposedNote, alert on error, return to the notes screen), and the loop poll that opens the keyboard — prefilling via setInitialText(getEditText()) when editPending().
main.cpp → include plus the tap-dispatch block, same shape as the alarm's.
CPUPowerManager.h — S3 variant, CPU_FREQ_BOOST capped to 80 MHz.
main.cpp — locked-loop throttle extended to the watches via the two MECK_TWATCH guard additions.
Both watches undercount steps. Two divergences from LilyGo's own configuration
for this hardware, applied to both boards:
1. Watermark. LilyGoLib calls setStepCounterWatermark(1) and SensorLib's
BMA423_StepDetector example passes step_counter_wm = 1. Meck's raw-I2C enable
never touched those bits, leaving whatever the config-file blob defaults to.
The watermark is BMA423_STEP_CNTR_WM_MSK (0x03FF), spanning feature_config
[0x36] as LSB and the low two bits of [0x37] as MSB; it does not collide with
the enable bit, which is bit 4 of [0x37]. Both are now written in one
read-modify-write. The bit arithmetic was checked exhaustively against
bma423_step_counter_set_watermark() + feature_enable() over all 65536 starting
states of cfg[0x36]/cfg[0x37]: identical.
2. ODR. SensorLib's step detector example runs the accelerometer at 100 Hz. Meck
used 50 Hz. Everything else already matched: NORMAL, FS_2G, OSR2_AVG2,
CIC_AVG_MODE. Reverting the single 100.0f literal to 50.0f A/Bs this against
the watermark change.
Ruled out: the axis remap is BOTTOM_LAYER_TOP_RIGHT_CORNER on both, identical to
LilyGoLib, and the enable bit itself was already register-for-register what
bma423_feature_enable(BMA423_STEP_CNTR, TRUE) writes.
Not attempted: the 25-value pedometer parameter block at feature_config[0x04..0x35],
reachable via bma423_stepcounter_set_parameter() but with no reference values.
The alarm was self-dismissing after ~10-15 seconds. Cause: AUTO_OFF_MILLIS is
15000, so the display turned off shortly after the alarm fired; UITask.cpp:2769
raise-to-wake then saw !_display->isOn() && board.tiltFired() -- the buzzing motor
on the wrist is exactly what trips the BMA423 tilt detector -- and called
setCurrScreen(lock_screen). The 'navigating away counts as a dismiss' rule added
with the alarm screen then killed it. Any tap did the same.
While ringing, UITask::loop now holds the alarm screen current and pushes
_auto_off forward each iteration. The display therefore stays on, raise-to-wake
is never reached, and checkDisplayOn() passes KEY_ENTER through to handleInput()
rather than swallowing the first press as a wake.
Dismissal is now the PWR key only (KEY_ENTER). Taps are swallowed. The
WATCH_ALARM_RINGING_MS timeout (5 minutes) is unchanged and remains the backstop.
Ringing footer updated from 'Tap to dismiss' to 'Press PWR to dismiss'.
The ordering inside loop() makes this safe: the button dispatch and
curr->handleInput() both run before the alarm hold, so a dismiss takes effect on
the same iteration and the hold is not re-applied. The 90s per-slot fire cooldown
prevents an immediate re-fire within the alarm's own minute.
Fixes a regression from the MECK_TWATCH rename. main.cpp's tap dispatch had
'#if defined(MECK_TWATCH) && HAS_GPS' wrapped around two unrelated things: the
map tap handler AND the early return that stops the watch falling through to the
T5S3 tile hit-test. MECK_TOUCH_ENABLED is defined for MECK_TWATCH (main.cpp:712),
so on the S3 that early return vanished and a plain tap on the home tile page ran
the T5S3 geometry (tileW=40, tileH=22). Split into two gates. The other three
HAS_GPS gates were audited and are genuinely map-only.
Adds WatchAlarmScreen: four slots, day-of-week presets, vibrate-only. It keeps
AlarmScreen.h's conventions (bit 0 = Sunday, dowFromEpoch, effect 14, the
3-buzz / 1200ms / 4000ms-pause cadence) but shares no code, because AlarmScreen
is bound to ESP32-audioI2S, an SD card and a 1-21 Audio volume scale, none of
which exist here. Config lives on the LittleFS 'maps' partition, already mounted.
Alarms are ticked from UITask::loop, not the screen's poll(), so one fires with
the display off or another screen showing. Navigating away while ringing counts
as a dismiss.
DRV2605Haptic.h is copied into the watch variant rather than shared, so MAX
builds are untouched. The watch needs no motorEnable(): the DRV2605 sits on
BLDO2, which power_init() already brings up.
The Maps tile becomes Alarms on LILYGO_TWATCH_S3 only. The Plus keeps Maps.
The 'buzz' CLI command now also builds on the watch and takes an optional effect
number ('buzz 14'), for characterising the motor. Exact-match behaviour of plain
'buzz' on the MAX is unchanged.
Three build failures, all mine:
1. XPowersLib access. setPowerChannelVoltage, enable/disablePowerOutput and
isPowerChannelEnable are protected on XPowersAXP2101 and public only on
XPowersLibInterface, so holding the PMU as the concrete type made every rail
call illegal. The board now keeps both pointers to the same object: PMU
(interface) for the channel ops, _axp (concrete) for setIrqLevelTime and the
PWRON press/release edges, which the interface does not declare. Verified
against XPowersLib v0.2.9, the version ^0.2.7 resolves to.
2. ENV_INCLUDE_GPS. sensor_base defaults it to 1. The variant zeroed every other
ENV_INCLUDE_* but not that one, so gpsStream, board.gpsPowerOn/Off and the
GPS home page were all still compiled in. Now -D ENV_INCLUDE_GPS=0.
3. GPS_BAUDRATE is referenced unguarded by MyMesh.cpp's gps.baud CLI command.
variant.h now supplies the same #ifndef fallback the T5S3 variant uses.
Also renames the S3 Plus envs to meck_twatch_s3_plus_standalone and
meck_twatch_s3_plus_ble, so they no longer read as the generic watch build.
Nothing outside variants/lilygo_twatch_s3_plus/platformio.ini referenced the old
names.
Same silicon and the same pin map as the T-Watch S3 Plus, verified against the
T_WATCH-S3 schematic (rev 25-03-24) and LilyGo's utilities.h. The differences
are what is populated:
- no onboard GNSS. HAS_GPS/ENV_INCLUDE_GPS are left undefined, which drops the
map screen, the GPS home page and BLDO1 rail control via the gates added in
the MECK_TWATCH rename.
- no GPIO button. Schematic sheet 1 wires the side switch SW7 to net PWR_KEY
-> AXP2101 pin 30 (PWRON), so PIN_USER_BTN is undefined and MECK_PMU_BUTTON
selects PMUButton, which reads PKEY interrupts over I2C. Short press is
enter/select; a 6s hold is a hardware power-off the firmware never sees.
- DLDO1 is the MAX98357A speaker rail (schematic sheet 6, net SPK_VDD), not
'unused' as LilyGo's doc claims. It stays off, so the amp is unpowered.
- VBACKUP is enabled at 3300 mV. It charges the MS412FE coin cell (J12) that
backs the PCF8563's single-VDD supply. Leaving it off drained the cell with
nothing to replenish it.
New envs: meck_twatch_s3_standalone, meck_twatch_s3_ble.
No existing board is affected: MECK_PMU_BUTTON is defined only here.
The macro was doing double duty: gating the 240x240 touch UI (tile grid, lock
screen, VKB, grey palette) and gating the Plus's onboard GNSS. A non-GPS watch
cannot reuse the first without dragging in the second.
MECK_TWATCH watch form factor
LILYGO_TWATCH_S3_PLUS Plus-only hardware (GNSS on BLDO1, GPIO0 user button)
MECK_PMU_BUTTON user button is the AXP2101 PWRON key, not a GPIO
84 sites renamed across 14 files. The WatchMapScreen include, construction and
its three touch handlers now also require HAS_GPS, as does the Maps tile branch
inside the home tile grid.
Behaviour-preserving: the Plus defines both MECK_TWATCH and HAS_GPS=1, so every
new condition evaluates as before. No other board defines MECK_TWATCH.
T-Watch: So many UI changes. Also swapped render battery indicator to use the AXP2101 gauge for hopefully higher accuracy. Also implemnted meck rx duty cycle for watch builds only to test out power saving effects first on watch build before implementing elsewhere.