You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .github/code-summary.md
+17-4Lines changed: 17 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -240,6 +240,7 @@ All modules in `src/core/` follow the **class + global instance** pattern:
240
240
-`station_t` fields (`name`, `url`, `title`) are sized by `STATION_FIELD_LENGTH` (default 170, defined in `options.h`). These are RAM-only fields — not NVS-stored. `BUFLEN` has been retired; use `STATION_FIELD_LENGTH` for station metadata buffers across the codebase.
241
241
-`SD_PATH_LENGTH` (256, defined in `sdmanager.h`) is used for SD filesystem path buffers where paths may exceed 170 bytes.
- IR remote codes use a separate named store: `struct irstore_t` with one `uint64_t[3]` field per button (`power`, `mute`, `up`, `down`, `prev`, `next`, `play`, `mode`, `hash`, `n0`…`n9`). It is persisted in its own NVS namespace (`ehradioir`) through a dedicated key map in `config.cpp`; buttons are addressed by name, never by array position. The old positional `ircodes_t` blob was removed.
243
244
-`Config::saveValue(...)` API now has two simple overloads only:
@@ -260,6 +261,8 @@ All modules in `src/core/` follow the **class + global instance** pattern:
260
261
- playlist-mode initialization and file-presence checks before delegating playlist indexing/load helpers to `utility`
261
262
- canonical SPIFFS asset allowlists (`Config::wwwFiles[]`, `Config::dataFiles[]`) used by startup recovery and file-maintenance flows
262
263
- reset section handlers (`defaultSettings(...)`)
264
+
- named IR code storage in the dedicated `ehradioir` NVS namespace (`IR_MAGIC` 1812 stored under key `irset`, one key per button via `irKeyMap[]`); helpers `loadIR()`, `saveIR()` / `saveIR(button)`, `irCodes()`, `clearIR()`, `irButtonByName()`, `irButtonCount()`, `irButtonKey()`, `irAction()`
265
+
-`deleteOldKeys()` also drops the legacy `ircodes` key from the `ehradio` namespace
263
266
- SPI bus initialization: `Config::init()` calls `SPI.begin(SPIA_SCK, SPIA_MISO, SPIA_MOSI)` only when `SPIA_SCK` is defined and `!= 255`, and `SPIB.begin(SPIB_SCK, SPIB_MISO, SPIB_MOSI)` only when `SPIB_SCK` is defined and `!= 255`. I2C-only builds skip SPI init entirely. Both buses are initialized before `_initHW()` and before `display.init()` / `player.init()`. Both SPI buses are fully configured before any peripheral uses them. `SPIClass SPIB(SPI_BUS_SECONDARY)` is declared at file scope in `config.cpp`; extern declared in `config.h`.
264
267
- SD-specific behavior:
265
268
-`_initHW()` configures `SD_CARD_DETECT_PIN` as `INPUT_PULLUP` when available
@@ -318,7 +321,7 @@ All modules in `src/core/` follow the **class + global instance** pattern:
318
321
- weather cache and formatting logic
319
322
- centralized runtime logging for reconnect/weather/boot progress/time-sync via `FUNCTIONLOG`/`SERIALLOG`/`BOOTLOGX`
320
323
- web-stream reconnect now resumes through `player.resumeLastWebSource()` so direct URL sources can recover via `/data/laststation.url` instead of always falling back to `lastStation`
321
-
-`retryStreamConnection` task (40 attempts × 15 s) can be externally cancelled by `commandhandler.cpp``cancelStreamRetry()` when the user issues any playback-changing command; the task also cleans itself up when conditions change (user stops, WiFi drops, or playback resumes)
324
+
-`retryStreamConnection` task (40 attempts × 15 s) is cancelled through `MyNetwork::cancelStreamRetry()`, the single owner of `streamRetryTaskHandle` (called by commandhandler on playback-changing commands, by `player.prev()`/`next()`/`toggle()`, and by `utility.turnoff()`); the task also cleans itself up when conditions change (user stops, WiFi drops, or playback resumes)
@@ -338,6 +341,8 @@ All modules in `src/core/` follow the **class + global instance** pattern:
338
341
- error reporting and display/net updates
339
342
- command queue depth: `xQueueCreate(10, ...)` — increased from 5 to prevent queue overflow during rapid mode-switch sequences (SD→web transitions) where multiple commands (PR_STOP, PR_PLAY, PR_VUTONUS) arrive before the first finishes processing.
340
343
- direct playback lifecycle side effects for `rgbled` and `backlightControls` (start/stop + initial stopped-state sync)
344
+
-`mute()`: volume-0 toggle backed by the private `_muteVol` member; uses raw `getVolume()`/`setVolume()` so `config.store.volume` and the displayed volume are deliberately untouched. Shared by the physical mute buttons, the `mute` command, and the IR mute button — the `DSP_DUMMY` suppression lives only at the physical-button call site.
- VS1053 SPI: `Player::Player()` constructor passes `&VS1053_SPIBUS` to the `Audio(CS, DCS, DREQ, SPIClass*)` constructor. `VS1053_SPIBUS` is the `SPIB` or `SPIA` object resolved by `options.h`. No `SPIClass` declared in `player.cpp` or `player.h`.
342
347
- Coupling:
343
348
- updates display queue and websocket state
@@ -411,6 +416,7 @@ All modules in `src/core/` follow the **class + global instance** pattern:
411
416
-`/settings.html`, `/update.html`, `/ir.html` no longer served via `index_html[]` — handled by PSRAM cache fallthrough
412
417
- websocket command parsing and outbound updates
413
418
- state request queue processing (`GETSYSTEM`, `GETSCREEN`, `GETLOCALE`, etc.)
419
+
- IR websocket helpers: `irToWs()` (protocol + code) and `irValsToWs()` (the active button's 3 codes, read through `config.irCodes()`)
414
420
- online update check/start tasks
415
421
- radio-browser search and curated task management
416
422
- exact-match-first preview/add handling on `/search`; unmatched preview now uses the same direct URL playback path as `playurl` instead of a mutating playlist scan
@@ -437,7 +443,9 @@ All modules in `src/core/` follow the **class + global instance** pattern:
437
443
- own shared command aliases across ingress channels (`playstation`/`play`, `boot`/`reboot`, `vol+`/`volup`, `dim`/`brightness`, `dspon`/`screenon`)
438
444
- player-command parity helpers (including exact-match-first direct URL playback command routing for `playurl` / `burl`)
439
445
- trigger curated operations and locale update tasks
440
-
- cancel the stream retry task (`cancelStreamRetry()`) before executing user-initiated playback commands (`stop`, `playstation`, `prev`, `next`, `toggle`, `turnoff`, `burl`, `mode`, `submitplaylist`) so explicit user actions always interrupt automatic reconnection loops
446
+
- cancel the stream retry task (`network.cancelStreamRetry()`) before executing user-initiated playback commands (`stop`, `playstation`, `prev`, `next`, `toggle`, `turnoff`, `burl`, `mode`, `submitplaylist`) so explicit user actions always interrupt automatic reconnection loops
447
+
-`turnon` / `turnoff` delegate to `utility.turnon()` / `utility.turnoff()`; the `mute` command maps to `player.mute()`
448
+
- IR recorder commands: `irbtn` resolves a button **name** via `config.irButtonByName()` (`-1` stops recording and saves), `chkid` selects the slot, and `irclr` clears a slot through `config.clearIR()`
441
449
- Critical coupling file for setting changes.
442
450
- New commands: `theme` (theme switching), `layout` (layout switching), `inverttitle` (invert title toggle). All persist via `saveValue` and trigger `display._applyState()`.
443
451
@@ -471,6 +479,8 @@ All modules in `src/core/` follow the **class + global instance** pattern:
471
479
- Converts hardware input events into same core actions used by WebUI (`controlsEvent`, player commands, display mode changes).
472
480
-`Controls::loop()` now calls `backlightControls.controlsLoop()` directly for non-PLAYER backlight wake behavior.
473
481
- IR record debug text now routes through centralized logging macros.
482
+
- IR dispatch is name-based: `irLoop()` iterates `config.irButtonCount()`, matches codes from `config.irCodes(button)`, then switches on the behaviour id from `config.irAction(button)` (`IRACT_POWER`, `IRACT_MUTE`, `IRACT_UP`, `IRACT_DOWN`, `IRACT_PREV`, `IRACT_NEXT`, `IRACT_PLAY`, `IRACT_MODE`, `IRACT_HASH`, `IRACT_DIGIT`). Digit buttons derive their value from the `n0`…`n9` key. The old positional `IR_UP`…`IR_HASH` enum is gone. Power/mute/mode are local actions and are allowed while offline or showing `LOST`.
483
+
- Physical mute (`EVT_ENC2_SW` / `EVT_BTN_MODE` double-click) calls `player.mute()` and keeps the `DSP_MODEL == DSP_DUMMY` no-op guard at the call site.
474
484
- Screensaver wake hardening:
475
485
-`controlsEvent()` now flushes pending display requests (`display.resetQueue()`) and zeroes screensaver tick counters before queueing `NEWMODE, PLAYER` when waking from `SCREENSAVER`/`SCREENBLANK`, preventing one-detent rotary wake races where a stale queued screensaver mode request could immediately re-apply.
476
486
@@ -520,6 +530,7 @@ All modules in `src/core/` follow the **class + global instance** pattern:
520
530
- playlist CSV parsing and station lookup/load helpers
- standby on/off helpers `standbyon()`, `standbyoff()`, `standbytoggle()` (shared by the `standbyon`/`turnon` and `standbyoff`/`turnoff` commands and the IR power button); `standbyoff()` also calls `network.cancelStreamRetry()`
523
534
- SPIFFS file-maintenance helpers shared with startup and WebUI update paths:
524
535
-`cleanupSpiffs()`
525
536
-`deleteMainwwwFile()`
@@ -646,6 +657,8 @@ All modules in `src/core/` follow the **class + global instance** pattern:
646
657
647
658
## `data/www/ir.html`
648
659
- IR recording and assignment UI.
660
+
- Every `.irbutton` carries a `data-irid` name (`power`, `mute`, `up`, `down`, `prev`, `next`, `play`, `mode`, `number`, `n0`…`n9`) that maps 1:1 to the `irstore` field / NVS key, so DOM order is irrelevant.
661
+
- The shell loads the page body from `irrecord.html`; the `/ir.html` route itself is handled by the PSRAM cache fallthrough.
649
662
650
663
## `data/www/search.html`
651
664
- Search UI for radio-browser integration.
@@ -658,7 +671,7 @@ All modules in `src/core/` follow the **class + global instance** pattern:
658
671
- Contains logic previously in `ir.js`, `updform.js`, `playstation.js`
659
672
- station preview/play helper (`sendStationAction`)
660
673
- online update check/start UI helpers
661
-
- IR setup/learn interactions (`initControls`, `checkSelect`, `irClear`, `backRecord`)
674
+
- IR setup/learn interactions (`initControls`, `checkSelect`, `irClear`, `backRecord`); `irbuttonClick()` sends the button's `data-irid` name as `irbtn=<name>` and `irbtn=-1` on deselect
662
675
- also consolidated with `data/www/locale.js`
663
676
- i18n runtime helper (`t(...)`) and translation application (`applyI18n`).
664
677
- Applies key-based translations to DOM and fallback behavior.
@@ -830,7 +843,7 @@ Each config type has its own field that makes a widget meaningful, and that is w
830
843
## Screen Rendering Fixes (Session: VU Rotated Layout)
831
844
832
845
-**New layout flag**`LayoutData::rotateVU` (exposed via `rotateVU_ptr`), treated exactly like `boomboxStyle` — absent means false. `VuWidget::_rotate` is read from `rotateVU_ptr` in `init()`.
833
-
-**Layout ordering** in `displayTFT480x320conf.h`: `_layoutNames` is now `Default`, `Default (VU Rotated)`, `VaraiTamas (BoomBox)`. The rotated layout is layout #2 (`bandsConf = { 32, 130, 4, 2, 10, 3 }`, `.rotateVU = true`); BoomBox moved to #3.
846
+
-**Layout ordering** in `displayTFT480x320conf.h`: `_layoutNames` is now `Default`, `Default (VU Rotated)`, `BoomBox (VaraiTamas)`. The rotated layout is layout #2 (`bandsConf = { 32, 130, 4, 2, 10, 3 }`, `.rotateVU = true`); BoomBox moved to #3.
834
847
-**Blit choice**: `VuWidget::_draw()` uses the manual `startWrite()` / `setAddrWindow()` / `writePixels()` / `endWrite()` sequence for all three modes. `drawRGBBitmap()` was deliberately removed from the widget layer — the manual path depends only on `setAddrWindow` and `writePixels`, which every TFT driver is guaranteed to implement, and it issues a single bulk transfer rather than one `writePixels` call per scanline. Do not switch this back.
835
848
-**Direction**: the rotated VU fills left-to-right with `_vumaxcolor` at the right end.
To enter a special SD-card only mode (with network functionality disabled), hold down any button (including encoder switches) shortly after powering-up (until the display shows something).
155
-
It is not necessary to hold these buttons while powering-up, and actually could cause issues if the builder put a button on a strapping pin.
155
+
It is not necessary to hold this button while powering-up, and actually could cause issues if the builder put that button on a strapping pin.
156
156
157
157
You can also enter this mode by pressing the play button, clicking a rotary encoder button, or tapping the touch screen
158
158
in AP/Improv Mode or when you see `* LOST *`, which will trigger a reboot.
159
159
160
-
If no RTC is connected, the clock will not display.
160
+
An RTC module is required for the time to be displayed in this mode.
161
+
Otherwise, the clock will not be shown.
162
+
161
163
Most settings, as set in the WebUI, are preserved in this mode.
162
164
These are disabled: Safe mode, Deep Sleep, Mode switch.
163
165
164
166
SD Shuffle (which makes the "previous" button do nothing) will be read from preferences and changeable using Mode switch (the mode button or double-click of a rotary encoder).
165
167
It is not saved to preferences in this mode. All other buttons will have expected behaviour.
166
168
167
-
Exit this mode (reboot with network functionality) by powering off and powering on again.
169
+
Exit this mode (and reboot with network functionality) by powering off and powering on again.
168
170
169
171
---
170
172
@@ -179,7 +181,8 @@ For a detailed guide to supported hardware and peripherals, wiring, and audio is
179
181
## A Warning & Disclaimer
180
182
181
183
With the `2026.07.31` release, the display architecture was overhauled to make layouts and colors changeable while running.
182
-
This involved significant changes to the original code and layouts.
184
+
This involved significant changes to the original ёRadio code and layouts.
185
+
Tools are available in the repository to assist in converting files from ёRadio mods.
183
186
184
187
Trip5 builds include OLED 128x64 and TFTs 480x320, 320x240, 160x128.
185
188
Other display sizes may have quirks or issues with layouts that need repair.
@@ -202,7 +205,7 @@ There is also a `no_display` build here if you just wish to test functionality.
202
205
Note that if following this path, you don't necessarily to attach all peripherals (rotary, buttons, IR Receiver, SD card, display).
203
206
The radio needs only the ESP board and the audio decoder to function... although your WebUI may show links to peripherals that don't exist.
204
207
205
-
If don't want to mess around with VS Code but would still like your own build added to the Releases, you can make a
208
+
If you don't want to mess around with VS Code but would still like your own build added to the Releases, you can make a
Please also do some research before requesting a firmware.
208
211
@@ -315,7 +318,7 @@ For that and other major needed changes to the codebase, there is a `code-issues
315
318
316
319
| Date | Release Notes |
317
320
| ---------- | ---------------- |
318
-
| 2026.09.13`dev`| NV3007 added (work in progress), Fixes to: SSD1327, `ROTATE_90` for square displays, TFT display resolutions, volume page, VU meter (timing, orientation, peaks), Firefox mobile |
321
+
| 2026.09.15`dev`| NV3007 added (work in progress), Fixes to: SSD1327, `ROTATE_90` for square displays, TFT display resolutions, volume page, VU meter (timing, orientation, peaks, OLED), Firefox mobile, IR code overhauled and mute/power added|
-[Kasperaitis](https://github.com/kasperaitis) - `lt_LT` translation, initiating locales, battery support and widget, and a bunch of work for ES3C28P (including ES8311 decoder, FT6336 touchscreen)
361
364
-[e2002](https://github.com/e2002) - for [ёRadio](https://github.com/e2002/yoradio/) without which ehRadio would not be possible
0 commit comments