AtomS3R v1 controller upgrade¶
Use atoms3r-normal for the AtomS3R with onboard BMI270 and newer ST7735 panel.
s3-normal retains the AtomS3 Lite/external-IMU configuration and its existing
private SDK config. Nothing is migrated by erasing NVS or replacing that config.
idf.py --preset atoms3r-normal menuconfig
idf.py --preset atoms3r-normal build
idf.py --preset atoms3r-recovery build
Hardware resources¶
The official AtomS3R pin map and v0.4.1 schematic identify these resources:
| Resource | Assignment |
|---|---|
| Left / right ear DIN | GPIO2 / GPIO1, Grove yellow / white |
| Internal I²C SDA / SCL | GPIO45 / GPIO0, external 2.2 kΩ pull-ups |
| BMI270 | 0x68; INT1 on GPIO16, sampled by polling |
| BMM150 | Behind BMI270 auxiliary I²C, unused by this firmware |
| User button | GPIO41, active low, external pull-up |
| LCD | 128 × 128; ST7735 on newer boards |
| LCD MOSI / clock / CS | GPIO21 / GPIO15 / GPIO14 |
| LCD DC / reset | GPIO42 / GPIO48 |
| Backlight/RGB driver | LP5562, internal I²C address 0x30; W drives backlight |
| Flash / PSRAM | 8 MiB / 8 MiB octal; GPIO33–37 unavailable |
| Other reserved resource | IR transmitter on GPIO47 |
The JW5712 is a fixed 5 V-to-3.3 V buck regulator. Its enable is wired to input power; no software PMIC initialisation is required. A PMS150G supervises reset/ download behaviour. LP5562 is an LED driver, not a battery gauge or charger. Neither the fixed supply nor USB-C resistors establish an ear current allowance. Keep external regulated 5 V ear distribution and common ground as in wiring.
Firmware defaults¶
The new named preset selects BMI270 at 0x68, 400 kHz on the internal bus. It uses
the existing Bosch SensorAPI initialisation/config upload, ±4 g acceleration,
±1000 degrees/s gyro and 100 Hz sampling, feeding the existing host motion filter.
Accelerometer/gyroscope filter performance and gyro noise performance are enabled.
Advanced power save is disabled for continuous polling, avoiding the vendor's
450 µs busy wait on every read. Auxiliary magnetometer, temperature and motion/step
features are not enabled. See the profiling record.
Both ears default to 18 pixels: 6 short-edge pixels then 12 long-edge pixels, unreversed, GRB. The Grove cable positions match the previous Lite setup. Normal brightness remains 20%, gamma 2.2, with a 250 mA combined-ear estimated limit. BMI270 sampling is pinned to core 0. The ears stay black for a two-second startup settling interval, then use the shared S3 packed dual SPI2 DMA transport initialised on core 1, with nine-phase 450 Hz scheduling and double-buffered DMA/CPU overlap. Radio/network workers, TCP/IP, HTTP, normal OTA and the FreeRTOS software timer service are pinned to core 0. The separate 50 Hz animation/composition worker runs on core 1 at priority 4; output/control runs at priority 6 and never waits for that worker. Adaptive timing reduces rate in 50 Hz steps after sustained overruns and retries one step after ten clean seconds. See the worker timing record for measurements. The single-button gestures and flashlight overlay retain their v1 semantics.
There is no onboard WS2812. In particular, GPIO35 must not inherit the Lite's onboard-LED configuration. Board validation reserves the display/IMU resources even when the status screen is disabled. PSRAM uses capability allocation only; application state and task stacks remain internal, while cached instructions and read-only data remain in flash. PSRAM XiP and flash auto-suspend are disabled. Missing PSRAM permits lighting startup but disables the optional screen allocation.
Validated NVS settings override first-boot defaults. Changing sensor/bus identity invalidates incompatible calibration while retaining other settings. Recalibrate neutral/right after mounting; a successful BMI270 read does not establish ear axes.
Status screen¶
ESPURR_STATUS_DISPLAY is enabled by default for this board, excluded from
recovery, and can be disabled through menuconfig. The screen shows effect name,
BLE/Wi-Fi mode, on/off/flashlight/update state, the applicable normal or flashlight
brightness, configured current limit and current IMU readiness. It also shows the
last measured LED update rate/dither state and per-core CPU usage. CPU samples
update every five seconds from idle-task runtime counters; the 2 Hz display task
only copies cached values. Unavailable samples show --. Sampling does not require
verbose serial task reports. BLE mode does not
claim an active phone connection. Effect names come from the catalogue generator;
no duplicated effect-ID list or application wire change is introduced.
One low-priority core-0 task copies the short published runtime/IMU snapshots and refreshes at most twice per second. It does no rendering inside the ear task and makes no settings writes. Its framebuffer is 32 KiB in PSRAM, with 2 KiB internal DMA staging and a 4 KiB internal task stack. Transfers drain before staging reuse. A transfer error/timeout turns the backlight off and stops refresh; potentially live DMA resources are retained until reboot. Initialisation failures clean up unstarted resources and never reject lighting or OTA.
Startup holds the LCD in reset and clears LP5562 RGB/W outputs before starting
clients. The screen enables a modest fixed backlight only after its first frame.
SPI and panel control use IDF esp_lcd plus the managed
Waveshare ST7735 component 2.0.0.
Its licence declaration/provenance and upstream
repository licence are retained with firmware notices.
The base offset (2,1), 180-degree orientation and inversion follow
M5GFX's AtomS3R configuration.
M5GFX transforms that offset for rotation; IDF's panel driver takes a literal gap.
For the mirrored 128 × 128 window in 132 × 132 RAM, use gap (2,3):
132 − 128 − 1 = 3 for Y. Using the unrotated Y gap leaves two visible rows unwritten.
No Arduino runtime, LVGL or periodic backlight PWM task is added.
M5 documents a GC9107-to-ST7735 panel change on 2026-05-14. This profile does not autodetect or support the older panel. Wrong-panel, orientation, colour order, backlight behaviour and legibility still require inspection on the actual device.
Recovery and qualification¶
atoms3r-recovery retains the existing 8 MiB partition table and module-only
recovery behaviour, with GPIO41 native five-second boot selection. Its PSRAM,
sensor buses and status screen are disabled. Use the matching preset with
tools/prepare_recovery_boot.py when preparing a recovery-enabled bootloader;
app-only OTA cannot install a bootloader or partition table.
This board uses the legacy espurr app family. The new build is not automatically
added to the public installer/release selector. Choose the matching hardware binary;
the common S3 chip/family check does not distinguish every legacy pin configuration.
See the dated bring-up record for build, host, matrix and framebuffer evidence. Physical IMU direction/calibration, LCD, ear output, current, buttons, BLE/OTA and recovery remain hardware checks.