Arduino Learning Studio full lab
0/13 lessons marked complete
● Saved in this browser← Back to homepage

Lesson 13 of 13

Build a project: the Rescue Rover

Objective: Test the whole rover like an engineer: five repeatable trials on a marked course, one change at a time, a debug record for every fault, and a stop rule with a margin.

MODEL OF THIS SKETCHSTOPPED
The box (set before a run) simulated input
Playback view setting

How fast the model's time runs on screen. The rover's own speed is a code setting.

The two stops simulated input

Use them while it runs. Run again to start over (like RESET).

The trial course · seen from above (model)
STARTGOALA20 cm
Serial Monitor · modelsketch stopped

Press ▶ Run. The monitor shows only what the sketch prints.

Line ending: New Line

The sketch sends at 9600 baud (Serial.begin(9600)).

Stage 1 of 7 · What am I building?

Explore

A small indoor robot must cross a pretend supply corridor without hitting cardboard obstacles. It must stop when commanded and work over several trials. This is lesson 12's rover on a marked course: the start at the left, the goal at the top, and a cardboard box in one of five placements. Select each subsystem below, then run it.

Pick a subsystem. 0/5 inspected.

⚠ Two stops. Which one always works?

STOP on D2 is a software stop: it works only while the sketch runs and its loop reads D2. A sketch stuck in a long wait, or a loose button wire, and the software STOP does nothing. The power-off switch cuts the motor battery itself: no motor current can flow, whatever the sketch does. That is the stop that always works. Try both while the rover runs: STOP brakes it; with the battery off the wheels coast to a halt while the sketch carries on (watch the state).

Before you start (optional refreshers)

Systematic debugging

Describe the symptom, decide whether it points to the code or the wiring, check one thing at a time, then confirm the fix.

Simulation vs real hardware

The lab models ideal parts. Real circuits add tolerances, noise, loose wires and power limits, so always check wiring before powering up.

New ideas in this lesson

Repeatable trials

One good run is not reliability. Fix the start, the goal and the obstacle placements so every trial can be run again, record each one, change one thing at a time, and compare before with after. Keep a margin: the spec is a limit, not a target.

Build it for real · Arduino Uno R3

You need

  • Arduino Uno R3 (or compatible) and its USB cable
  • Breadboard and jumper wires
  • TB6612FNG dual motor driver (breakout board)
  • Two-wheel rover chassis with 2 small DC gear motors
  • HC-SR04 ultrasonic sensor
  • Push button (4-leg tactile): the software STOP
  • Motor battery: 4 × AA (6 V) with a power-off switch
  • For the course: tape for the start and the goal, a ruler, and cardboard boxes

Wiring

  1. Driver VCC → 5V; driver GND → GND.
  2. Motor battery + (through its power-off switch) → VM; battery − → driver GND, so every GND is joined.
  3. STBY → D3.
  4. Left motor: PWMA → D5, AIN1 → D4, AIN2 → D7; its leads → AO1 and AO2.
  5. Right motor: PWMB → D6, BIN1 → D12, BIN2 → D11; its leads → BO1 and BO2.
  6. Sensor at the front of the rover: VCC → 5V, GND → GND, Trig → D9, Echo → D10.
  7. STOP button across the breadboard's centre gap: one leg → D2; the diagonally opposite leg → GND. No resistor: INPUT_PULLUP provides it.
  8. Lift the wheels, upload, and test the STOP button and the power-off switch before the rover goes on the floor.

Before you power up: The power-off switch on the motor battery is the stop that always works: test it, and the STOP button, before every trial. Debug Dojo faults go in the code or the sensor's signal wires only, never the power path, and the power is off before anyone touches a wire. Never power the motors from a pin or the board's 5V pin; join every GND.

Tip: The Serial Monitor needs the USB cable: read it with the wheels lifted, or keep the cable slack with an adult beside the rover. Slow speed, indoors, clear of feet. Log the real trials in the course notebook.

