When the Board Blinks Wrong: Two Ways to Point an LLM at Arduino, ESP32 and IoT — Spec-First Sketching vs Log-Driven Autopsy

Most people point an LLM at a microcontroller the same way they point it at a blog post: "write me a sketch that reads a DHT22 and posts to MQTT." It works — for about ten minutes. Then the servo jitters, the I2C bus hangs after the third sensor, and the model cheerfully rewrites the whole file instead of telling you that your pull-up resistors are missing.

The real fork in the road is not which model you use. It is whether you already have a board in front of you and what exactly is broken. If the project lives only in your head, you go spec-first: prompts that force the LLM to produce a pinout, a state machine and a sketch in that order. If the device already misbehaves, you go log-first: prompts that turn Serial output, bus scans and symptoms into a diagnosis instead of a rewrite.

Below are twelve prompts split across those two modes, with the failure modes each one is designed to catch. The same discipline that keeps CI pipelines honest (see the CI/CD Without Surprises) applies here: give the model constraints and evidence, not vibes.

Mode A — Spec-first: from a task to a sketch that survives contact with hardware

1. The pinout contract before a single line of code

Task: stop the model from inventing pins that collide with your board.

Prompt:

You are an embedded engineer. Board: ESP32-WROOM-32 DevKit.
Task: read a DHT22 (temp/humidity) and drive one 5V relay.
Before any code, produce a table: signal, GPIO, reason for the choice,
and every ESP32 pin this signal MUST NOT use (strapping pins, input-only
34-39, pins used by flash). Flag any conflict with UART0.
Only after I approve the table, write the sketch.

Why it matters: the model's default is to pick GPIO2 or GPIO15 because they look nice in examples — both are strapping pins that change boot behavior. Forcing a table first makes the collision visible before it becomes a boot loop.

2. State machine instead of delay()

Task: convert a blocking tutorial sketch into a non-blocking one.

Prompt:

Refactor this sketch to a non-blocking state machine using millis().
List states, transitions and timeouts as a table first.
No delay() calls in the result. Show the loop() body.

Example result: states IDLE → READ_SENSOR → PUBLISH → BACKOFF, with a 2s sensor timeout and a 30s backoff on MQTT failure. The loop() becomes a switch over state plus one if (now - lastRead > INTERVAL).

Where people go wrong Asking for a fix and a rewrite in one prompt. The model silently changes pin assignments and library versions while "fixing" timing, and you lose the ability to tell which change broke the build.

3. Protocol-specific prompt for I2C/SPI/UART setup

Task: get the bus parameters right the first time.

Prompt:

For an SSD1306 over I2C on ESP32: give the Wire.begin() call with
SDA/SCL pins, the address, the clock speed, and explain what happens
if two devices share address 0x3C. Then show a bus scan sketch that
prints every responding address.

Example result: Wire.begin(21, 22); Wire.setClock(400000); plus a scan loop from 1 to 126 printing hex addresses. The explanation of the address collision is the part most tutorials skip — and it is exactly why the second OLED never lights up.

4. Power and level-shifting audit

Task: catch the 3.3V/5V mismatch before the magic smoke.

Prompt:

My ESP32 (3.3V logic) drives this 5V relay module and reads a 5V sensor.
List every wire, state whether a level shifter or divider is required,
and give the resistor values if a divider is needed. Show the math.

Example result: a divider for a 5V signal into a 3.3V input: 10k and 20k give 5 × 20/(10+20) ≈ 3.33V, which is safe. The model shows the arithmetic, so you can check it instead of trusting it.

5. Library and version pinning

Task: avoid the "works on my machine" of the Arduino world.

Prompt:

For this sketch, list every library with the exact version you assume,
and the board core version (ESP32 core). Where the API differs between
major versions (e.g. WiFi vs WiFiClient changes), note it explicitly.

Why it matters: a sketch that compiles against one core version fails on another with errors that look unrelated to the change. Pinning versions in platformio.ini or the Library Manager is the embedded equivalent of a lockfile.

Mode B — Log-first: when the board is already misbehaving

6. The Serial log autopsy

