Skip to content

Commit 9c5e214

Browse files
committed
2026.07.11
1 parent bfde72d commit 9c5e214

10 files changed

Lines changed: 51 additions & 41 deletions

File tree

Hardware.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -305,26 +305,26 @@ for additional noise filtering.
305305

306306
| Part | Qty | Purpose | Alternatives |
307307
| ------------------------------ | --- | ---------------------------------------------------- | ------------ |
308-
| ESP32-S3-DevKitC-1 N16R8 | 1 | Main microcontroller with PSRAM | Any ESP32-S3 with 2MB+ PSRAM |
308+
| ESP32-S3-DevKitC-1 N16R8 | 1 | Main microcontroller with 16MB flash and 8MB PSRAM | Any ESP32-S3 with minimum 8MB flash and 2MB PSRAM |
309309
| RS-15-5 (MEAN WELL) | 1 | 5V 15W AC-DC power supply | USB-C PD trigger board (5V), any regulated 5V 3A+ supply |
310-
| F0505S-3WR2 (MORNSUN) | 1 | Isolated 5V→5V DC-DC converter for digital isolation | B0505S-1WR2 (1W, lighter loads), CRE1S0505SC |
311-
| PLY17BN9612R0B2B | 5 | KEMET PLY17 CM choke; EMI filter, one per power domain | ? |
310+
| F0505S-3WR2 (MORNSUN) | 1 | Isolated 5V→5V DC-DC (3W, 600mA); ultra-low 20pF isolation capacitance for clean analog/digital separation | Requires ≥3W for ESP32-S3; cheaper converters lack both power and low isolation capacitance |
311+
| PLY17BN9612R0B2B | 5 | Murata hybrid CM+DM choke (0.96mH CM, 47μH DM); filters common-mode and differential noise | Standard CM choke ≥2A, 0.5–2mH (e.g., Bourns 8111-RC); or separate CM choke + DM inductor |
312312
| LTK5128 | 2 | Class AB 3W audio amplifier (Left + Right) | PAM8406 (Class D, 5W), PAM8403 (3W), MAX98357A (I2S, 3W) |
313-
| LD06AJSA | 1 | LED constant-current driver / power indicator | 220Ω resistor + LED (simpler), any 20mA LED driver |
313+
| LD06AJSA | 1 | LED constant-current driver; supports LED filaments for encoder illumination | 220Ω resistor + LED (simpler), any 20mA LED driver |
314314
| RCH664NP-100M | 1 | 100μH shielded power inductor for DC-DC filtering | Any 100μH 1A+ shielded inductor |
315315
| EI14 600:600Ω | 1 | 1:1 audio isolation transformer (line-level) | Any 600:600Ω audio transformer, 10μF DC blocking caps |
316316
| EC11 | 1 | Rotary encoder (15 pulse/30 detent) with push switch | KY-040, PEC11, any quadrature encoder with switch |
317317
| VS1838B | 1 | 38kHz IR receiver | TSOP38238, TSOP4838, TSOP31238 |
318318
| SD Card module | 1 | SPI microSD card reader for offline playback | Built-in display SD slot (check for proper resistors!) |
319-
| XRR6H-6*10-3T | 5+ | 6-pin 2.54mm locking header connectors | Standard 2.54mm Dupont headers, JST XH2.54 6-pin |
319+
| XRR6H-6*10-3T | 7 | 6-hole ferrite bead (6×10mm, 3-turn); EMI suppression on signal/power lines | Any 6-hole ferrite bead (6×10mm), clip-on ferrite choke, toroidal ferrite core |
320320

321321
#### Capacitors & Resistors
322322

