Students build a sign-driven delivery robot in three milestones; its distance sensor overrules the model, it reports back through the bridge, and it counts deliveries.
Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.
Predict, Run and Investigate 12 min · Milestone 1 10 min · Milestone 2 15 min · Full system 8 min (typical) · Milestone 3 12 min
The challenge: everything from AI robots plus Robotics challenges. The robot starts as the careful sign robot (Go twice, Unsure and silence stop it). The milestones add no new blocks:
if obstacle distance < 15 → stop goes first in forever. The model only knows what the camera sees on the sign; the sensor knows what is in front of the robot. Jo's robot (the check last) is the mistake to watch for.dist twice a second; blocked 1 and blocked 0 go once each, using a variable (the same "send once" idea as Patrol and alert). The bridge turns them into lines such as dist:23, which the board shows as values.Without robots: every milestone works in the simulator (buttons stand in for the model; drag a box to block). The full-system step is typical.
Putting the sensor check first so it overrules the model. When the robot pushes boxes, ask which question in the chain is asked first.
| What you will see | A question to ask | The fix |
|---|---|---|
| The robot still pushes boxes. | Where is the distance check in the if, else if chain? | Make it the first if; turn the Go branch into else if. |
| blocked arrives over and over. | What stops it being sent again next time round forever? | Send only if blocked is false, then set blocked to true. |
| No values arrive on the board. | Is the bridge connected, in the same radio group, running the bridge program? | Connect the bridge and match the groups. |
One possible solution for each task. Other correct programs are fine.

radio.onReceivedString(function (receivedString) {
if (receivedString == "Go") {
goCount += 1
} else {
goCount = 0
}
mode = receivedString
lastHeard = input.runningTime()
})
basic.forever(function () {
if (input.runningTime() - lastHeard > 2000) {
mode = "Stop"
}
if (robot.obstacleDistance() < 15) {
robot.motorStop()
robot.setColor(0xff0000)
} else if (mode == "Go" && goCount >= 2) {
robot.motorTank(40, 40)
robot.setColor(0x00ff00)
} else if (mode == "Left") {
robot.motorTank(-30, 30)
robot.setColor(0xffff00)
} else if (mode == "Right") {
robot.motorTank(30, -30)
robot.setColor(0xffff00)
} else {
robot.motorStop()
robot.setColor(0xff0000)
}
})
input.onButtonPressed(Button.A, function () {
radio.sendString("Left")
})
input.onButtonPressed(Button.B, function () {
radio.sendString("Right")
})
input.onButtonPressed(Button.AB, function () {
radio.sendString("Go")
})
input.onLogoEvent(TouchButtonEvent.Pressed, function () {
radio.sendString("Stop")
})
let goCount = 0
let lastHeard = 0
let mode = ""
robot.elecfreaksCuteBot.start()
radio.setGroup(7)

radio.onReceivedString(function (receivedString) {
if (receivedString == "Go") {
goCount += 1
} else {
goCount = 0
}
mode = receivedString
lastHeard = input.runningTime()
})
basic.forever(function () {
if (blocked && robot.obstacleDistance() >= 15) {
radio.sendValue("blocked", 0)
blocked = false
}
if (input.runningTime() - lastHeard > 2000) {
mode = "Stop"
}
if (robot.obstacleDistance() < 15) {
robot.motorStop()
robot.setColor(0xff0000)
if (!(blocked)) {
radio.sendValue("blocked", 1)
blocked = true
}
} else if (mode == "Go" && goCount >= 2) {
robot.motorTank(40, 40)
robot.setColor(0x00ff00)
} else if (mode == "Left") {
robot.motorTank(-30, 30)
robot.setColor(0xffff00)
} else if (mode == "Right") {
robot.motorTank(30, -30)
robot.setColor(0xffff00)
} else {
robot.motorStop()
robot.setColor(0xff0000)
}
})
basic.forever(function () {
radio.sendValue("dist", robot.obstacleDistance())
basic.pause(500)
})
input.onButtonPressed(Button.A, function () {
radio.sendString("Left")
})
input.onButtonPressed(Button.B, function () {
radio.sendString("Right")
})
input.onButtonPressed(Button.AB, function () {
radio.sendString("Go")
})
input.onLogoEvent(TouchButtonEvent.Pressed, function () {
radio.sendString("Stop")
})
let blocked = false
let goCount = 0
let lastHeard = 0
let mode = ""
robot.elecfreaksCuteBot.start()
radio.setGroup(7)

radio.onReceivedString(function (receivedString) {
if (receivedString == "Go") {
goCount += 1
} else {
goCount = 0
}
if (receivedString == "Stop" && mode == "Go") {
deliveries += 1
radio.sendValue("done", deliveries)
}
mode = receivedString
lastHeard = input.runningTime()
})
input.onGesture(Gesture.Shake, function () {
basic.showNumber(deliveries)
})
basic.forever(function () {
if (blocked && robot.obstacleDistance() >= 15) {
radio.sendValue("blocked", 0)
blocked = false
}
if (input.runningTime() - lastHeard > 2000) {
mode = "Stop"
}
if (robot.obstacleDistance() < 15) {
robot.motorStop()
robot.setColor(0xff0000)
if (!(blocked)) {
radio.sendValue("blocked", 1)
blocked = true
}
} else if (mode == "Go" && goCount >= 2) {
robot.motorTank(40, 40)
robot.setColor(0x00ff00)
} else if (mode == "Left") {
robot.motorTank(-30, 30)
robot.setColor(0xffff00)
} else if (mode == "Right") {
robot.motorTank(30, -30)
robot.setColor(0xffff00)
} else {
robot.motorStop()
robot.setColor(0xff0000)
}
})
basic.forever(function () {
radio.sendValue("dist", robot.obstacleDistance())
basic.pause(500)
})
input.onButtonPressed(Button.A, function () {
radio.sendString("Left")
})
input.onButtonPressed(Button.B, function () {
radio.sendString("Right")
})
input.onButtonPressed(Button.AB, function () {
radio.sendString("Go")
})
input.onLogoEvent(TouchButtonEvent.Pressed, function () {
radio.sendString("Stop")
})
let deliveries = 0
let blocked = false
let goCount = 0
let lastHeard = 0
let mode = ""
robot.elecfreaksCuteBot.start()
radio.setGroup(7)
Stretch: Make the robot slow down (motor tank 20) between 15 and 30 cm, and stop under 15.
Support: Give milestone 1's if complete; students write the reports in milestone 2.
Why does the distance check go before the Go branch?
An if, else if chain runs only the first match; the safety check must win whatever the model says, so it goes first.
Where this leads: real robots fuse a camera model with many sensors (distance, bump, line), and safety checks always win. The Python pathway's Machine learning with Gradio trains models in code.