Robotics Simulator (Wokwi-style) is part of Practice Tools. Sign in or create an account, then subscribe for โฆ4,000/month to unlock it.
Step-by-step exercises for Robotics Simulator (Wokwi-style) - read them here, then practice live in the tool above.
What you're practicing: real embedded/Arduino-style syntax โ pinMode, digitalWrite, and delay โ the same three building blocks behind almost every real Wokwi or ESP32 blink starter project, on a virtual LED with no hardware required. This is the expanded version of this lab: more explanation, more patterns to build, and a glossary if any of the terms are new to you.
This sandbox is intentionally simple, so read this before you start: it reads your code top to bottom, one line at a time, and only recognises three exact patterns:
pinMode(LED, OUTPUT); โ sets up the pin (just logs a confirmation)digitalWrite(LED, HIGH); โ turns the virtual LED ONdigitalWrite(LED, LOW); โ turns the virtual LED OFFdelay(500); โ waits that many milliseconds before the next change happensThere are no loops or conditionals here yet โ that's Labs 2 and 3. Right now it's all about one core embedded-programming habit: sequence and timing. Every line must end with a semicolon, and the pin is always written exactly as LED (capital letters), HIGH, and LOW โ the simulator is looking for that exact spelling.
LED.OUTPUT), as opposed to reading a sensor (INPUT).HIGH means "send power" (LED on), LOW means "stop sending power" (LED off).ms stands for milliseconds โ there are 1000 in a second, so delay(1000); is a one-second wait.; โ marks the end of an instruction, the same way a period ends a sentence. Real embedded code (and JavaScript) is strict about this.digitalWrite lines it has, out loud or in your head, before running it.digitalWrite lines you counted in step 2? If not, re-read your code line by line before continuing.delay(500) to delay(1000). Write your prediction down somewhere, even just in your head โ specifically, predict the new timestamp of the very last log entry.delay(100) โ and run it. Notice how much faster the blinking reads in the log, and how much smaller the final timestamp is.pinMode(LED, OUTPUT);, then alternate digitalWrite(LED, HIGH); / digitalWrite(LED, LOW); with a delay(...) line after each one.delay(...) line from the middle of your sequence and run it again. Watch what happens to the log timestamps around that spot โ this is the fastest way to really understand what delay is doing.delay(...) line back, and add a comment-style note to yourself in the editor (a line starting with anything not matching the four patterns is simply ignored, so short throwaway notes are safe to leave in).HIGH, a delay(500);, one LOW. Confirm you get exactly one log entry for ON and one for OFF.HIGH, a long delay(2000);, then LOW โ notice how the log makes the timing obvious even though nothing "moves" on screen for two full seconds.Every log line's number is the total elapsed milliseconds since the run started โ not the gap since the previous line. If you see 500ms: LED ON followed by 1200ms: LED OFF, the LED was actually on for 1200 โ 500 = 700ms, even though the delay you wrote right before the OFF line might have said something else entirely โ because delay values accumulate across every line before them, not just the one immediately above. Getting comfortable doing that subtraction in your head is exactly the skill that makes reading a real embedded device's timing log easy later.
LED, HIGH, and LOW exactly, in capitals.delay(...) line between your HIGH and LOW calls; without it, both changes are scheduled at the same moment.digitalWrite(led, high) in lowercase and nothing happened โ this simulator matches exact capitalisation; real Arduino code is also case-sensitive for these constants, so this habit will serve you later too.delay(...) line affects everything that comes after it, not the line directly above it; re-read the "Reading the log like a pro" section if this feels confusing.digitalWrite line has a matching delay after itdelay line on purpose and understood why the timing shiftedWhich pattern was hardest to get the timing right on, and what did you change in your thinking to fix it? There's no wrong answer here โ this is about noticing your own learning, not getting a perfect score.
๐ก Tip: embedded code is really just "wait, then change something" repeated in sequence. Getting comfortable reading a timeline of on/off/wait โ and doing the mental math to know exactly when each change happens โ before any real loops or sensors are involved, is exactly what makes the jump to real conditional and repeating logic in Lab 2 much easier.
Building on Lab 1: you can already write a sequence of pinMode, digitalWrite, and delay lines and correctly predict when the LED will change, including doing the cumulative-time math by hand. This lab is about two intermediate skills: designing longer, precise, composite patterns, and practicing conditional thinking the way real embedded engineers do before their code ever touches a board.
pinMode(LED, OUTPUT); ยท digitalWrite(LED, HIGH); ยท digitalWrite(LED, LOW); ยท delay(ms);Everything else on a line is simply ignored โ which is actually useful, and Lab 1 briefly mentioned it: it means you can write a short plain-English note on its own line as a comment-style reminder, and the simulator will skip right over it without breaking anything.
Real Arduino/embedded code has genuine if/else logic โ "if the sensor reads above X, do this; otherwise do that." This particular simulator does not evaluate real conditions โ it only ever plays a flat sequence of lines, in order, every time. So in this lab, "conditional logic" means something specific: you'll write two separate, complete sequences โ one for each branch of a scenario โ as if you were sketching out both possible paths a real program might take, before ever writing the real if/else that would choose between them. This is a genuine professional habit, not a workaround: experienced embedded developers often draft both branches of a decision as plain sequences first, to make sure each one is correct in isolation, before wiring the actual condition around them.
delay(...) value.Below is a broken sequence, meant to blink the LED twice with a distinct pause between blinks. Copy it into the editor and find all three bugs before running it โ read it line by line first, the same way you'd review teammate's code, rather than just running it and guessing from the result.
pinMode(LED, OUTPUT);
digitalWrite(led, HIGH);
delay(400)
digitalWrite(LED, LOW);
delay(400);
digitalWrite(LED, HIGH);
delay(400);
digitalwrite(LED, LOW);
Write one sentence per bug once you've found them, explaining what was wrong and how you noticed it โ not just what you changed. (Hint: two of the three bugs are about exact spelling/capitalisation, and one is about a missing piece of punctuation you were told about in Lab 1.)
led, high, low, or a lowercase function name.When you compared your two branch sequences, what made one actually feel different from the other โ timing, number of flashes, or something else? If a real program had to pick between them automatically, what condition do you think it should check?
๐ก Tip: writing both branches of a decision as plain, runnable sequences before wiring up the real condition is exactly how experienced embedded developers avoid a common bug โ code where the "if" is correct but one of the branches was never actually tested in isolation. Carrying that habit into Lab 3, where you'll combine timing, branching, and a documented finished design into one capstone project, will save you real debugging time.
Building on Labs 1โ2: you can plan and hit exact cumulative timing, and you've hand-simulated a two-branch decision as two separate runnable sequences. This capstone asks you to put both skills together into one thing: a small, documented, multi-mode "device behaviour spec" โ the kind of planning document a real embedded engineer writes before touching actual hardware.
pinMode(LED, OUTPUT); ยท digitalWrite(LED, HIGH); ยท digitalWrite(LED, LOW); ยท delay(ms); โ read top to bottom, no real loops or live conditionals. Everything in this capstone is still built from those four lines; what's expert-level is the planning, precision, and documentation around them, not new syntax.
Design a single imaginary device โ pick one: a smoke detector, a low-battery warning light, or a doorbell/notification light. Your device must have three distinct modes, each built as its own complete, separately-runnable sequence:
The sequence below is meant to be an "Active" mode: three medium flashes, evenly spaced. Find all four bugs before running it โ review it line by line first, the way you would review a teammate's pull request, and write one sentence per bug once you've found them.
pinMode(LED, OUTPUT)
digitalWrite(LED, HIGH);
delay(300);
digitalWrite(LED, LOW);
delay(300);
digitalWrite(LED, High);
delay(300);
digitalWrite(LED, LOW);
delay(300)
digitalWrite(LED, HIGH);
delay(300);
digitalWrite(LED, LOW);
(Hint: two are missing punctuation you were warned about back in Lab 1, one is a capitalisation slip, and the fourth is about counting โ look carefully at how many ON/OFF pairs this is actually supposed to be versus how many it has.)
Read back through your own spec sheet and code as if you were reviewing a stranger's submission, and answer honestly:
If any answer is "no" or "not really," fix that part before saving โ a self-review that doesn't change anything usually means it wasn't done carefully enough.
Which of your three modes was hardest to make feel genuinely different from its neighbour, and what specifically did you change โ timing, number of flashes, or both โ to fix it? If you added the "what if it's triggered again mid-run" edge case to a real board, what's the one piece of information the real code would need that this simulator can't give you?
๐ก Tip: the actual expert skill this capstone is testing isn't writing more digitalWrite lines โ it's the habit of documenting a design decision in plain language before code exists to enforce it, and then honestly checking your own work against that documentation instead of assuming it's fine because it runs without error. That habit is what separates code that works once from code a team can actually maintain.