The assistance challenge. Students build a patrol system in three milestones (the base starts and stops the robot by radio, the robot reports blockages once, then an alert log and status lights), combining line following, distance, modes, roles and two-way radio.
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, Investigate 12 min · Milestone 1 15 min · Milestone 2 18 min · Milestone 3 12 min · Real robot 3 min (optional)
This is the assistance strand's challenge, using lessons 3 and 5 (distance and line), 6 (mode), 7 (radio commands) and 9 (roles and alert once). The key design rule: radio handlers only change the mode; forever drives the motors. A move made in a handler would be undone straight away by forever (or, like Jack's patrol, would only run once).
Both micro:bits run the same program; A+B makes one the base. The base's A sends "go", B sends "stop". The robot sends "blocked" (once, using alerted) and "clear". The milestones fade the help: Milestone 1 gives three numbered steps, Milestone 2 an outline, Milestone 3 goals only. Each starter is the previous milestone's solution. Showcase and Patrol and alert together mark the course Challenge complete.
The robots start near the line in the simulator; if one does not pick it up, drag it on. The logo needs a micro:bit V2 (shake works on V1).
Each side has its own job, and radio handlers only set the mode. Ask which part of the program drives the motors.
| What you will see | A question to ask | The fix |
|---|---|---|
| The robot never moves after 'go'. | What is the robot's role? Does forever check the mode? | Only the robot's micro:bit sets mode; forever follows the line when mode = patrol. Press A on the base, not the robot. |
| The base beeps again and again. | Is 'blocked' sent only if not alerted? | Send it inside if not alerted, then set alerted to true. |
| The base and robot both react to the same message. | Does on radio received check the role? | Robot jobs inside if role = robot; base jobs inside else if role = base. |
One possible solution for each task. Other correct programs are fine.

function followLine () {
if (robot.detectLine(RobotLineDetector.Left) && robot.detectLine(RobotLineDetector.Right)) {
robot.motorTank(40, 40)
} else if (robot.detectLine(RobotLineDetector.Left)) {
robot.motorTank(10, 40)
} else if (robot.detectLine(RobotLineDetector.Right)) {
robot.motorTank(40, 10)
} else {
robot.motorStop()
}
}
input.onButtonPressed(Button.AB, function () {
role = "base"
robot.motorStop()
basic.showString("B")
})
input.onButtonPressed(Button.A, function () {
radio.sendString("go")
})
input.onButtonPressed(Button.B, function () {
radio.sendString("stop")
})
radio.onReceivedString(function (receivedString) {
if (role == "robot") {
if (receivedString == "go") {
mode = "patrol"
} else if (receivedString == "stop") {
mode = "stop"
}
}
})
let mode = ""
let role = ""
robot.elecfreaksCuteBot.start()
radio.setGroup(1)
role = "robot"
mode = "stop"
basic.forever(function () {
if (role == "robot") {
if (mode == "patrol") {
followLine()
} else {
robot.motorStop()
}
}
})

function followLine () {
if (robot.detectLine(RobotLineDetector.Left) && robot.detectLine(RobotLineDetector.Right)) {
robot.motorTank(40, 40)
} else if (robot.detectLine(RobotLineDetector.Left)) {
robot.motorTank(10, 40)
} else if (robot.detectLine(RobotLineDetector.Right)) {
robot.motorTank(40, 10)
} else {
robot.motorStop()
}
}
input.onButtonPressed(Button.AB, function () {
role = "base"
robot.motorStop()
basic.showString("B")
})
input.onButtonPressed(Button.A, function () {
radio.sendString("go")
})
input.onButtonPressed(Button.B, function () {
radio.sendString("stop")
})
radio.onReceivedString(function (receivedString) {
if (role == "robot") {
if (receivedString == "go") {
mode = "patrol"
} else if (receivedString == "stop") {
mode = "stop"
}
} else if (role == "base") {
if (receivedString == "blocked") {
basic.showIcon(IconNames.No)
music.play(music.tonePlayable(880, music.beat(BeatFraction.Whole)), music.PlaybackMode.InBackground)
} else if (receivedString == "clear") {
basic.showIcon(IconNames.Yes)
}
}
})
let alerted = false
let mode = ""
let role = ""
robot.elecfreaksCuteBot.start()
radio.setGroup(1)
role = "robot"
mode = "stop"
basic.forever(function () {
if (role == "robot") {
if (mode == "patrol") {
if (robot.obstacleDistance() < 15) {
robot.motorStop()
if (!(alerted)) {
radio.sendString("blocked")
alerted = true
}
} else {
if (alerted) {
radio.sendString("clear")
}
alerted = false
followLine()
}
} else {
robot.motorStop()
}
}
})

function followLine () {
if (robot.detectLine(RobotLineDetector.Left) && robot.detectLine(RobotLineDetector.Right)) {
robot.motorTank(40, 40)
} else if (robot.detectLine(RobotLineDetector.Left)) {
robot.motorTank(10, 40)
} else if (robot.detectLine(RobotLineDetector.Right)) {
robot.motorTank(40, 10)
} else {
robot.motorStop()
}
}
input.onButtonPressed(Button.AB, function () {
role = "base"
robot.motorStop()
basic.showString("B")
})
input.onButtonPressed(Button.A, function () {
radio.sendString("go")
})
input.onButtonPressed(Button.B, function () {
radio.sendString("stop")
})
input.onLogoEvent(TouchButtonEvent.Pressed, function () {
basic.showNumber(alerts)
})
radio.onReceivedString(function (receivedString) {
if (role == "robot") {
if (receivedString == "go") {
mode = "patrol"
} else if (receivedString == "stop") {
mode = "stop"
}
} else if (role == "base") {
if (receivedString == "blocked") {
alerts += 1
basic.showIcon(IconNames.No)
music.play(music.tonePlayable(880, music.beat(BeatFraction.Whole)), music.PlaybackMode.InBackground)
} else if (receivedString == "clear") {
basic.showIcon(IconNames.Yes)
}
}
})
let alerts = 0
let alerted = false
let mode = ""
let role = ""
robot.elecfreaksCuteBot.start()
radio.setGroup(1)
role = "robot"
mode = "stop"
basic.forever(function () {
if (role == "robot") {
if (mode == "patrol") {
if (robot.obstacleDistance() < 15) {
robot.motorStop()
robot.setColor(0xff0000)
if (!(alerted)) {
radio.sendString("blocked")
alerted = true
}
} else {
if (alerted) {
radio.sendString("clear")
}
alerted = false
robot.setColor(0x00ff00)
followLine()
}
} else {
robot.motorStop()
robot.setColor(0x000000)
}
}
})
Stretch: Add a 'home' command: the base's logo sends 'home' and the robot turns round (a 'turn' mode that spins for 1.2 seconds, then goes back to patrol).
Support: Pair students for Milestone 2: one builds the robot side, one the base side, then they agree the words.
Why does the robot's radio handler set the mode instead of driving the motors?
A handler runs once per message, and forever keeps driving the motors. Setting the mode lets forever do the job again and again until the mode changes.
Real-world links: hospital delivery robots, farm fence checkers, warehouse robots, pipeline inspection. Discussion: what should the robot do when it cannot reach the base?