Challenge 1
Prove the interlock. Attempt to fire unarmed and show the refusal, then arm and fire successfully.
EV3 Robotics›Level 2 · Intermediate›Lesson 19
Level 2 · Lesson 19 · EV3-L02-1960 minutes · Ages 9–16 · Model: Archery Robot
The Archery Robot: a driving base carrying a bow. One Medium Motor draws the bow and holds it; a second Medium Motor lets it go.
Four motors, three jobs. Two drive it to the shooting line, one arms it, one shoots. The arming and the shooting are deliberately separate, which is how every serious machine that stores energy is built.
By the end of the lesson the release will physically refuse to fire a bow that is not drawn — and it will tell you why it refused.
Safety, before anything else. This model launches a beam. Point it at a wall or a cardboard box, never at a person, an animal or a window. Everybody within two metres wears eye protection. One rule, no exceptions, all lesson.
Every archery range in the world runs on the same rules, and they are not suggestions. Arrows are not nocked until the whistle. Nobody crosses the line until the whistle. A single blast means shoot; two mean go and collect; five or more mean stop immediately, whatever you are doing.

A drawn bow is a great deal of stored energy waiting for one small event. A modern compound bow holds tens of kilograms of draw, and its whole design is about making that energy easy to hold and hard to release by accident.
So the range does not rely on everybody being careful. It makes the dangerous thing impossible at the wrong moment — no arrow on the string, no archer past the line, and one signal everybody obeys. Rules that depend on nobody making a mistake are not safety rules; they are hopes.
Lesson 41’s Chu Ko Nu could not get its order wrong — the peg could not reach the string until the lever had come far enough, so the sequence was guaranteed by the shape of the parts.
This machine has no such guarantee. Its release motor will happily fire an undrawn bow, and the program is the only thing standing between the two.
When the mechanism cannot make a mistake impossible, the program has to.
An interlock is a check that guards a dangerous or irreversible step. It is not there to make the machine clever. It is there so a mistake produces nothing instead of producing a disaster.
if <<(sedia) = (1)> and <([A v] degrees counted) > (300)>> then [D v] run [clockwise v] for (0.5) [rotations v] at (100) % speed :: motors set [sedia v] to (0) else play beep (45) for (0.5) seconds :: sound write [DITOLAK] at line (3) :: display end
This is what makes the interlock strong, and it is worth slowing down for.
| Condition | What it really is | Can it be wrong? |
|---|---|---|
(sedia) = (1) | The program’s belief. It was set when the arm stack ran. | Yes. If somebody un-draws the bow by hand, the variable still says 1 |
([A v] degrees counted) > (300) | The machine’s evidence. The draw motor really is that far round. | Much harder. This is a measurement of the actual mechanism |
Lesson 33 ended on exactly this problem. The rifle’s ammunition count was what the machine believed, and reaching into the magazine by hand made the belief false with nothing to notice. This is the answer: check the belief and check the world, and join them with and.
set [sedia] to (0) is in the then branch, and it matters: the bow has been fired, so it is no longer armed. Forget that and a second press fires an empty bow with the program’s blessing.The and is Lesson 17. The state variable is Lesson 34. The encoder reading is Lesson 8. The audible refusal is Lesson 28. Nothing here is a new block — the lesson is the arrangement, and the arrangement is what makes a machine safe to hand to somebody else.
Up to now a robot has been able to wait for a sensor. Deciding is different: the robot checks the sensor and does one thing or another depending on the answer — and then carries on either way.
| Block | What it does |
|---|---|
if <> then end | Runs the blocks inside only when the condition is true. Otherwise skips them. |
if <> then else end | Runs one set of blocks when true and a different set when false. |
A decision made once, at the start, is almost never what you want. Here are two robots with the identical if-else, testing the identical sensor against the identical number — one inside a loop and one not.
decision inside a loop
the same decision, once
A decision is only worth as much as the last time it was made. Inside a loop, that is a few milliseconds ago.
Nothing is wrong with the right-hand program’s decision. It asked the question, got a truthful answer, and acted on it correctly. It simply never asked again, and the world moved on. Decisions belong inside a loop, so the robot keeps re-deciding as things change.
when program starts :: events hat
forever
if <([4 v] distance in [cm v] :: sensors) < (15)> then
stop moving :: movement
else
start moving [straight: 0] :: movement
end
endOnce there is more than one question, decisions can be arranged in four ways. They look nearly identical stacked up in the editor, which is exactly why they get muddled — the thing that differs is not what the blocks say, it is which routes through them exist.
One question sorts them almost completely:
| Are the questions… | Use | How many bodies can run |
|---|---|---|
| independent — any combination can be true | separate ifs | none, some, or all |
| one question, two answers | if / else | exactly one |
| the second only matters when the first is true | nested if | one, and only via the outer |
| mutually exclusive cases — exactly one should win | chained if / else | exactly one, the first that matches |
Each if is asked no matter what the others answered, so any number of them can fire on the same pass. That is the right shape when the conditions genuinely have nothing to do with each other.
forever
if <[3 v] is ambient light intensity [< v] (20) %? :: sensors> then
[A v] start motor [clockwise v] :: motors
end
if <[4 v] is distance [< v] (15) [cm v]? :: sensors> then
[D v] start motor [clockwise v] :: motors
end
endExactly one branch runs, every time. Reach for this whenever the robot must do something either way — and in preference to two ifs testing opposite conditions, which is the same idea written twice and can drift apart.
Putting one if inside another means the inner question is only ever asked when the outer one is true. Use it when the second question is meaningless otherwise: there is no point asking which side an obstacle is on when there is no obstacle.
if <[4 v] is distance [< v] (15) [cm v]? :: sensors> then
if <([2 v] angle :: sensors) < (0)> then
start moving [right: 50] :: movement
end
endWhen not to nest. If you only want “both true” and nothing happens at the outer level, an and says it in one block and reads better:
if <<[4 v] is distance [< v] (15) [cm v]? :: sensors> and <([2 v] angle :: sensors) < (0)>> then start moving [right: 50] :: movement end
Nesting earns its place when something happens at the outer level too, or when there is an else at each level and the two mean different things.
This is the shape for a list of cases where exactly one should win: colour bands, distance bands, speed ranges. EV3 Classroom has no else-if block, so you build a chain by putting the next if inside the else of the last one.
And here is why it matters, because this is the single commonest bug in this whole module. Three bands written as three separate ifs are each perfectly correct, and together they are wrong: a reading of 20 is under 30 and under 60 and under 90, so all three run and the last one to run is the one that sticks.
three separate ifs
chained — if / else / if
↑ the rest is inside the else — never asked
Separate ifs are not wrong here so much as unguarded: nothing stops a second one matching. Chaining is what makes “the first one wins” true.
The rule to carry away: if the cases are meant to be exclusive, they must be made exclusive. Chaining does it by construction. Separate ifs only work if you are careful to write non-overlapping bands yourself — light < 30, 30 to 60, 60 and over — which is more to get right and easy to break later.
This is the point at which a machine stops following a script and starts responding. A thermostat, an automatic door, a robot vacuum — all of them are a decision inside a loop.
Every EV3 motor contains a sensor that counts how far it has actually turned. That means a motor is not only an output — you can ask it where it is, and the answer describes what really happened rather than what you asked for.
| Block | What it does |
|---|---|
([A v] degrees counted :: sensors) | Reports how far this motor has actually turned since the count was last reset, in degrees. |
[A v] reset degrees counted :: motors | Sets the count back to zero, making right here the new reference point. |
([A v] speed :: sensors) | Reports how fast the motor is turning right now, as a percentage. A motor that is being driven but reads zero is a motor that is stuck. |
Those two are not always the same. Here are two identical motors, running the same program, with only their mechanisms different.
the same program, on two identical motors
Nothing on the Brick announces a stall. The only evidence is that the counter stopped changing while the motor was still being told to turn.
Nothing on the Brick announces the jam. The motor is still being driven, the program is still sitting on the same block, and the only trace of the problem anywhere is a counter that has stopped climbing. Comparing what you asked for with what was counted is how a robot notices — which is the whole of stall detection.
When the Brick powers on, the count is simply whatever it happens to be. It is not a position on the machine — it becomes one only when you tie it to something physical.
That is what reset degrees counted is for, and it is not just a tidy-up block for the top of a program. Where you put it decides what zero means, so putting it part-way through — after the mechanism has been driven somewhere known — is the normal way to use it, not an abuse of it.
A conveyor has no idea where it is. Give it a touch sensor at one end and it can find out: drive it until the sensor is pressed, and it is now at a place you can name. Only then reset the count, and that end becomes 0.
when program starts :: events hat [A v] start motor [counterclockwise v] :: motors [1 v] wait until [pressed v] :: sensors [A v] stop motor :: motors [A v] reset degrees counted :: motors
The order is the whole point. Reset before the sensor is pressed and you have zeroed a random spot; reset after it, and every later reading means “how far from home”. This is called homing, and it is why a printer rattles its head to one side when you switch it on.
Drive towards home gently. The mechanism is deliberately being run into its own end stop, so a slow speed saves the gears — and the touch sensor is what stops it, which means it stops in the same place every time regardless of where it started.
Home is often a corner, and a corner is an awkward place to measure from. Say the conveyor carries a chute that dispenses bricks, and you would rather describe its position as left and right of the middle. Then home once, drive the known distance to the middle, and reset again there:
when program starts :: events hat [A v] start motor [counterclockwise v] :: motors [1 v] wait until [pressed v] :: sensors [A v] stop motor :: motors [A v] reset degrees counted :: motors [A v] run [clockwise v] for (900) [degrees v] :: motors [A v] reset degrees counted :: motors
Now the middle is 0. Moving right counts up, moving left counts down past zero into negative numbers — the count is perfectly happy to go negative — and “go back to the middle” becomes the simplest instruction in the program: drive until the count reaches 0.
Both resets earn their place. The first one turns a meaningless number into a distance from a real, repeatable place. The second one moves zero to where the maths is easiest. A reset in the middle of a program is only a mistake when the mechanism is somewhere you cannot name.
Because the count is in degrees, and a wheel of known size travels a known distance per turn, the reading can be converted into how far the robot has actually driven. That is how a robot reports a distance in centimetres rather than in rotations — and it is why changing the wheels changes the answer.
This is how a printer knows the paper jammed, how a car window stops when it meets your hand, and how a robot arm knows it has reached its limit. A machine that can only give orders is fragile; one that can check what happened can recover.
Guard the step you cannot undo. Check what you believe and what you can measure, and refuse out loud.
A sensor that reports a number cannot be used to make a decision on its own — 23 is neither true nor false. An operator turns that number into an answer by comparing it with something.
| Block | What it does |
|---|---|
<(x) > (50)> | True when the left value is bigger than the right. |
<(x) < (50)> | True when it is smaller. |
<<> and <>> | True only when both conditions are true. |
<<> or <>> | True when at least one of them is. |
Forget the symbols for a moment. A comparison is a question about position on a number line: is x to the left of the other number, or to the right? Left is smaller, right is bigger — and that is the whole of it.
Drag the orange x and the black marker, and change the comparison. The green stretch is every position of x that would make the answer true — so you can see where the answer flips before you get there. Turn not on and watch the green jump to the other side.
Drag either marker, or use the arrow keys.
is x to the LEFT of it?
The lab above asks one question at a time: is this x true? A robot never has just one x, though — a sensor reading slides up and down all the time, so what really matters is which stretch of the line makes the condition true. This one draws the whole answer at once.
Drag the circle to move the number you are comparing against, and change the comparison. Everything shaded green is a value of x that would make it true.
Drag the circle, or use the arrow keys. It moves in steps of 0.2.
Every number to the left of 0.2 — but not 0.2 itself, so the circle is hollow.
Watch the circle, because it carries the part everyone gets wrong:
Now turn not on with x > 2 selected and watch two things happen together. The shading jumps to the other side, and the circle fills in — because “not greater than 2” means 2 or less, and 2 has to be part of it. That pairing is the whole reason a hollow circle is worth drawing.
Why a robot cares. Two conditions that look almost identical — light < 30 and not (light > 30) — differ by exactly one value, the reading of precisely 30. A robot sitting right on its threshold behaves differently under the two, and that is the sort of bug that only shows up occasionally and looks like a broken sensor.
These three join answers together rather than numbers. The trap is that English is looser than a program: “stop if it is close and the bumper is pressed” sounds like it covers both situations, when it covers neither on its own.
Flip the two conditions and watch the table. There are only four possible situations in total, and and and or differ on exactly two of them.
| close | bumper | and | or |
|---|---|---|---|
| true | true | true | true |
| true | false | false | true |
| false | true | false | true |
| false | false | false | false |
and is fussy: it wants both. Three of the four rows are false.
Two sensors are running below: an Ultrasonic reporting a number, and a Touch Sensor reporting true or false. Watch the comparison turn the number into an answer, and watch and and or disagree.
and was true in one row out of four. or was true in three. That is the whole difference, and it is why one of them makes a robot look broken.
The comparison is doing one job: it takes a reading that is neither true nor false and, by holding it against a number you chose, produces something a decision can use. The moment the blue fill crosses the black marker is the moment the answer changes.
The number you compare against is a design decision, not a fact. “Close” for a parking sensor might be 15 cm; for a robot arm it might be 3. Pick it by measuring what the sensor actually reads in the situation you care about, then leave a margin.
and narrows: both must hold, so the robot acts less often but more certainly — stop only if something is close and the bumper is pressed. or widens: either will do, so the robot acts more readily — stop if something is close or the bumper is pressed.
In the four situations above, and was true in one of them and or in three. That is the practical difference: swapping one for the other does not adjust a robot slightly, it changes how often it reacts at all.
Say this back before moving on: “Armed, and really drawn. Otherwise, no.”
Look at your robot. Four motors. Which two work as a pair, and which two work alone?
| Part | What it is doing here |
|---|---|
| EV3 Intelligent Brick | The body and the range officer. Its buttons arm and fire; its screen says which state the bow is in. |
| Large Motor ×2 — the drive | A matched pair. They move the robot to the shooting line. |
| Medium Motor — the draw | Pulls the string back and holds it. Its encoder is your safety evidence — the number of degrees it has turned is a direct measurement of how drawn the bow is. |
| Medium Motor — the release | Trips the catch. It is the irreversible step, and the only one with a guard on it. |
| The bow and its band (not electronic) | Stores the energy. Nothing in the program can put that energy back once the catch is tripped — which is what “irreversible” means. |
Sensors go in ports 1, 2, 3, 4. Motors go in ports A, B, C, D. They are not interchangeable, and nothing will tell you politely if you swap them.
| Part | Port | Why this one |
|---|---|---|
| Left drive wheel | B | The pair. Two motors, one job. |
| Right drive wheel | C | The other half of the pair. |
| Draw motor | A | Its own job — and the port whose encoder the interlock reads. Get this wrong and the guard is measuring the wrong motor. |
| Release motor | D | A separate job, at the far end of the port list from A, so a mis-typed port cannot make the draw motor fire. |
This is the first model in the course to use all four motor ports. The layout is not decoration: B/C is a pair, A and D are loners, and A and D are as far apart as the alphabet allows because confusing them is the one mistake that matters here.
The manual never says which port anything goes in — it ends at the last building step with no cables drawn. So the ports above are this course’s conventions, not the manual’s instructions, and nothing in your build contradicts them.
Check your own wiring now:
Two routes, and either is fine. USB is the reliable one and the one to fall back on when a room’s Bluetooth is busy; Bluetooth leaves the robot free to move, which some models need.
Do these in order. Naming the Brick after you go looking for it in the list is how groups end up driving each other’s robots.
EV3 until somebody changes it.EV3.The two failures, every class, every time. The Brick has gone to sleep while you were building — press the centre button to wake it. Or you have paired with the group at the next table, which is why the name matters.
The long version, including Port View and how to read the port tiles, is in the Brick & Bluetooth guide.
Bluetooth, and stay in reach of the stop button. This robot drives and stores energy, so you want to be able to walk with it and still stop it. A cable puts you at the laptop instead of beside the machine.
Stuck? The long version, with a photograph of every screen, is in the Brick & Bluetooth guide.
Four stacks: set up, drive to the line, arm, and a guarded release.
when program starts :: events hat set movement motors to [B v] and [C v] :: movement set [sedia v] to (0) [A v] reset degrees counted :: motors clear display :: display write [TIDAK SEDIA] at line (1) :: display when [up v] button [pressed v] :: events hat move [forward v] for (1) [rotations v] at (30) % speed :: movement when [left v] button [pressed v] :: events hat [A v] run [clockwise v] for (1) [rotations v] at (30) % speed :: motors set [sedia v] to (1) write [SEDIA ] at line (1) :: display when [center v] button [pressed v] :: events hat if <<(sedia) = (1)> and <([A v] degrees counted) > (300)>> then [D v] run [clockwise v] for (0.5) [rotations v] at (100) % speed :: motors set [sedia v] to (0) [A v] reset degrees counted :: motors write [TIDAK SEDIA] at line (1) :: display else play beep (45) for (0.5) seconds :: sound write [DITOLAK] at line (3) :: display end
then branch does three things: fire, clear the flag, and reset the encoder. All three are part of “the shot is over”, and leaving any out leaves the machine lying about its own state.else branch is not empty. A beep you can hear across the room and a message on the screen. A silent refusal is a fault report nobody receives.SEDIA so it fully covers TIDAK SEDIA underneath. The display overwrites only the characters it is given.What success looks like: press centre first, before arming. Nothing fires, the Brick beeps low, the screen says DITOLAK. Now press left — the bow draws, the screen says SEDIA. Press centre — it shoots, and the screen goes back to TIDAK SEDIA. Press centre again: refused.
If it refuses a properly drawn bow, your 300 is higher than your real draw — go back to your measurement. If it fires when it should not, stop and check that the draw motor really is on port A, because the guard is reading whichever motor is there.
One change at a time, and do all of these with no arrow loaded until the last one. The mechanism cycles perfectly well empty, and you are testing refusals, not range.
(sedia) = (1). Do the same thing. It fires an undrawn bow, because the program’s belief was never checked against anything.set [sedia] to (0) from the then branch. Fire twice. The second one goes off on a bow that is no longer drawn — the machine forgot it had already fired.else branch entirely. Press centre unarmed: nothing at all. Ask somebody who has not seen the program to use it and watch how long before they say it is broken.(sedia) = (1). A machine should not drive about with a loaded bow, and this is one if.Steps 2 and 3 together are the argument for using both. The variable knows things the encoder cannot — like whether a shot has already been taken. The encoder knows things the variable cannot — like whether a human interfered. Neither alone is a safety check. Together they are hard to fool.
An interlock is not one check. It is every check the dangerous step depends on, joined by and.
Build the model before you read any further. Everything after this is about making it do something, and none of it will make much sense with nothing on the table in front of you.
Use the viewer's own controls to zoom and turn pages. Fullscreen makes it big enough to build from.
The same build on Google Drive — sometimes a video, sometimes a scan:
Use the viewer's own controls to zoom and turn pages. Fullscreen makes it big enough to build from.
Check the finished build against the picture before you switch anything on. A motor mounted the wrong way round is far easier to spot now than it is to debug later, when it looks like a program fault.

This model drives, so its challenges are run on a mat. Mats differ between branches — check you are looking at the one in your room.

WRO 2026 RoboMission Elementary — Robot Rockstars · official WRO game mat, 2362 × 1143 mm
The challenges name these places rather than distances, so the same challenge works on any mat:
Work through the challenges in order — each is harder than the last. The mission comes after all three, and it is meant to make you plan before you build.
Prove the interlock. Attempt to fire unarmed and show the refusal, then arm and fire successfully.
Add the drive interlock — the robot refuses to move while armed — and make the two refusals sound different from each other.
Defeat your own interlock. Find a way to make it fire when it should not, then close the hole you found.
Drive to the firing line your teacher names, arm, fire at a target and return — with the robot refusing every step that is out of order at every point along the way. Write the full list of things it must refuse BEFORE you build it, then hand it to another group and let them try to break it.