An experimental lo-fi sound looper toy. The M5Stack Core2 is the looper (touchscreen, mixer, speaker, effects) and an M5Stack StickS3 is a wireless microphone. They talk over the air via ESP-NOW, so you can record into the Core2 from the stick while it keeps playing — a hands-on, slightly glitchy little groovebox, not a polished product.
Record short loops, stack them across four tracks, mangle them with lo-fi effects, and merge them down — all from the Core2 touchscreen. The StickS3 is held like a little handheld mic and triggers recording with its button.
This is a hobby / experimentation project: expect rough edges, lo-fi audio, and the charm that comes with both.
| Device | Role | Key parts |
|---|---|---|
| M5Stack Core2 | Loop deck: UI, mixer, speaker, effects | ESP32, 8 MB PSRAM, touch LCD, NS4168 amp, PDM mic |
| M5Stack StickS3 (optional) | Wireless microphone / record trigger | ESP32-S3 (N8R8), ES8311 codec, MEMS mic, 1.14" LCD, KEY1/KEY2 |
On the Core2 the internal PDM microphone and the speaker amplifier share I2S pins, so the device cannot record and play at the same time. The deck therefore works in two phases when using the internal mic (record, then loop).
The StickS3 has its own ES8311 codec on separate pins. Used as a wireless mic over ESP-NOW, it lets the Core2 keep playing loops through the speaker while recording new audio → true overdub.
Acoustic note: with the Core2 speaker on, the StickS3 mic also picks up the playing loop. For clean overdubs, lower the volume, hold the stick close to the source, or add headphones/line-out later. For lo-fi fun it's part of the charm.
- 4 loop tracks, 16 kHz / mono / 16-bit PCM, up to 10 s each, buffered in PSRAM.
- Per-track effects:
DRY,REVERSE,CRUSH(bitcrusher),LOFI(downsample),DRIVE(distortion),ECHO. - Loop matching (applied automatically when a take finishes):
- Onset trim — drops leading silence so the loop starts on the first hit.
- Master loop length — the first take sets the tempo grid; later takes snap to integer multiples so they never drift.
- Auto-align — followers are nudged to the master groove via cross-correlation.
- Seam fade — a tiny fade at both ends removes the loop-point click.
- Visual metronome — while recording a follower the tile border flashes on the beat grid (downbeat cyan, off-beats amber).
- Merge — fold one track into another (baking its effect) and free the source.
- Wireless overdub via StickS3 (ESP-NOW, IMA-ADPCM, 20 ms frames).
- Settings screen with live volume control; battery gauge and RF link indicator in the header.
Hardware dots A / B / C and the on-screen buttons do the same thing:
| Button | Deck | Settings |
|---|---|---|
| A | REC / STOP (internal mic, two-phase) |
volume − |
| B | PLAY / STOP (loop all tracks) |
back |
| C | FX> (cycle effect of selected track) |
volume + |
Touchscreen:
- Tap a track tile — select it. Tap again — mute / un-mute.
- Hold a track tile — clear that track.
MIXbutton (top-right of a tile) — arm that track as the merge target, then tap another tile to fold it in (the source is cleared). TapMIXagain to cancel.- Top-right corner / gear — open/close Settings.
- Hold KEY1 — record: streams the mic to the Core2 over ESP-NOW (overdub; the Core2 keeps playing). Release to finish.
- Display shows mic level, link status, mirrored deck state and battery.
Requires PlatformIO. Two environments live in one project (see platformio.ini):
# Core2 (default environment)
pio run -e m5stack-core2
pio run -e m5stack-core2 -t upload --upload-port <CORE2_PORT>
# StickS3 wireless mic
pio run -e sticks3
pio run -e sticks3 -t upload --upload-port <STICK_PORT>- The Core2 enumerates as a CH9102 USB-serial bridge; the StickS3 uses native
ESP32-S3 USB. Pick the matching
--upload-portfor each. - There is no dedicated PlatformIO board for the StickS3 yet, so it builds on a
generic
esp32-s3-devkitc-1with octal PSRAM flags; M5Unified detects the real hardware at runtime.
StickS3 (ESP32-S3, ES8311) Core2 (ESP32, NS4168)
M5.Mic 16 kHz mono ESP-NOW recv (Wi-Fi task)
KEY1 held -> 20 ms frame -> ADPCM decode
IMA-ADPCM encode (320 -> 160 B) -> PSRAM ring buffer (SPSC)
ESP-NOW broadcast (ch. 1) ----RF----> loop(): drain into track
status frame <----------------------- speaker keeps playing the mix
Source layout:
| Path | Purpose |
|---|---|
| include/Config.h | Audio format, effect + loop-matching tuning |
| include/AudioLink.h | Shared ESP-NOW protocol + IMA-ADPCM codec |
| include/AudioEngine.h / src/AudioEngine.cpp | Recording, mixing, effects, loop matching, merge |
| include/NetAudio.h / src/NetAudio.cpp | Core2 ESP-NOW receiver + ring buffer |
| include/Ui.h / src/Ui.cpp | Core2 touchscreen UI |
| src/main.cpp | Core2 wiring / state machine |
| src/stick/main_stick.cpp | StickS3 wireless-mic firmware |
The Core2 build excludes src/stick/ and the StickS3 build compiles only it,
via build_src_filter. Shared code is header-only in include/.
Working: 4-track looping, effects, loop matching, merge, internal-mic recording, wireless overdub from the StickS3.
Possible next steps: IMU (BMI270) motion → effect parameters on the stick, SD card save/load of loops, headphone/line-out for clean overdub, pitch/speed.
MIT © Marcel Dütscher