PTENES
MODULE 1.6 · PROJECT

🛞 Project: obstacle-avoiding rover

The project that wraps up Track 1 brings it all together: the HC-SR04 senses the wall, the ESP32 decides using a state machine and two DC motors steer the rover around it. You'll plan on paper, build, test each part on its own and only then let the robot loose on the floor.

6
Topics
Beginner
Level
Project
Type
Complete kit
Hardware required
0%
0 of 0 topics read · Section 1 of 6

Detailed content

1

📝 Plan the rover on paper

Before cutting the chassis, answer three questions on paper: what components the robot has, which pin each wire connects to e how much current everything draws. The answers become the block diagram, pin map, and power budget below. Module 1.5 already used these same pins for the motors and servo, so you can use the sketches from there to test them here.

🌱 New here?

A block diagram shows the system components as boxes and what passes between them as arrows, without worrying about wires. The pin map shows which ESP32 GPIO connects to each component's terminal. The power budget adds up each component's current draw so you can choose a battery and regulator with enough headroom.

SENSEHC-SR04+ servo (neck) THINKESP32state machine ACTTB6612 driver+ 2 TT motors cmanglePWM POWER: 2× 18650 + switch5 V buck converter · driver's VM 5 V5 VVM

How to read this map: the green arrows show information (distance comes from the sensor, the angle goes to the servo, and PWM goes to the driver). The amber dashed arrows show power, which comes from one place at the bottom. The motors get battery voltage through VM; everything else gets 5 V from the regulator. It's the sense, think, act cycle from Module 1.1, now with real components.

Pin map (used throughout this module)

ESP32 pinTB6612FNGDRV8833Purpose
GPIO 25PWMAfreeleft motor speed
GPIO 26 · 27AIN1 · AIN2AIN1 · AIN2left motor direction (and PWM on the DRV8833)
GPIO 33PWMBfreeright motor speed
GPIO 32 · 4BIN1 · BIN2BIN1 · BIN2right motor direction (and PWM on the DRV8833)
GPIO 13STBYnSLEEP / EEPenables the driver (high level)
GPIO 18HC-SR04 TRIGtriggers the sound pulse
GPIO 19HC-SR04 ECHO, through the voltage divider (1 kΩ from ECHO to the pin; 2 kΩ from the pin to GND)converts the 5 V echo to 3.3 V
GPIO 23servo, orange wireturns the sensor
GPIO 0 (BOOT)button already on the boardpauses and resumes
3V3VCCnot useddriver logic
5V / VIN5 V buck output, also connected to the HC-SR04's VCC and the servo's red wirepowers the electronics
GNDCommon GND: driver, buck converter, battery (−), HC-SR04, and servo's brown wiresingle reference point
Power budgetWhile moving (approx.)Peak (approx.)
ESP32 without Wi-Fi50 to 100 mA250 mA with Wi-Fi
HC-SR0415 mA15 mA
SG90 servoalmost nothing while idlea few hundred mA while moving
2 TT motors300 to 500 mA (combined)close to 1 A each, at startup or when stalled
Conclusiona buck rated for at least 2 A for the 5 V; a battery that can handle 2 A or more peak. Measure yours with a multimeter: the numbers vary by model.

Why learn this

Planning prevents you from having to redo the wiring or replace parts halfway through. Engineers who build commercial robots create exactly these three documents before the first prototype; with them in hand, you can also ask for help on a forum in a way that helps someone understand your robot.

Key concepts

Block diagram
what passes between the parts
Pin map
one wire, one destination
Power consumption
average and peak
Headroom
regulator rated above the peak
2

🔧 Assemble the chassis and wiring

The chassis can be made of thick cardboard, MDF, or acrylic: what matters is that it is rigid and has room for everything to be secured. Motors at the back, a caster wheel at the front (or the other way around, if your kit comes that way), battery low and near the wheel axle, ESP32 and breadboard on top, sensor at the front with a clear field of view.

ESP32 GPIO 25GPIO 26GPIO 27GPIO 33GPIO 32GPIO 4GPIO 13GPIO 23GPIO 18GPIO 19 5V/VIN ← buck TB6612FNG PWMAAIN1AIN2PWMBBIN1BIN2STBY AO1/AO2BO1/BO2 VM: battery · VCC: 3V3 · GNDleft right servo (orange) TRIGECHOHC-SR04 1 kΩ 2 kΩGND

