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,
}
FeatureCYDOLEDNodeMCU
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.html and 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.

StockFrozen
Panel mode, fresh boot4,784 B24,704 B
After ~5 minutes2,512 B24,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