CYD Studio › Docs › Boards and firmware
Boards and firmware
The system is the same system on every board. Differences are data, not code: each board's boards/<name>/cyd.py declares an OZELLIKLER (features) dict, runtime.ozellikler() merges it with the measured core capabilities (threads, max_jobs, platform), GET /api/caps publishes it and the panel hides unsupported buttons.
OZELLIKLER = {
'display': True, 'touch': True, 'sd': True, 'rgb_led': True,
'light': True, 'speaker': True, 'ble': True, 'tls': True,
'wifi_config': True, 'docs': True, 'lite_ui': False,
}
| Feature | CYD | OLED | NodeMCU |
|---|---|---|---|
| display / touch / sd | ✅ ✅ ✅ | ✅ ❌ ❌ | ❌ ❌ ❌ |
| rgb_led / light / speaker | ✅ ✅ ✅ | ❌ ❌ ❌ | ✅ ✅ ❌ |
| ble / tls (in solo mode) | ✅ ✅ | ✅ ✅ | ❌ ❌ |
| wifi_config / docs | ✅ ✅ | ✅ ✅ | ❌ ❌ |
| lite_ui | ❌ | ❌ | ✅ |
| threads / max_jobs | ✅ 8 (threads 4) | ✅ 4 | ❌ 3 |
CYD (ESP32-2432S028R)
The "Cheap Yellow Display": 320×240 ILI9341 display, XPT2046 touch, SD slot, RGB LED, LDR, speaker, CH340 USB-serial. Custom firmware (micropython-esp32-cydstudio.bin, a CYD_STUDIO board based on v1.28.0):
- 12 core modules frozen into flash:
runtime jobs sysinfo logstore settings netmgr store server+cyd ili9341 xpt2046 sdcard.boot.py,main.py,index.htmland the docs stay on the filesystem. - Fixed heap of 96 KB, auto-growth off.
- RAM diet (
sdkconfig.free_ram): TCP window 5760 → 3072, WiFi buffers 10/32/32 → 8/12/12, sockets 16 → 6. - Result: 55 KB of internal DRAM idle (27.8 on stock), 7 normal + 2–3 thread projects.
The panel is natively landscape and RGB-ordered: the verified setting is rotation=0, bgr=False, 320×240. Display MISO is not wired, so nothing can be read back; orientation is set by eye (Settings → Display, live). SD only on VSPI (slot 3); slot 2 is the display's bus.
Rebuild (WSL, ESP-IDF v5.5.1): python tools/frozen_esp32_build.py [--heap 96], ~3 minutes. Flash: tools/flash.py COM3 firmware/micropython-esp32-cydstudio.bin --koru (BOOT-held procedure). Deploy: tools/deploy.py --board cyd --frozen --samples --reset.
NodeMCU (ESP8266)
On-board LED, one analog input (cyd.A0), 38 KB heap, no _thread. Custom firmware (micropython-esp8266-frozen.bin): core + lite server (boards/nodemcu/server.py) + ssd1306 frozen, asyncio included.
| Stock | Frozen | |
|---|---|---|
| Panel mode, fresh boot | 4,784 B | 24,704 B |
| After ~5 minutes | 2,512 B | 24,672 B (no fragmentation) |
On stock even a 3 KB project hit MemoryError at the compile() line; freezing keeps the bytecode in flash. Pin labels cyd.D0…cyd.D8: D1/D2 (GPIO5/4) are easy I/O and the standard I2C pair, D4 (GPIO2) is the on-board blue LED (inverted), D3/D8/D0 have boot duties, avoid them.
Lite panel (boards/nodemcu/index.html, ~31 KB): search, ▶/■/✕ per row, Console | Saved log, Settings, Docs, TR/EN, shortcuts. Build: tools/frozen_esp8266_build.py; flash tools/flash_esp8266.py COM3 --koru (no button); deploy tools/deploy_mpy.py --board nodemcu --frozen --samples --reset.
ESP32 OLED
128×64 SSD1306 OLED, no touch or SD, CP2102 USB-serial. Stock MicroPython v1.28; the core goes to the filesystem (deploy.py --board oled --samples). The Ekran class offers the same API as the ILI9341 with colours collapsing to on/off; CYD projects run unchanged. I2C pins vary by model, _oled_bul() tries the candidate pairs (5,4) (4,15) (21,22) (16,17) (13,14) in turn. cyd.touch(), cyd.light(), cyd.beep() raise a clear error.
Warning: powered from a PC USB port this board's 3.3 V rail collapses the moment WiFi starts (brownout loop). It cannot be fixed in software; bare MicroPython dies on the same wlan.active(True) line. Fix: a LiPo on the battery connector, a powered hub, or an external fixed 3.3 V supply. On external 3.3 V it ran fine at 240 MHz and full TX power.
Adding a board
Put a cyd.py (an OZELLIKLER dict + re-exports of should_stop, sleep, rota, dram, wifi, buton from runtime) and a config.example.json under boards/<name>/; drivers if needed. The core does not need to change. When adding a feature: add the key to runtime._VARSAYILAN, enable it in the relevant boards' OZELLIKLER, check caps.<key> in the panel.
Shadowing
sys.path = ['', '.frozen', '/lib', '/projects']: a same-named .py on the filesystem overrides the frozen one. mpremote fs cp x.py :x.py is fine for a quick single-file experiment, but every shadow costs ~10 KB of heap and measurements are misleading in that state. A core change means a firmware rebuild; clean up with --frozen afterwards.
Last updated 2026-10-17 · This page as Markdown