323323
| Component | PCM5102 | VS1053 | Where Used | Notes |
324324
| ------------------------- | :-----: | :----: | ----------------------------------- | ----- |
325-
| 2200 μF 25V electrolytic | 1 | 1 | Audio rail bulk decoupling | Low ESR recommended |
326-
| 1000 μF 16V electrolytic | 2 | 2 | Amp L+R power, PSU input | Low ESR recommended |
327-
| 470 μF 16V electrolytic | 3 | 3 | Digital rail, Display, LED power | |
325+
| 2200 μF 25V electrolytic | 1 | 1 | Audio rail bulk decoupling | Must use Low ESR |
326+
| 1000 μF 16V electrolytic | 2 | 2 | Amp L+R power, PSU input | Must use Low ESR |
327+
| 470 μF 16V electrolytic | 3 | 3 | Digital rail, Display, LED power | Should use Low ESR |
328328
| 100 μF 16V electrolytic | 2 | 2 | LED driver, SD card | |
329329
| 47 μF 10V electrolytic | 2 | 2 | Digital rail, IR receiver | |
330330
| 10 μF 10V electrolytic | 1 | 1 | DC-DC output filtering | |
@@ -333,6 +333,9 @@ for additional noise filtering.
333333
| 33 Ω resistor (1/4W) | 6 | 6 | SPI bus damping resistors | On MOSI, SCLK, MISO, CS, DC lines |
334334
| 100 Ω resistor (1/4W) | 2 | 5 | IR receiver, VS1053 control lines | |
335335

336+
Kle7rx recommends using Low ESR for all electrolytic capacitors.
337+
It is also recommended to use metal film resistors but carbon film resistors are acceptable.
338+
336339
#### PCM5102 Build — Additional Parts
337340