How to read this map: each horizontal green line connects a GPIO to the driver input at the same height, so you can check each wire one by one with your finger. The circles on the right are the motors. At the bottom, the amber line goes to the servo, and the sensor wires pass through the voltage divider: ECHO comes out of the HC-SR04 at 5 V, passes through the 1 kΩ resistor, and reaches GPIO 19 at about 3.3 V, because the 2 kΩ resistor diverts the rest to GND. The power connections are shown in the pin map and in the module 1.5 figure.

Assembly steps

1

Solder wires and capacitors to the motors

Two wires per motor (red and black) and a 100 nF ceramic capacitor across the terminals. Do this before mounting them: they are hard to reach afterward.

2

Mount the motors, wheels, and caster wheel

Keep the motors parallel and aligned, using M3 screws or cable ties. Turn the wheels by hand: they must not scrape against the chassis.

3

Battery, switch, and buck

Place the battery holder with BMS underneath, near the axle; put the switch on the positive wire. Use a multimeter to adjust the buck to 5.0 V before connecting any board to it.

4

Breadboard, ESP32, and driver

Use double-sided tape to attach the breadboard; place the ESP32 and driver on it. Make the common GND rail first, then add the signal wires, following the table.

5

Servo and sensor at the front

Attach the servo at the front, with the HC-SR04 secured to its arm, about 8 to 10 cm above the ground and pointing straight ahead. The sensor’s “eyes” must not see the chassis itself.

✓ Wiring that lasts

  • ✓Use consistent colors: red = positive, black = GND, other colors = signal.
  • ✓Strain relief: secure the wire with a cable tie before the terminal so a tug does not reach the solder joint.
  • ✓Use only the wire length you need, with no extra loops near the wheels.

✗ Intermittent failure guaranteed

  • ✗A loose jumper on the breadboard atop a vibrating chassis.
  • ✗A loose battery: it shifts the center of mass and pulls on the wires at every turn.
  • ✗HC-SR04 5 V ECHO connected directly to GPIO, without the voltage divider.

Why learn this

Loose wiring is the biggest source of intermittent failures in mobile robots: the problem appears only when the robot vibrates, disappears on the workbench, and takes an entire afternoon to track down. Assembling everything neatly (GND first, wires secured, weight low) prevents this from the start.

Key concepts

Center of mass
weight low, between the wheels
Strain relief
the tug does not reach the solder joint
Main switch
turns everything off at once
Divider on ECHO
5 V becomes 3.3 V
3

🧪 Test each part separately

If you upload the final code and the robot does not move, the problem could be in any of ten places. By testing one part at a time, in the right order, each test depends only on the ones that have already passed. It is the hardware equivalent of a unit test in programming.

1 powermultimeter 2 sensorcm in Serial 3 left motorwheels off the ground 4 right motorsame direction 5 servowithout shaking 6 everythingon the ground move up only when the step below has passed

How to read this map: Start at the bottom left. Power (amber) comes first because if it is wrong, all the other tests give false results. Test the motors one at a time so you know which one spins backward. The “everything on the ground” step only happens in topic 4.

Workbench checklist (step 1, with the switch on and no USB)

  • ✓Battery: between 7 and 8.4 V with two 18650 cells (or close to 5 V with four fully charged NiMH cells).
  • ✓Buck output: 5.0 V. ESP32 3V3 pin: about 3.3 V.
  • ✓Continuity (beep) between the GND on the ESP32, driver, and sensor, and the battery negative terminal.

Goal (step 2): see the distance in Serial Monitor and confirm that the ECHO voltage divider works. This also runs in Wokwi, which has the HC-SR04.

teste-sensor.ino
// Isolated HC-SR04 test (ESP32, esp32 package 3.x)
const int PINO_TRIG = 18, PINO_ECHO = 19;   // ECHO through 1k/2k divider
void setup() {
  Serial.begin(115200);
  pinMode(PINO_TRIG, OUTPUT); digitalWrite(PINO_TRIG, LOW);
  pinMode(PINO_ECHO, INPUT);
}
void loop() {
  digitalWrite(PINO_TRIG, HIGH); delayMicroseconds(10); digitalWrite(PINO_TRIG, LOW);
  unsigned long us = pulseIn(PINO_ECHO, HIGH, 30000UL);  // waits at most 30 ms
  if (us == 0) Serial.println("no echo (nothing up to ~4 m, or ECHO wire disconnected)");
  else Serial.printf("distance: %.1f cm\n", us / 58.0);  // 58 us per cm, round trip
  delay(100);
}