The lab models ideal parts. Real circuits add tolerances, noise and loose wires, so check your wiring against this list before you plug in.

Sketch · read-only
// Rescue Rover: sense, decide, act, and log each reading for the debug record
// STOP button D2 to GND (INPUT_PULLUP); HC-SR04 Trig D9, Echo D10
// TB6612FNG: STBY D3~; left PWMA D5~, AIN1 D4, AIN2 D7; right PWMB D6~, BIN1 D12, BIN2 D11
// A reading every 60 ms, timed with millis()
// Serial Monitor at 9600 baud: one line per distance reading, in cm
const int stopPin = 2;                        // STOP button to GND: LOW = pressed
const int trigPin = 9;
const int echoPin = 10;
const int stbyPin = 3;
const int pwmAPin = 5;                        // left motor (A)
const int ain1Pin = 4;
const int ain2Pin = 7;
const int pwmBPin = 6;                        // right motor (B)
const int bin1Pin = 12;
const int bin2Pin = 11;

const int DRIVE = 0;                          // the rover's states
const int BRAKE = 1;
const int TURN = 2;
const int STOPPED = 3;
int state = DRIVE;                            // what the rover is doing now

const unsigned long readEvery = 60;           // ms between distance readings
const int stopCm = 20;
const unsigned long brakeMs = 300;
const unsigned long turnMs = 600;
unsigned long lastRead = 0;                   // millis() at the last reading
unsigned long stateStart = 0;                 // millis() when this state began

void leftMotor(int in1, int in2, int speed) {
  digitalWrite(ain1Pin, in1);
  digitalWrite(ain2Pin, in2);
  analogWrite(pwmAPin, speed);
}

void rightMotor(int in1, int in2, int speed) {
  digitalWrite(bin1Pin, in1);
  digitalWrite(bin2Pin, in2);
  analogWrite(pwmBPin, speed);
}

unsigned long readEcho() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);                // a 10 µs pulse starts a reading
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  return pulseIn(echoPin, HIGH, 30000);       // µs; 0 if no echo within 30 ms
}

void setup() {
  pinMode(stopPin, INPUT_PULLUP);             // HIGH until pressed
  Serial.begin(9600);                         // the Serial Monitor must match
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(stbyPin, OUTPUT);
  pinMode(ain1Pin, OUTPUT);
  pinMode(ain2Pin, OUTPUT);
  pinMode(bin1Pin, OUTPUT);
  pinMode(bin2Pin, OUTPUT);
  digitalWrite(stbyPin, HIGH);                // wake the driver up
}

void loop() {
  unsigned long now = millis();               // ms since the program started
  bool close = false;
  if (now - lastRead >= readEvery) {          // safe when millis() rolls over
    lastRead = now;
    unsigned long duration = readEcho();      // waits in pulseIn() for the echo
    float distance = duration * 0.0343 / 2;
    close = duration > 0 && distance <= stopCm; // 0 is no reading, never close
    Serial.println(distance);                 // cm, for the debug record
  }
  if (digitalRead(stopPin) == LOW) state = STOPPED; // STOP wins: checked on every pass, first
  switch (state) {                            // decide: run the case for the state
    case DRIVE:
      leftMotor(HIGH, LOW, 128);              // act: both forward
      rightMotor(HIGH, LOW, 128);
      if (close) {
        state = BRAKE;
        stateStart = now;
      }
      break;
    case BRAKE:
      leftMotor(HIGH, HIGH, 0);               // short brake
      rightMotor(HIGH, HIGH, 0);
      if (now - stateStart >= brakeMs) {
        state = TURN;
        stateStart = now;
      }
      break;
    case TURN:
      leftMotor(LOW, HIGH, 110);              // spin left
      rightMotor(HIGH, LOW, 110);
      if (now - stateStart >= turnMs) state = DRIVE;
      break;
    case STOPPED:
      leftMotor(HIGH, HIGH, 0);               // stays here until RESET
      rightMotor(HIGH, HIGH, 0);
      break;
  }
}

Press ▶ Run.