338341
| Part | Qty | Purpose | Alternatives |

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ For that and other major needed changes to the codebase, there is a `code-issues
283283

284284
| Date | Release Notes |
285285
| ---------- | ---------------- |
286+
| 2026.07.11 | `not a release` minor fixes to PSRAM handling, documentation |
286287
| 2026.07.10 | SD Offline Mode added, `Hardware.md` added, Fixes to Deep Sleep, smart start, SD index, VS1053 volume curve |
287288
| 2026.07.05 | More fixes to VS1053, Wake from Deep Sleep now works on all RTC-capable pins |
288289
| 2026.07.01 | 🍁 Minor fixes: WebUI, Display, Locales, VS1053 patch, smart start (last station plays after OTA/flash upgrade) |
@@ -316,6 +317,7 @@ A full history of ёRadio from v0.4.177 to v0.9.533 can be seen in the [old Read
316317
### Credit
317318

318319
Thanks to:
320+
319321
- [kle7rx](https://github.com/kle7rx) - `ru_RU` translation, debugging, VS1053, and amplifier schematics
320322
- [Kasperaitis](https://github.com/kasperaitis) - `lt_LT` translation, initiating locales (WebUI and display language) and a bunch of work for ES3C28P (including ES8311 decoder, ILI9341 battery widget, FT6336 touchscreen)
321323
- [e2002](https://github.com/e2002) - for [ёRadio](https://github.com/e2002/yoradio/) without which ehRadio would not be possible

src/core/config.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ bool Config::_wwwFilesExist() {
7171

7272
void Config::init() {
7373
loadPreferences();
74-
if (!config.store.lastBootGood) delay(1000); // Allow serial monitor to connect before logging safe mode
74+
if (!config.store.lastBootGood) delay(1000); // Allow serial monitor to connect before logging in Safe mode
7575
bootInfo();
7676
#if RTCSUPPORTED
7777
BOOTLOG("RTC begin(SDA=%d,SCL=%d)", RTC_SDA, RTC_SCL);

src/core/options.h

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,21 @@ Use this tool to setup connections:
300300
/* If using PRETEXT_ALLCAPS, why not do the same to your WebUI? */
301301
// #define WWW_CASETRANSFORM
302302

303+
/* Other Transforms */
304+
// Frankly speaking, most of these go beyond regular display transforms.
305+
// They may hide the option in the WebUI completely.
306+
// It is not recommended to build without these options but here they are anyways.
307+
//#define HIDE_TITLE2
308+
//#define DSP_INVERT_TITLE
309+
//#define HIDE_VU
310+
//#define HIDE_VOLBAR
311+
//#define HIDE_BUFFERBAR
312+
//#define HIDE_VOL
313+
//#define HIDE_IP
314+
//#define HIDE_RSSI
315+
//#define HIDE_BATTERY
316+
//#define HIDE_WEATHER
317+
303318

304319
/* ============================== SPI BUSES AND PINS ============================== */
305320

@@ -854,30 +869,23 @@ Use this tool to setup connections:
854869
#define USE_FBUFFER true // framebuffer: best to leave this on (will use PSRAM if available and SRAM if not)
855870
#endif
856871

857-
// PSRAM Audio Buffer
858-
// WebUI files cache: (up to) 300KB + Decoder Output: 52KB + Display framebuffer (480×320): 74KB = 426KB... leaves 598KB free in 1MB or 1598 free in 2MB
859-
// To be fair, in real-world use, above 300KB may be excessive...
860-
#ifndef PSRAM_BUFSIZE
861-
#if defined(ARDUINO_ESP32_DEV)
862-
#define PSRAM_BUFSIZE 550
863-
#else
864-
#define PSRAM_BUFSIZE 1500
865-
#endif
866-
#endif
872+
// PSRAM Buffers (highest-case estimates, in allocation order: display.init() → player.init() → netserver.begin())
873+
// Display framebuffer (480×320): ~74 KB (allocated first, display.init())
874+
// VS1053 audio buffer: ~293 KB (300000 bytes, player.init())
875+
// I2S audio buffer: ~640 KB (UINT16_MAX * 10, player.init())
876+
// WebUI files cache: up to 300 KB (actually ~60KB if using .gz files, allocated last, netserver.begin(); dynamic, falls back to SPIFFS)
877+
// ---------------------------------------
878+
// VS1053 total: ~667 KB or ~427KB with .gz WebUI files
879+
// I2S total: ~1014 KB or ~774KB with .gz WebUI files
880+
// Note: WebUI cache is opportunistic so if PSRAM runs low, files stay in SPIFFS instead.
881+
867882
// Since the audio buffer can never technically fill up (most streams prevent reading-ahead excessively),
868883
// the visual buffer bar should look "full" when the buffer is near to maximum potential (250KB is ~6 seconds at 320kbps)
884+
// there is no
869885
#ifndef BUFFERBAR_VISUAL_FULL_KB
870886
#define BUFFERBAR_VISUAL_FULL_KB 250
871887
#endif
872-
#if BUFFERBAR_VISUAL_FULL_KB < 16 || BUFFERBAR_VISUAL_FULL_KB > 500
873-
#warning BUFFERBAR_VISUAL_FULL_KB is outside 16 to 500 range, using default of 250KB
874-
#undef BUFFERBAR_VISUAL_FULL_KB
875-
#define BUFFERBAR_VISUAL_FULL_KB 250
876-
#endif
877-
#if PSRAM_BUFSIZE < BUFFERBAR_VISUAL_FULL_KB
878-
#undef BUFFERBAR_VISUAL_FULL_KB
879-
#define BUFFERBAR_VISUAL_FULL_KB (PSRAM_BUFSIZE)
880-
#endif
888+
881889

882890
/* --- CPU CORES --- */
883891
/* ESP32 and ESP32-S3 have 2 cores (Main loop runs on Core 1). ESP32-C3 has 1 core (Main loop runs on Core 0) .*/

src/core/sdmanager.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ void SDManager::listSD(File &plSDfile, File &plSDindex, const char* dirname, uin
134134
}
135135
} else {
136136
if (_endsWith(strlwr((char*)fn), ".mp3") || _endsWith(fn, ".m4a") || _endsWith(fn, ".aac") ||
137-
_endsWith(fn, ".wav") || _endsWith(fn, ".flac")) {
137+
_endsWith(fn, ".wav") || _endsWith(fn, ".flac") || _endsWith(fn, ".ogg") ||
138+
_endsWith(fn, ".opus")) {
138139
pos = plSDfile.position();
139140
plSDfile.print(fn);
140141
plSDfile.print('\t');
@@ -212,7 +213,8 @@ uint32_t SDManager::_countAudioFilesRecursive(const char* dirname, uint8_t level
212213
}
213214
} else {
214215
if (_endsWith(strlwr((char*)fn), ".mp3") || _endsWith(fn, ".m4a") || _endsWith(fn, ".aac") ||
215-
_endsWith(fn, ".wav") || _endsWith(fn, ".flac")) {
216+
_endsWith(fn, ".wav") || _endsWith(fn, ".flac") || _endsWith(fn, ".ogg") ||
217+
_endsWith(fn, ".opus")) {
216218
_sdFCount++;
217219
}
218220
}

src/libraries/I2S_Audio (ehRadio Maleksm v0.9.434m(04.04.25))/Audio.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class AudioBuffer {
134134
bool havePSRAM() { return m_f_psram; };
135135

136136
protected:
137-
size_t m_buffSizePSRAM = 1024 * PSRAM_BUFSIZE;
137+
size_t m_buffSizePSRAM = UINT16_MAX * 10; // most webstreams limit (65535 * 10)
138138
size_t m_buffSizeRAM = 1600 * AUDIOBUFFER_MULTIPLIER2;
139139
size_t m_buffSize = 0;
140140
size_t m_freeSpace = 0;

src/libraries/I2S_Audio/Audio.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class AudioBuffer {
134134
bool havePSRAM() { return m_f_psram; };
135135

136136
protected:
137-
size_t m_buffSizePSRAM = 1024 * PSRAM_BUFSIZE;
137+
size_t m_buffSizePSRAM = UINT16_MAX * 10; // most webstreams limit (65535 * 10)
138138
size_t m_buffSizeRAM = 1600 * AUDIOBUFFER_MULTIPLIER2;
139139
size_t m_buffSize = 0;
140140
size_t m_freeSpace = 0;

src/libraries/VS1053_Audio (ehRadio Maleksm v0.9.434m(04.04.25))/audioVS1053Ex.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class AudioBuffer {
153153

154154
protected:
155155
// size_t m_buffSizePSRAM = 327675; // most webstreams limit the advance to 100...300Kbytes
156-
size_t m_buffSizePSRAM = 1024 * PSRAM_BUFSIZE;
156+
size_t m_buffSizePSRAM = 300000; // most webstreams limit the advance to 100...300Kbytes
157157
size_t m_buffSizeRAM = 1600 * AUDIOBUFFER_MULTIPLIER2;
158158
size_t m_buffSize = 0;
159159
size_t m_freeSpace = 0;

src/libraries/i2s-frankenstein-surgery-notes.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ void Audio::performAudioTask() {
103103
- No fallback to SRAM in v0.9.720m
104104
- `psram_unique_ptr.hpp` provides the allocation framework
105105
- `audiolib_structs.hpp` depends on it
106-
- **options.h adjustment needed**: PSRAM_BUFSIZE may need reduction to fit within 2MB (user's note)
107-
- Around `options.h` line 862: check PSRAM Audio Buffer section
108106

109107
### 5. Task / Core Assignment
110108
- `m_audioTaskCoreId = 1` (default)
@@ -160,7 +158,7 @@ Additionally, v0.9.720 I2S is a prerequisite for the v0.9.720 VS1053 library —
160158
- Add `#include "../../core/config.h"` if needed
161159
- Wire `m_audioTaskCoreId` to `AUDIO_CORE` from options.h
162160
- Add "Some adjustments by Trip5 for ehRadio" to header
163-
3. **Verify PSRAM macros**: Check `PSRAM_BUFSIZE`/`PSRAM_RES_BUFSIZE` work with `psram_unique_ptr.hpp`
161+
3. **Verify PSRAM allocation**: Ensure `psram_unique_ptr.hpp` allocates correctly
164162
4. **Build and test**: All I2S environments. Verify audio playback, VU meter, volume control.
165163
5. **If Stage 1 works**: PSRAM framework confirmed. Proceed to Stage 2.
166164
6. **If Stage 1 fails**: Problem is in PSRAM or decoder file changes. Fix before proceeding.