How to check

  • ✓Hold your hand 20 cm from the sensor: Serial shows values between 18 and 22 cm, changing as you move your hand closer or farther away.
  • ✓Steps 3 and 4: upload the motores-teste.ino from module 1.5 (same pins) with the robot suspended; with the DRV8833, use the sketch’s test menu in topic 4. Step 5: the servo-olhar.ino.
  • ✗Always “no echo”? Check for 5 V at the sensor’s VCC and check the voltage divider resistors (1 kΩ on the sensor side).

Why learn this

When you test everything at once, you don’t know which part failed. With the ladder, each problem shows up on its own, at its own step, and the fix takes minutes. This habit applies to any robot, from a rover to an AI-powered arm.

Key concepts

Unit test
one part at a time
Order
power first
Wheels off the ground
a test with no runaway
Checklist
the same for every build
4

🧠 Obstacle avoidance logic with a state machine

A “if the wall is close, turn” inside the loop() works until the robot needs to do two things in sequence (stop, reverse, look, turn). Then the code becomes a tangle of if e delay. The solution is a state machine: the robot is always in a named state, does only what that state tells it to, and changes state when a condition occurs.

🌱 New here?

A state is a behavior mode (MOVE, STOP...). A transition is the rule that takes it from one state to another (“distance less than 25 cm”). In C++, enum creates the list of state names, and switch selects the code block for the current state. millis() tells you how long the board has been powered on; with it, the robot measures how long it has been in a state without blocking with delay().

MOVE STOP REVERSE LOOK TURN dist < 25 cm200 ms350 msmeasuredboth sidesturn complete: move again

How to read this map: each capsule is a state and each arrow is a transition, with its condition written beside it. Notice that only one condition depends on the sensor (dist < 25 cm); the others depend on time. The cyan arrow closes the loop. In the code below, each capsule is a case of the switch.

Goal: full rover. It starts paused: the board’s BOOT button toggles between paused and moving, and while paused, you can run tests through the Serial Monitor (s, e, d, c). Two lines at the top select the driver (TB6612FNG or DRV8833) and whether a servo is present. First install the ESP32Servo library (module 1.5).

