Build Your Mark I
We'll build the Mark I together, step by step. At each step: a 3D animation you can spin with your finger or mouse to see it from every angle, and a real video that shows you the move. Take your time — when you're ready, move on to the next step.
Don't have your Mark I yet? Build yours in 3D and pick your colors.
Customize my Mark I ← Learning CenterHow does the code work?
Now that your Mark I is built, let's discover its brain: the code. You don't need to know how to program — we'll read it together, one little piece at a time. A program is just a list of really simple instructions that the robot follows very fast, over and over.
#include <AFMotor_R4.h>
We give the robot a ready-made cookbook to control its motors. That way we don't have to explain everything again: we reuse recipes that were already written for us.
AF_DCMotor moteur1(1);
AF_DCMotor moteur2(2);
We introduce the two motors to the robot and give them a name: moteur1 and moteur2. The (1) and the (2) tell which socket on the board they're plugged into. Now, when we say “moteur1”, the robot will know who we're talking about.
#define TRIG A0
#define ECHO A1
The robot has special eyes: a sensor that sends out a little “beep” and listens for the echo that comes back, exactly like a bat. TRIG is the wire that sends the beep; ECHO is the wire that listens for the return. We just say which sockets they're plugged into (A0 and A1).
const int SEUIL_CM = 20;
const int VITESSE = 200;
We set two numbers once and for all. SEUIL_CM = 20 is the safety distance: if an obstacle is closer than 20 centimeters, the robot will say “watch out!”. VITESSE = 200 is the speed of the motors, on a scale from 0 (stopped) to 255 (full speed).
long lireDistanceCm() {
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
long duree = pulseIn(ECHO, HIGH, 30000UL);
if (duree == 0) return 999;
return duree * 0.034 / 2;
}
This is the “measure the distance” recipe. The robot sends a tiny beep (it turns TRIG on for a short moment), then it times how long the echo takes to come back (ECHO). The longer the echo takes, the farther away the wall is. The last line turns that time into centimeters. And if it hears no echo at all, it answers 999 — its way of saying “I don't see anything ahead, the way is clear”.
void avancer() {
moteur1.run(BACKWARD);
moteur2.run(FORWARD);
}
void reculer() {
moteur1.run(FORWARD);
moteur2.run(BACKWARD);
}
void pivoter() {
moteur1.run(FORWARD);
moteur2.run(FORWARD);
}
void arret() {
moteur1.run(RELEASE);
moteur2.run(RELEASE);
}
Here are the robot's legs: four little recipes for moving. avancer = straight ahead, reculer = backward, pivoter = turn in place, and arret = stop (we release the motors). The words BACKWARD/FORWARD have been set on purpose to match the way the motors are mounted on YOUR robot: what matters is that “avancer” really does make it go straight ahead.
void setup() {
Serial.begin(9600);
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT);
moteur1.setSpeed(VITESSE);
moteur2.setSpeed(VITESSE);
setup is what the robot does just ONCE when it wakes up, when we switch it on. It opens a line of conversation with the computer (so it can write us messages), it explains that TRIG is for talking and ECHO is for listening, then it sets the speed of the two motors.
Serial.println(F("=== Demarrage ==="));
long test = lireDistanceCm();
if (test >= 999) {
Serial.println(F("ATTENTION: capteur muet (AUCUN ECHO)."));
Serial.println(F("-> corrige Trig/Echo / 5V / GND, le robot n'evitera RIEN."));
} else {
Serial.print(F("Capteur OK, distance initiale: "));
Serial.print(test);
Serial.println(F(" cm"));
}
delay(1500);
}
Before moving, the robot does a little eye test: it measures the distance once. If the sensor doesn't answer (999), it writes a warning message to tell you it won't dodge anything (to check: the wires and the connection). Otherwise it writes “Capteur OK” with the distance. Then it waits 1.5 seconds, long enough for you to read the message and put the robot on the ground.
void loop() {
long distance = lireDistanceCm();
Serial.println(distance);
loop is what the robot repeats in a loop, over and over, very fast, the whole time it's switched on — it's its way of thinking all the time. On each go-around, it measures the distance in front of it and writes it down (so we can see it on the computer).
if (distance < SEUIL_CM) {
arret(); delay(150);
reculer(); delay(400);
arret(); delay(150);
pivoter();
unsigned long debut = millis();
while (lireDistanceCm() < SEUIL_CM && millis() - debut < 2000) {
delay(50);
}
arret(); delay(150);
} else {
avancer();
}
}
Then it makes a decision. IF an obstacle is too close (less than 20 cm): it stops, backs up a little, stops again, then pivots (turns in place) until the way is clear — but 2 seconds at most, so it doesn't spin forever. OTHERWISE, the way is clear, so it goes straight ahead. And since all of this repeats dozens of times per second, the robot looks like it's wandering around all by itself, avoiding the walls!
And there you have it — that's what programming is: giving the robot a list of really simple instructions that it follows very fast. Soon, you'll be able to change these numbers yourself: try a different VITESSE, or a bigger SEUIL_CM, and watch how your Mark I behaves!
See the full code (to copy)
#include <AFMotor_R4.h>
AF_DCMotor moteur1(1);
AF_DCMotor moteur2(2);
#define TRIG A0
#define ECHO A1
const int SEUIL_CM = 20;
const int VITESSE = 200;
long lireDistanceCm() {
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
long duree = pulseIn(ECHO, HIGH, 30000UL);
if (duree == 0) return 999;
return duree * 0.034 / 2;
}
void avancer() {
moteur1.run(BACKWARD);
moteur2.run(FORWARD);
}
void reculer() {
moteur1.run(FORWARD);
moteur2.run(BACKWARD);
}
void pivoter() {
moteur1.run(FORWARD);
moteur2.run(FORWARD);
}
void arret() {
moteur1.run(RELEASE);
moteur2.run(RELEASE);
}
void setup() {
Serial.begin(9600);
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT);
moteur1.setSpeed(VITESSE);
moteur2.setSpeed(VITESSE);
Serial.println(F("=== Demarrage ==="));
long test = lireDistanceCm();
if (test >= 999) {
Serial.println(F("ATTENTION: capteur muet (AUCUN ECHO)."));
Serial.println(F("-> corrige Trig/Echo / 5V / GND, le robot n'evitera RIEN."));
} else {
Serial.print(F("Capteur OK, distance initiale: "));
Serial.print(test);
Serial.println(F(" cm"));
}
delay(1500);
}
void loop() {
long distance = lireDistanceCm();
Serial.println(distance);
if (distance < SEUIL_CM) {
arret(); delay(150);
reculer(); delay(400);
arret(); delay(150);
pivoter();
unsigned long debut = millis();
while (lireDistanceCm() < SEUIL_CM && millis() - debut < 2000) {
delay(50);
}
arret(); delay(150);
} else {
avancer();
}
}