IDA Embedded Workshop

AI-assisted ESP-IDF + LVGL firmware for the JC3636K518C round knob display

View the Project on GitHub dalager/ida-embedded-workshop

Exercise ladder

Ten rungs. Each one is small enough to finish with your AI in 10–15 minutes and leaves the firmware building, flashable and host-tested. Climb in order; rung 6 and rung 10 are where the guardrails earn their keep.

Template layout (what the scaffold gives you):

components/board/   pins and capabilities — the ONLY place a GPIO number may appear
components/ui/      display + touch + LVGL bring-up, knob poll + pure decoder, backlight
components/theme/   design tokens and the colour = state policy (no LVGL dependency)
components/haptic/  DRV2605L click
components/net/     Wi-Fi join, HTTP GET, streamed HTTP PUT through one 16 KB PSRAM chunk
components/app/     the state machine: knob/touch events in, theme/haptic/net calls out
main/main.c         boot order: NVS → ui → haptic → net → app
test/host/          gcc + Unity suites for the pure modules; scripts/test.sh runs them
scripts/            build.sh (Docker), flash.sh (host esptool --no-stub), test.sh, check-board-pins.sh

API you will touch: knob_init(cb), haptic_click(), theme_state_color(state), theme_state_motion_ms(state), net_init(), net_is_connected(), net_rssi(), net_http_get(url, buf, cap), net_http_put_stream(url, total_len, rewind_cb, read_cb, ctx, progress_cb).

Receiver for rungs 9–10: python3 templates/receiver/receiver.py --port 8080 on your laptop (same Wi-Fi as the knob). It prints bytes received, throughput, and which byte ranges it holds.


UI half

The template already runs a title, a knob counter, a tap toggle, a state ring, a haptic click and an RSSI line. Every rung adapts that working app; nothing is built from nothing. Widgets live in components/app/app_screen.c, behaviour in components/app/app.c.

Rung 1 — Hello screen

Rung 2 — Knob counter

Rung 3 — Touch target

Rung 4 — Colour = state

Rung 5 — Haptic cue

Rung 6 — Host test for your pure logic


Network half

Rung 7 — Join Wi-Fi, show RSSI

Rung 8 — GET a JSON value and render it

Rung 9 — Stream ≥ 2 MB with a progress arc

Rung 10 — Survive a Wi-Fi drop mid-transfer


Prompt card

Good prompts (specific, name the file, state the constraint):

  1. “Read components/board/include/board_jc3636k518c.h first; use only those macros for pins.”
  2. “Keep every new file under 300 lines and put pure logic in a file with no ESP-IDF includes.”
  3. “Use the LVGL 9 API that esp_lvgl_port 2.x exposes; do not use LVGL 8 style functions.”
  4. “Run scripts/build.sh and scripts/test.sh and show me the output before you say it works.”
  5. “Allocate anything over 4 KB with heap_caps_malloc(.., MALLOC_CAP_SPIRAM) and explain why.”
  6. “Do the network call on the net task and deliver the result to the UI through a callback.”

Bad prompts (and why they bite):

  1. “Make the knob work.” — The AI picks a quadrature driver. This knob is not one.
  2. “Add Wi-Fi and upload the file.” — Invites a whole-file buffer in internal RAM and a crash.
  3. “Use the pins from the Waveshare example.” — There are several boards with that name; the AI will mix pin maps. The board header is the only source.
  4. “Just fix the warnings.” — Strapping warnings on GPIO45/46 are expected; “fixing” them breaks the mic and sometimes the boot.