Task: turn a wall of Serial output into a ranked hypothesis list.

Prompt:

Here is the full Serial output from boot to hang, plus the sketch.
Do not rewrite the code. Produce: (1) the last healthy line,
(2) three ranked hypotheses with the evidence line for each,
(3) one test per hypothesis that costs under a minute.

Example result: last healthy line is I2C scan: 0x3C found; hypotheses are a watchdog reset, a blocking while(!client.connected()) loop, and a brownout on the relay coil. Each gets a one-minute test — comment out the relay, add esp_task_wdt logging, replace the while with a timeout.

What to check first Paste raw logs, not your summary of them. A summary drops the timestamps and the repeated lines that reveal a reset loop — and those are usually the whole diagnosis.

7. The reset-loop detector

Task: distinguish a crash from a reboot from a hang.

Prompt:

This log shows the boot banner repeating. Identify the reset reason
from the ESP32 reset codes in the log, and tell me which of these it is:
panic, watchdog, brownout, or software restart. Cite the exact line.

Example result: rst:0xc (SW_CPU_RESET) plus a Guru Meditation Error line points to a panic in application code, not power. The distinction matters: chasing brownout when it is a null pointer wastes an evening.

8. Bus-level diagnosis for I2C/SPI/UART

Task: find out why a device does not answer.

Prompt:

My I2C scanner finds nothing. Walk me through, in order:
pull-ups, wiring, address, clock speed, and power.
For each, give the measurement or test that confirms or rules it out.

Example result: pull-ups first (many breakout boards omit them; the bus needs them somewhere), then continuity, then a scan at 100kHz instead of 400kHz. The order is the value — checking the address before the pull-ups is the classic wasted hour.

9. Symptom-to-protocol mapping

Task: translate a vague complaint into a bus-level question.

Prompt:

Symptom: the SPI display shows garbage after 10 minutes of running.
Map this to likely causes on SPI (clock too high, long wires, missing CS
timing, power droop). Give one change per cause and the expected effect.

Example result: drop SPI clock from 40MHz to 20MHz and shorten the jumper wires; if the garbage interval doubles, it is signal integrity, not the library.

Key distinction In log-first mode the model is a diagnostician, not an author. Every prompt should forbid a rewrite until the hypothesis is confirmed by a test.

10. Protocol decoder prompt for UART

Task: read raw bytes instead of guessing.

Prompt:

Here are the raw bytes my UART receives in hex. The device datasheet
says frames start with 0xAA and end with a checksum. Decode the frames,
verify the checksum, and tell me where framing breaks.

Example result: the model splits frames at 0xAA, computes the checksum, and reports that frame 4 fails — which points to a baud-rate mismatch or a missed byte, not a protocol error.

The trade-off nobody mentions

Spec-first gives you clean, reviewable code but costs you the evidence — the model designs against your description, and if the description is wrong the sketch is confidently wrong. Log-first gives you grounded diagnosis but costs you the overview — you fix symptoms one at a time and can miss that the architecture caused them.

The non-obvious move: run both, in this order. Write the spec prompt first to get a pinout and state machine, then paste the real logs back and ask the model to compare its design against the evidence. The gap between what it designed and what the board reports is usually the bug. The same detective discipline that works on web DevTools traces — see the LLM detective prompt set — applies to a Serial monitor.

What to keep in your prompt kit

Mode Prompt Catches
Spec Pinout contract Strapping-pin collisions
Spec State machine refactor Blocking delay() bugs
Spec Bus setup Address and clock errors
Spec Power audit 3.3V/5V mismatch
Log Serial autopsy Wrong root cause
Log Reset detector Crash vs power
Log Bus diagnosis Missing pull-ups
Log UART decoder Framing errors

Start with one board, one sensor, one bus. Write the pinout contract, flash the sketch, then break it on purpose — pull a jumper, drop the clock — and run the autopsy prompt on the resulting log. That loop teaches more about embedded debugging than any tutorial, and it is exactly what an LLM is good at when you stop asking it to write and start asking it to reason.

← All posts

Ask AI about this article

Ask a question about this article — ASI Biont will answer based on its text.