rover-desvia.ino
// Obstacle-avoiding rover - Robotics from Zero to Expert, project 1.6
// ESP32 + HC-SR04 + 2 DC motors (TB6612FNG or DRV8833) + optional servo
// esp32 package 3.x (ledcAttach / ledcWrite) and ESP32Servo library
#include <ESP32Servo.h>
// ---------- choose the hardware ----------
#define USAR_DRV8833 0   // 0 = TB6612FNG, 1 = DRV8833
#define USAR_SERVO   1   // 0 = fixed sensor, facing forward
// ---------- pins (map from topic 1) ----------
const int PINO_PWMA = 25, PINO_AIN1 = 26, PINO_AIN2 = 27;  // left motor
const int PINO_PWMB = 33, PINO_BIN1 = 32, PINO_BIN2 = 4;   // right motor
const int PINO_STBY = 13;          // STBY (TB6612) or nSLEEP (DRV8833)
const int PINO_TRIG = 18, PINO_ECHO = 19;  // ECHO through 1k/2k divider
const int PINO_SERVO = 23;
const int PINO_BOTAO = 0;          // board BOOT button: pause / resume
// ---------- adjustments (topic 5) ----------
const int PWM_FREQ = 20000, PWM_BITS = 8;
const int VEL_MAX = 180;   // limit: around 6 V average with 2x 18650 (4x AA NiMH: 255)
const int VEL_MIN = 90;    // TT motor dead zone
const int VEL_FRENTE = 170, VEL_GIRO = 160;
const int AJUSTE_ESQ = 0, AJUSTE_DIR = 0;  // add +10 to the weaker motor
const float DIST_PARAR_CM = 25.0;
const unsigned long T_PARAR = 200, T_RE = 350, T_GIRO_90 = 380;  // em ms
#if USAR_SERVO
Servo cabeca;
#endif
enum Estado { ANDAR, PARAR, RE, OLHAR, VIRAR };
const char* NOME[] = {"ANDAR", "PARAR", "RE", "OLHAR", "VIRAR"};
Estado estado = PARAR;
unsigned long inicioEstado = 0, ultimaLeitura = 0, tGiro = T_GIRO_90;
int ladoGiro = 1;            // +1 = right, -1 = left
bool pausado = true;         // starts stopped: press BOOT to move
float hist[3] = {400, 400, 400}; int iHist = 0;  // last 3 readings
// ---------- motors ----------
void motor(int in1, int in2, int pinoPwm, int vel) {
  vel = constrain(vel, -VEL_MAX, VEL_MAX);
  int forca = abs(vel);
  if (forca > 0 && forca < VEL_MIN) forca = VEL_MIN;
#if USAR_DRV8833
  ledcWrite(in1, vel > 0 ? forca : 0);       // DRV8833: PWM on both input pins
  ledcWrite(in2, vel < 0 ? forca : 0);
#else
  digitalWrite(in1, vel > 0 ? HIGH : LOW);   // TB6612: direction on IN, power on PWM
  digitalWrite(in2, vel < 0 ? HIGH : LOW);
  ledcWrite(pinoPwm, forca);
#endif
}
void andar(int esq, int dir) {   // -255 to 255 for each wheel
  if (esq != 0) esq += (esq > 0) ? AJUSTE_ESQ : -AJUSTE_ESQ;
  if (dir != 0) dir += (dir > 0) ? AJUSTE_DIR : -AJUSTE_DIR;
  motor(PINO_AIN1, PINO_AIN2, PINO_PWMA, esq);
  motor(PINO_BIN1, PINO_BIN2, PINO_PWMB, dir);
}
// ---------- sensor ----------
float medirCm() {
  digitalWrite(PINO_TRIG, HIGH); delayMicroseconds(10); digitalWrite(PINO_TRIG, LOW);
  unsigned long us = pulseIn(PINO_ECHO, HIGH, 30000UL);
  return (us == 0) ? 400.0 : us / 58.0;    // no echo = nothing nearby (around 4 m)
}
float mediana3(float a, float b, float c) { return max(min(a, b), min(max(a, b), c)); }
float distanciaAndando() {               // 1 new reading + the 2 previous ones
  hist[iHist] = medirCm(); iHist = (iHist + 1) % 3;
  return mediana3(hist[0], hist[1], hist[2]);
}
float distanciaParado() {                // 3 new readings, 60 ms apart
  float a = medirCm(); delay(60);
  float b = medirCm(); delay(60);
  return mediana3(a, b, medirCm());
}
// ---------- state machine ----------
void mudar(Estado novo) {
  estado = novo; inicioEstado = millis();
  Serial.printf("-> %s\n", NOME[novo]);
}
void escolherLado() {                    // if the servo looks the wrong way, swap 20 and 160
#if USAR_SERVO
  cabeca.write(20);  delay(400); float dDir = distanciaParado();
  cabeca.write(160); delay(600); float dEsq = distanciaParado();
  cabeca.write(90);  delay(300);
  Serial.printf("right %.0f cm | left %.0f cm\n", dDir, dEsq);
  ladoGiro = (dDir >= dEsq) ? 1 : -1;
  tGiro = (max(dDir, dEsq) < DIST_PARAR_CM) ? 2 * T_GIRO_90 : T_GIRO_90;  // dead end: turn around
#else
  ladoGiro = -ladoGiro;                  // without a servo: alternate sides
  tGiro = T_GIRO_90;
#endif
}
void testar(char c) {                   // isolated tests through the Serial Monitor
  if (c == 's') Serial.printf("distance: %.1f cm\n", distanciaParado());
  if (c == 'e') { Serial.println("left motor, forward"); andar(VEL_FRENTE, 0); delay(1000); andar(0, 0); }
  if (c == 'd') { Serial.println("right motor, forward"); andar(0, VEL_FRENTE); delay(1000); andar(0, 0); }
#if USAR_SERVO
  if (c == 'c') { Serial.println("servo: right, left, forward"); cabeca.write(20); delay(600); cabeca.write(160); delay(800); cabeca.write(90); }
#endif
}
void setup() {
  Serial.begin(115200);
  pinMode(PINO_BOTAO, INPUT_PULLUP);
  pinMode(PINO_STBY, OUTPUT); digitalWrite(PINO_STBY, HIGH);
#if USAR_DRV8833
  ledcAttach(PINO_AIN1, PWM_FREQ, PWM_BITS); ledcAttach(PINO_AIN2, PWM_FREQ, PWM_BITS);
  ledcAttach(PINO_BIN1, PWM_FREQ, PWM_BITS); ledcAttach(PINO_BIN2, PWM_FREQ, PWM_BITS);
#else
  pinMode(PINO_AIN1, OUTPUT); pinMode(PINO_AIN2, OUTPUT);
  pinMode(PINO_BIN1, OUTPUT); pinMode(PINO_BIN2, OUTPUT);
  ledcAttach(PINO_PWMA, PWM_FREQ, PWM_BITS); ledcAttach(PINO_PWMB, PWM_FREQ, PWM_BITS);
#endif
  andar(0, 0);
  pinMode(PINO_TRIG, OUTPUT); digitalWrite(PINO_TRIG, LOW); pinMode(PINO_ECHO, INPUT);
#if USAR_SERVO
  ESP32PWM::allocateTimer(3);            // servo on a timer separate from the motors
  cabeca.setPeriodHertz(50);
  cabeca.attach(PINO_SERVO, 500, 2400);
  cabeca.write(90);
#endif
  Serial.println("Rover ready and PAUSED. Press BOOT to move.");
  Serial.println("Paused, test: s=sensor e=left motor d=right motor c=servo");
}
void loop() {
  static bool botaoAntes = HIGH;
  bool botao = digitalRead(PINO_BOTAO);
  if (botaoAntes == HIGH && botao == LOW) {      // just pressed
    pausado = !pausado; andar(0, 0); delay(250);   // 250 ms filters out button bounce
    Serial.println(pausado ? "PAUSADO" : "ANDANDO");
    if (!pausado) { hist[0] = hist[1] = hist[2] = 400; mudar(ANDAR); }
  }
  botaoAntes = botao;
  if (pausado) { if (Serial.available()) testar(Serial.read()); return; }
  unsigned long agora = millis(), noEstado = agora - inicioEstado;
  switch (estado) {
    case ANDAR:
      andar(VEL_FRENTE, VEL_FRENTE);
      if (agora - ultimaLeitura >= 60) {          // HC-SR04: one reading every 60 ms
        ultimaLeitura = agora;
        if (distanciaAndando() < DIST_PARAR_CM) mudar(PARAR);
      }
      break;
    case PARAR:
      andar(0, 0);
      if (noEstado >= T_PARAR) mudar(RE);
      break;
    case RE:
      andar(-VEL_GIRO, -VEL_GIRO);
      if (noEstado >= T_RE) { andar(0, 0); mudar(OLHAR); }
      break;
    case OLHAR:
      escolherLado();                              // robot stopped for about 1.5 s
      mudar(VIRAR);
      break;
    case VIRAR:
      andar(VEL_GIRO * ladoGiro, -VEL_GIRO * ladoGiro);
      if (noEstado >= tGiro) {
        andar(0, 0);
        hist[0] = hist[1] = hist[2] = 400;         // discard old readings
        mudar(ANDAR);
      }
      break;
  }
}