src/main.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,13 @@ void loop() {
152152
if (psramFound()) {
153153
size_t psramTotal = ESP.getPsramSize();
154154
size_t psramUsed = psramTotal - ESP.getFreePsram();
155-
size_t known = (1024 * PSRAM_BUFSIZE) + netserver.getFileCache().totalBytes() + psramFrameBufferBytes;
156155
size_t audioFill = player.inBufferFilled();
157-
FUNCTIONLOG("PSRAM", "Allocated: %uKB / %uKB total: Framebuffer: %uKB, WebUI Cache: %uKB, Other: %uKB, Audio: buffered %uKB / %uKB allocated",
156+
FUNCTIONLOG("PSRAM", "Allocated: %uKB / %uKB total: Framebuffer: %uKB, WebUI Cache: %uKB, Audio buffered: %uKB",
158157
psramUsed / 1024,
159158
psramTotal / 1024,
160159
psramFrameBufferBytes / 1024,
161160
netserver.getFileCache().totalBytes() / 1024,
162-
(psramUsed > known ? (psramUsed - known) / 1024 : 0),
163-
audioFill / 1024,
164-
PSRAM_BUFSIZE);
161+
audioFill / 1024);
165162
}
166163
}
167164
cmLastPrint = millis();

0 commit comments

Comments
 (0)