CYD Studio › Docs › Run modes and memory

Run modes and memory

Every project row has an N / T / S chip. Click to cycle; the choice is saved per file (config.json modlar). Default N. The selected file's bar: Save · N|T|S · Run/Stop (one button) · Delete; it turns into Stop while running and "Start solo" when S is selected.

The three modes

ModeHow it runsHeapInternal DRAMFor
N Normalasyncio task on one shared thread2–15 KB0 (shared worker 17 KB once)LEDs, sensors, small pages: most projects
T ThreadIts own FreeRTOS thread, sliced by the GIL (ESP32 only)same10–15 KBDisplay drawing, tight loops without sleep, heavy compute
S SoloPanel off, board reboots, single projectallallBLE, HTTPS

How normal mode works: the project file is wrapped line by line into an async def, top-level names are declared global, cyd.sleep( → await, time.sleep( → await. The result is streamed to a temp file and compiled from the file with execfile; a 12 KB source never sits in the heap as a string. If a sleep call is inside a def body the transform does not apply (MicroPython silently turns a plain def containing await into a generator and the body never runs); a pre-scan catches that and the project opens as T by itself, the log says why. If you write async def yourself it is left alone and the main() coroutine is run.

Solo is one-shot: main.py deletes the flag first thing; pressing RESET brings the panel back. Panel: select the file → S → Run → WiFi on/off question. API: POST /api/solo?name=<file>&wifi=0|1.

Two memories

The ESP32 has two separate pockets, and the second one is the critical one:

MicroPython heapProject data, gc.mem_free()
Internal DRAMWiFi, sockets, thread stacks, BLE, TLS. cyd.dram()

gc.mem_free() can look generous while a thread cannot be created or BLE/TLS returns ENOMEM. Measure with the panel's system box or cyd.dram().

CYDStock v1.28.0CYD Studio firmware
Internal DRAM free, panel up27.8 KB55 KB
Python heap free80 / 137 (auto-grows, swallows DRAM)75 / 94 (fixed 96 KB)
Concurrent projects1 (2nd: can't create thread)7 normal + 2–3 threads

What the firmware changed: 12 core modules frozen into flash; the heap is fixed (auto-doubling dropped DRAM from 76 to 12 KB with two small projects); smaller lwIP/WiFi buffers; live log ring 250 → 100 lines (250 lines held 20.7 KB and fragmented the heap); server socket timeout 5 seconds.

Thread stacks come out of internal DRAM: 10–15 KB per project. A project drawing on the display takes 25–33 KB of heap (returned on stop). No leak: same project started/stopped 4× → 65.7 / 65.7 / 65.9 / 65.8 KB.

Measured: projects side by side (CYD)

Started in orderFrom heapFrom DRAMRunning
yuk_kontrol (12 KB source + /yuk routes)12.2 KB17.3 KB (shared worker, once)1
led_yakip_sondur2.202
gece_lambasi8.20.33
ornek_led4.704
isik_olcum7.705
harici_led7.107
ornek_isik_sensoru (draws on the display)33 KB → heap 1 KB08

Seven small projects side by side with internal RAM flat at 37 KB; the limit is now the heap. If you need a bigger heap: frozen_esp32_build.py --heap 112 (DRAM 55 → 39 KB, still room for 2 threads).

Truly side by side?

A "button poller" project looping every 10 ms; longest gap between turns over 15 seconds:

Scenarioaveragemaxgaps >100 ms
Alone, normal12.9 ms21 ms0
+ 4 normal projects (LED/LDR)15.93525
+ a display-drawing project (normal)25.597333
+ a thread doing 90% CPU math21134055
Same load, poller in T12.0370

Reading: next to LED/sensor neighbours normal mode really alternates. A display-drawing project is a bad neighbour in normal mode (an SPI fill holds the single thread for ~1 s): make it T. A thread that never yields the CPU starves the normal worker: put cyd.sleep(0.001) in the heavy loop or make the input project T. For buttons the real answer is cyd.buton (interrupt), not polling.

Stopping and the watchdog

Threads do not isolate: MicroPython has a GIL, one thread runs bytecode at a time. A project that never yields the CPU starves the panel. The protection:

  1. The server writes a heartbeat every turn; the listening socket has a timeout so accept() cannot stall it.
  2. When the first job starts a watchdog thread checks the heartbeat every 2 seconds.
  3. If the panel misses heartbeats for 20 seconds, running projects get the stop flag.
  4. If a project does not stop within 4 seconds, machine.reset(). The panel always comes back; verified: a choking project was stopped at second 28 and the panel returned to 302 ms.

On the ESP8266

There is no _thread. N = a task in the server's asyncio loop (the T chip is hidden), S is available. Measured: 3 normal projects side by side, heap 19 → 9 KB (2.5–5 KB each), panel max 0.69 s. Practical limit 3 projects. For a project that never calls cyd.sleep() a timer pre-emptor checks every 100 ms and, if the pump has not run for 400 ms, slips a server turn between two bytecodes; with while True: x += 1 running, 6/6 status queries were answered. If a stop request is ignored for 4.5 seconds the log gets !!! yanit vermiyor and the board resets (the panel is back in ~10 s).

Last updated 2026-10-17 · This page as Markdown