How to check

  • ✓When it starts, the Serial Monitor displays “Rover ready and PAUSED”. Enter s, e, d, and c: each part responds on its own (this is step 6 of the ladder, still on the workbench).
  • ✓With the robot on the floor and BOOT pressed, it moves straight. When it reaches a box, the Serial Monitor displays “-> STOP”, “-> REVERSE”, “-> LOOK”, the two distances, and “-> TURN”, and the robot continues toward the more open side.
  • ✗Servo shaking or motor beeping only after you connect the servo? That’s a PWM timer conflict: check the line allocateTimer(3) and update ESP32Servo.
  • ✗Compilation error in ledcAttach? The installed esp32 package is older than 3.x: update it in Boards Manager.

💡 Test without motors in Wokwi

Wokwi simulates the ESP32, HC-SR04, servo, and button, but not the H-bridge. To test the logic without the robot, connect LEDs (with resistors) to pins 25 and 33 in place of the motors, drag the virtual sensor’s distance control, and watch the states in Serial.

Why learn this

State machines are how real robots organize behavior, from vacuum cleaners to self-driving cars. To add a new behavior (for example, “honk while reversing”), you change just one state without breaking the others. In Track 2, the same idea expands to parallel tasks with FreeRTOS.

Key concepts

State
named mode
Transition
condition that changes the mode
enum
list of names
switch + millis
one block per state, without blocking
5

🩺 Fine-tuning and common problems

Every first rover needs adjustments: the motors are never identical, the floor changes the friction, and the battery voltage drops with use. That’s what the numbers in the “adjustments” block at the top of the sketch are for. Use the Serial Monitor as a dashboard: it tells you what state the robot was in when the problem occurred.

SymptomLikely causeFix
Pulls to one sidea weaker motor or a wheel rubbingAJUSTE_ESQ or AJUSTE_DIR in increments of 10; check the wheels
Restarts when reversing or turningbrownout: reversing the motor draws a current spikecharged battery, capacitors, 2 A buck converter, common GND; confirm with the reset-reason.ino from 1.5
Doesn’t see the wallsensor tilted, too high, or a soft/slanted surface that scatters the echosensor level, 8–10 cm above the floor; increase DIST_PARAR_CM
Stops by itself in the middle of the roombrief false reading (noise on the sensor wire)keep the sensor wires away from the motor wires; check the voltage divider; the median filter already removes isolated spikes
Turns too far or not far enoughT_GIRO_90 depends on the floor and batteryadjust in 50 ms steps and test on the floor where it will run
Nothing movesit is paused, or STBY/nSLEEP is lowpress BOOT; measure 3.3 V at STBY

✓ Adjust methodically

  • ✓Change one number at a time and note the before and after.
  • ✓Always test on the same floor with a charged battery.
  • ✓Read Serial with the robot connected to the computer by a long cable, or suspended.

✗ Guesswork adjustments

  • ✗Changing three values at once and not knowing which one fixed it.
  • ✗Blaming the code before measuring the battery.
  • ✗Removing the limit VEL_MAX with 2× 18650 cells to “get more power”.

Why learn this

Knowing how to diagnose is the skill that stays with you after the rover goes on the shelf. The sequence “symptom, state in Serial, likely cause, one change, new test” is the same one you’ll use to tune the PID in Track 2 and navigation in Track 4.

Key concepts

Compensation
balances the motors
Threshold
distance that triggers the stop
Median
ignores erratic readings
Serial as a dashboard
status + numbers
6

🚀 Next steps: line, buzzer, and Wi-Fi

The rover is ready, but the chassis can do much more. Three small upgrades, each adding a new state or sensor, show how the same robot can grow with code and parts, without starting over. Each one also opens a module in Track 2.

YOUR ROVERTrack 1 complete 2 IR sensors: line followingPID in module 2.6 buzzer: alertsbeep in the REVERSE state Wi-Fi: phonecontrol in module 2.1

How to read this map: the green block is what you already have; the cyan blocks are independent additions that you can make in any order. None requires replacing the chassis, motors, or state machine: each adds a sensor, a state, or another task.

〰️ Line following

Point the two infrared modules in the kit (module 1.4) at the floor, one on each side of the black line. Create the FOLLOW state: if the left sensor sees black, turn left. In Track 2, the 5-sensor array and PID make this smooth.

🔊 Buzzer

A passive buzzer on an unused pin (25 and 33 are free with the DRV8833; with the TB6612, pin 17 works on the DevKit WROOM-32). In API 3.x, ledcAttach on the pin and ledcWriteTone(pino, 2000) play a beep; turn it on in the REVERSE state and turn it off with tone 0.

📶 Wi-Fi

The ESP32 creates a page with forward, reverse, and stop buttons, served to your phone. Module 2.1 shows you how, with one important safety rule: if the connection drops, the robot stops by itself.

💡 Document your rover

Record a short video of the robot avoiding obstacles, take a photo of the wiring, and save the sketch with your tuning values. Use “My Journey” to mark what you’ve completed and export your progress. This record is the start of your robotics portfolio, and it’s what a teacher asks for in a project-based assessment.

Why learn this

Watching the robot grow in small steps teaches you the way all robotics work: start with a working, tested base, then add things you can verify one at a time. That’s exactly how Track 2 turns this rover into a truly autonomous robot.

Key concepts

Expansion
one more sensor or state
Reuse
same chassis, new code
Available pins
plan where the next one will fit
Next level
Track 2: autonomous robot

✅ Module summary

✓
Plan first: block diagram, pin map, and power budget.
✓
Assemble in order: weight low down, common GND first, secure wires, voltage divider on ECHO.
✓
Test step by step: power, sensor, each motor, servo, and only then everything together.
✓
State machine: MOVE, STOP, REVERSE, LOOK, TURN, with enum, switch e millis().
✓
Adjust methodically: one number at a time, Serial as a dashboard, power before code.

To go further

Next track

Track 2 · Autonomous robot. Starts with module 2.1, phone control over Wi-Fi and BLE, then continues with IMU, encoders, PID, and telemetry through to a line follower.