Community project

Moon Rover Controller

Arduino
Photo of Moon Rover Controller
Generated with AI

Soren de loose

Published October 29, 2026

Build a tracked rover controlled by an Arduino that navigates autonomously using ultrasonic distance sensing. This project combines motor control, obstacle detection, and user feedback through an LCD display, 7-segment digit display, and buzzer to create a small autonomous vehicle that can drive, pause, and turn based on what it detects ahead.

The guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions starting with the caterpillar track chassis and progressing through motor wiring, sensor integration, and display connections. Firmware is included to handle motor speed control via PWM, distance measurement, state management, and user input through push buttons, allowing the rover to operate in multiple modes from startup through autonomous driving.

Wiring diagram

Loading diagram…

Gather all the parts

QtyComponent
1

Whadda WPI409 L298N dubbele motorbesturing

L298N motorbesturing, motorvoeding VMS 5-35 V. Verwijder 5V EN jumper voor externe 5V-logica; verwijder ENA en ENB jumpers voor PWM; CSA en CSB met hun jumpers naar GND. Maximale stroom 2 A per kanaal, daadwerkelijke grens door koeling en spanningsverlies lager.

1

HC-SR04

HC-SR04 ultrasonic distance sensor module with separate 40 kHz transmitter and receiver transducers, measuring 2 to 400 cm. A 10 us pulse on TRIG starts a measurement and ECHO stays high for the echo's round-trip time. This HC-SR04+ board runs from 3.3 V or 5 V.

1

Gele TT DC-tandwielmotor 3-6 V

Gangbaar voorbeeld Adafruit 3777 1:48, 3-6 V, gemeten circa 1,5 A bij blokkeren op 6 V; eigen gele motor kan afwijken. Controleer spanning en blokkeerstroom van de eigen motor vóór gebruik.

1

Gele TT DC-tandwielmotor 3-6 V

Gangbaar voorbeeld Adafruit 3777 1:48, 3-6 V, gemeten circa 1,5 A bij blokkeren op 6 V; eigen gele motor kan afwijken. Controleer spanning en blokkeerstroom van de eigen motor vóór gebruik.

1

Batterijhouder voor 4 AA-cellen

4 AA alkaline, 6 V

Vier alkaline AA-cellen in serie, nominaal 6 V.

1

Batterijhouder voor 4 AA-cellen met schakelaar

4 AA alkaline, 6 V

Tweede houder met ingebouwde blijvende aan-uit-schakelaar, vier alkaline AA-cellen in serie, nominaal 6 V.

1

Aan-uit schakelaar voor motorvoeding

Blijvende schakelaar, geschikt voor minstens 4 A bij 6 V DC. Controleer de aansluitingen en stroomwaarde van de eigen schakelaar vóór gebruik.

1

Push Button

Momentary push button switch

1

Push Button

Momentary push button switch

1

ALLNET 4duino B27 zoemermodule

5 V module met TTL ingang, volgens ALLNET B27 handleiding.

1

Joy-IT COM-LCD16x2

16x2 HD44780 LCD met 16-polige aansluiting, blauwe achtergrondverlichting; rechtstreeks op de Uno aangesloten.

1

220 Ω weerstand LCD-achtergrondlicht

220 Ω

Stroombegrenzing volgens de Joy-IT COM-LCD16x2 handleiding.

1

Pololu S7V8F5 5V step-up/step-down spanningsregelaar

5 V

Pololu 2123, vaste 5 V uitgang, ingang 2,7 tot 11,8 V. SHDN blijft los; standaard ingeschakeld. Controleer polariteit; geen ompoolbeveiliging.

1

Kingbright SC56-11GWA cijferdisplay

Groen display met gemeenschappelijke kathode. Alleen segmenten a tot f voor 0 en 1; g en punt blijven los.

1

1 kΩ weerstand segment a

1 kΩ

Begrenst de stroom door één displaysegment.

1

1 kΩ weerstand segment b

1 kΩ

Begrenst de stroom door één displaysegment.

1

1 kΩ weerstand segment c

1 kΩ

Begrenst de stroom door één displaysegment.

1

1 kΩ weerstand segment d

1 kΩ

Begrenst de stroom door één displaysegment.

1

1 kΩ weerstand segment e

1 kΩ

Begrenst de stroom door één displaysegment.

1

1 kΩ weerstand segment f

1 kΩ

Begrenst de stroom door één displaysegment.

1

10kΩ Potentiometer

10 kΩ linear (B10K) rotary potentiometer in the common 16 mm panel style: metal can, M7 threaded bushing for mounting through a panel, a 6 mm knurled split shaft for push-on knobs, and three PCB pins in a row 5 mm apart. Connect the outer pins across 3.3 V or 5 V and ground; the middle wiper pin then gives an adjustable voltage for an ADC input over about 300° of rotation.

Assemble it in 9 steps

1. Maak het rupsonderstel klaar

Print de Desert Planet Exploration Rover en bekijk waar de twee motoren, elektronica en batterijen passen. Monteer de twee gele TT-tandwielmotoren en controleer of de motorhuizen en assen passen. De exacte uitvoering en benodigde kracht zijn niet bevestigd. Maak zo nodig passende steunen of koppelingen. Forceer niets. Zet de Uno, driver en batterijen stevig en geïsoleerd vast, uit de buurt van de rupsen.

  • Controleer vóór aansluiten dat beide motoren geschikt zijn voor 6 V en dat hun stroom bij blokkeren binnen de mogelijkheden van de driver en motorvoeding blijft. De voorbeeldmotor trekt ongeveer 1,5 A per motor bij blokkeren; jouw motoren kunnen afwijken. Rijd niet als de aandrijving klemt.
  • De rover ziet geen trappen of tafelranden. Gebruik uitsluitend op een vlakke vloer onder toezicht.

2. Bedraad de motoren en afstandssensor

Haal USB los en verwijder alle batterijen. Gebruik de Whadda WPI409 in plaats van de Pololu motorbesturing. Verbind de linkermotor met de twee MOTORA-klemmen en de rechtermotor met de twee MOTORB-klemmen. Verwijder de 5V EN jumper en verbind de 5V-aansluiting van de WPI409 met Uno 5V. Verwijder de ENA- en ENB-jumpers en verbind ENA en ENB allebei met Uno D5. Verbind IN1 met D7, IN2 met D8, IN3 met D9 en IN4 met D10; verbind GND met Uno GND. Laat CSA en CSB elk met hun jumper met GND verbonden voor normaal gebruik zonder stroommeting. Zet de HC-SR04 vooraan en verbind VCC met 5V, GND met GND, TRIG met D2 en ECHO met D3.

  • Sluit motoren nooit rechtstreeks op de Uno aan. Zet en isoleer motorverbindingen stevig vast.

3. Plaats je breadboard en drukknoppen

Gebruik het breadboard voor de signalen en verbind de minbaan met Uno GND. Sluit van de startknop één kant op A3 en de andere op GND aan. Sluit van de stopknop één kant op A0 en de andere op GND aan. Bij vierpoots drukknoppen zitten de twee pootjes aan dezelfde kant al aan elkaar; gebruik twee tegenover elkaar liggende zijden en controleer zo nodig met een multimeter. De Uno verzorgt de benodigde interne weerstanden.

4. Sluit beide displays aan

Zet USB en beide batterijhouders uit en haal de batterijen eruit. Gebruik geen I2C-adapter. Sluit het Joy-IT LCD direct aan: pin 1 (VSS) naar GND, pin 2 (VDD) naar 5V, pin 4 (RS) naar D11, pin 5 (RW) naar GND, pin 6 (E) naar D12, pin 11 (D4) naar D13, pin 12 (D5) naar A2, pin 13 (D6) naar A4 en pin 14 (D7) naar A5. Laat LCD-pinnen 7 tot en met 10 leeg. LCD pin 15 gaat via de 220-ohmweerstand naar 5V, pin 16 naar GND. Verbind de twee buitenste pootjes van de 10k-potmeter met 5V en GND en het middelste met LCD pin 3 (VO). Draai voor leesbare tekst.

  • Controleer pinnummers op het scherm. Sluit de achtergrondverlichting niet zonder de 220-ohmweerstand aan.

5. Sluit het cijferdisplay aan

Verbind van de Kingbright SC56-11GWA pin 3 (gemeenschappelijke min) met GND. Verbind pin 6 en pin 4 elk via een eigen 1k-ohmweerstand met 5V. Verbind pin 7, 2, 1 en 9 elk via een eigen 1k-ohmweerstand met D6. Sluit de weerstanden aan op de losse pootjes: nooit segmenten zonder weerstand. De overige displaypinnen blijven los. Het display toont 0 bij stoppen en 1 als de rover gestart is.

  • De aansluiting van de tweede motor en de schermen kan veranderd zijn; volg het nieuwe schema.

6. Sluit de zoemer aan

Verbind op de ALLNET B27 module +5V met Uno 5V, GND met GND en TTL met A1. De zoemer piept kort bij een obstakel of wanneer er geen betrouwbare echo is.

7. Sluit de twee AA-houders aan

Haal USB los en verwijder alle batterijen. Gebruik voor de motoren je bestaande 4-AA-houder. Verbind de plus via de losse aan-uit-schakelaar met VMS van de motorbesturing en de min met Uno GND en driver GND. Gebruik daarnaast een tweede 4-AA-houder met eigen aan-uit-schakelaar voor de Uno. Verbind de plus van deze tweede houder met VIN van de Pololu S7V8F5-regelaar, en de min met GND van de regelaar en Uno GND. Laat SHDN van de regelaar los. Plaats in elke houder vier AA-alkalinecellen volgens de plus- en mintekens. Zet alleen de tweede houder aan en controleer met een multimeter dat VOUT ten opzichte van GND ongeveer 5 V is. Schakel de houder uit en verbind dan VOUT met de 5V-pin van de Uno.

  • Sluit de 4-AA-houder nooit rechtstreeks aan op de 5V-pin of ronde voedingsaansluiting van de Uno. Verwissel plus en min niet; dit kan schade veroorzaken. De regelaar maakt een stabiele 5 V.
  • De losse schakelaar schakelt alleen de motoren uit. Schakel de tweede houder ook uit om de Uno uit te zetten. Koppel VOUT van de regelaar volledig los van Uno 5V voordat je USB aansluit; sluit nooit beide voedingen tegelijk aan.
  • Gebruik geen breadboard voor de motorstroom. Controleer dat houder, bedrading en schakelaar minstens de motorstroom aankunnen; de schakelaar minimaal 4 A bij 6 V DC. De L298N verliest spanning en wordt warm; met vier AA-cellen kunnen de motoren te weinig kracht hebben. Bij belasting kan de voeding onder de minimale 5 V zakken. Laad alkalinebatterijen nooit op.

8. Test met opgeheven rupsen

Steun de rover zodat beide rupsen vrij hangen. Zet na het installeren van de firmware de Uno-voeding aan en daarna de motorvoeding. Bij inschakelen toont het LCD GESTOPT, het cijferdisplay 0 en blijven motoren uit. Druk op start; het LCD toont START OVER 5 SEC en na vijf seconden kunnen de motoren draaien. Houd een vlak voorwerp eerst op 50 cm en daarna binnen 25 cm voor de sensor. De rover moet eerst vooruit draaien, dan piepen en draaien. Druk op stop en controleer dat de motoren stoppen en GESTOPT verschijnt. Schakel beide voedingen uit en haal de USB-kabel los voordat je iets aanpast. Draait een kant verkeerd, verwissel dan alleen de twee motordraden van die kant.

  • De stopknop is geen noodstop of stroomschakelaar. Bij geen echo stopt de rover; druk opnieuw op start om na controle te proberen.

9. Probeer op de vloer

Gebruik een vlakke vrije binnenvloer met brede kartonnen obstakels en houd ruimte rondom vrij. Blijf bij de rover. Schakel beide voedingen uit als iets klemt. Gebruik geen trappen, randen of zachte of smalle obstakels als test.

Review all connections

1. Connections between "driver" and "Arduino"

FunctiondriverArduino
power5V5V
groundGNDGND
powerVMS → Aan-uit schakelaar voor motorvoeding OUTEXT
pwmENAGPIO 5
pwmENB → Whadda WPI409 L298N dubbele motorbesturing ENAEXT
digitalIN1GPIO 7
digitalIN2GPIO 8
digitalIN3GPIO 9
digitalIN4GPIO 10

2. Connections between "motor_switch" and "Arduino"

Functionmotor_switchArduino
powerIN → Batterijhouder voor 4 AA-cellen VOUTEXT

3. Connections between "motor_battery" and "Arduino"

Functionmotor_batteryArduino
groundGNDGND

4. Connections between "logic_battery" and "Arduino"

Functionlogic_batteryArduino
powerVOUT → Pololu S7V8F5 5V step-up/step-down spanningsregelaar VINEXT
groundGNDGND

5. Connections between "logic_regulator" and "Arduino"

Functionlogic_regulatorArduino
groundGNDGND
powerVOUT5V

6. Connections between "left_motor" and "Arduino"

Functionleft_motorArduino
powerM1 → Whadda WPI409 L298N dubbele motorbesturing MOTORA1EXT
powerM2 → Whadda WPI409 L298N dubbele motorbesturing MOTORA2EXT

7. Connections between "right_motor" and "Arduino"

Functionright_motorArduino
powerM1 → Whadda WPI409 L298N dubbele motorbesturing MOTORB1EXT
powerM2 → Whadda WPI409 L298N dubbele motorbesturing MOTORB2EXT

8. Connections between "sonar" and "Arduino"

FunctionsonarArduino
powerVCC5V
groundGNDGND
digitalTRIGGPIO 2
digitalECHOGPIO 3

9. Connections between "buzzer" and "Arduino"

FunctionbuzzerArduino
power+5V5V
groundGNDGND
digitalTTLGPIO 15

10. Connections between "start_button" and "Arduino"

Functionstart_buttonArduino
groundGNDGND
digitalSIGNALGPIO 17

11. Connections between "stop_button" and "Arduino"

Functionstop_buttonArduino
groundGNDGND
digitalSIGNALGPIO 14

12. Connections between "display" and "Arduino"

FunctiondisplayArduino
ground3_COMGND
digital7_a → 1 kΩ weerstand segment a 1EXT
digital6_b → 1 kΩ weerstand segment b 1EXT
digital4_c → 1 kΩ weerstand segment c 1EXT
digital2_d → 1 kΩ weerstand segment d 1EXT
digital1_e → 1 kΩ weerstand segment e 1EXT
digital9_f → 1 kΩ weerstand segment f 1EXT

13. Connections between "r_a" and "Arduino"

Functionr_aArduino
digital2GPIO 6

14. Connections between "r_b" and "Arduino"

Functionr_bArduino
power25V

15. Connections between "r_c" and "Arduino"

Functionr_cArduino
power25V

16. Connections between "r_d" and "Arduino"

Functionr_dArduino
digital2 → 1 kΩ weerstand segment a 2EXT

17. Connections between "r_e" and "Arduino"

Functionr_eArduino
digital2 → 1 kΩ weerstand segment a 2EXT

18. Connections between "r_f" and "Arduino"

Functionr_fArduino
digital2 → 1 kΩ weerstand segment a 2EXT

19. Connections between "lcd" and "Arduino"

FunctionlcdArduino
groundVSSGND
powerVDD5V
analogVO → 10kΩ Potentiometer WIPEREXT
digitalRSGPIO 11
groundRWGND
digitalEGPIO 12
digitalD4GPIO 13
digitalD5GPIO 16
digitalD6GPIO 18
digitalD7GPIO 19
powerLED+ → 220 Ω weerstand LCD-achtergrondlicht 1EXT
groundLED-GND

20. Connections between "contrast" and "Arduino"

FunctioncontrastArduino
groundGNDGND
powerVCC5V

21. Connections between "lcd_resistor" and "Arduino"

Functionlcd_resistorArduino
power25V

Deploy the firmware

#include <Arduino.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(11, 12, 13, A2, A4, A5);
int lcdShown=-1;
// ENA en ENB delen PWM D5; de vier IN-pinnen bepalen de richting.
constexpr uint8_t TRIG=2, ECHO=3, PWM_ENABLE=5;
constexpr uint8_t AIN1=7, AIN2=8, BIN1=9, BIN2=10;
constexpr uint8_t START_BTN=A3, STOP_BTN=A0, BUZZER=A1;
constexpr uint8_t SEG_ZERO=6; // a,d,e,f via individual resistors; b,c permanently on
int digitShown=-1;
constexpr int SPEED=255, TURN_SPEED=255;
enum class State {Stopped, Startup, Drive, Pause, Turn, Settle};
State state=State::Stopped;
unsigned long since=0, lastPing=0, buzzerUntil=0;
bool turnLeft=false, prevStart=false, prevStop=false;
void stopMotors(){
 analogWrite(PWM_ENABLE,0);
 digitalWrite(AIN1,LOW); digitalWrite(AIN2,LOW);
 digitalWrite(BIN1,LOW); digitalWrite(BIN2,LOW);
}
void drive(int left,int right){
 analogWrite(PWM_ENABLE,0);
 digitalWrite(AIN1,left>0); digitalWrite(AIN2,left<0);
 digitalWrite(BIN1,right>0); digitalWrite(BIN2,right<0);
 analogWrite(PWM_ENABLE,max(abs(left),abs(right)));
}
void enter(State next){state=next; since=millis();}
void showLcd(){
 if(lcdShown==static_cast<int>(state))return;
 lcdShown=static_cast<int>(state);
 lcd.setCursor(0,0); lcd.print(F("MOON ROVER      "));
 lcd.setCursor(0,1);
 switch(state){
 case State::Stopped: lcd.print(F("GESTOPT         ")); break;
 case State::Startup: lcd.print(F("START OVER 5 SEC")); break;
 case State::Drive: lcd.print(F("RIJDT           ")); break;
 case State::Pause: lcd.print(F("OBSTAKEL        ")); break;
 case State::Turn: lcd.print(F("DRAAIT          ")); break;
 case State::Settle: lcd.print(F("OPNIEUW METEN   ")); break;
 }
}
unsigned int distanceCm(){digitalWrite(TRIG,LOW); delayMicroseconds(2); digitalWrite(TRIG,HIGH); delayMicroseconds(10); digitalWrite(TRIG,LOW); unsigned long echo=pulseIn(ECHO,HIGH,25000UL); return (echo<116 || echo>23200)?0:echo/58;}
void beep(){digitalWrite(BUZZER,HIGH); buzzerUntil=millis()+150;}
void setup(){
 const uint8_t outputs[]={PWM_ENABLE,AIN1,AIN2,BIN1,BIN2};
 for(uint8_t p:outputs){digitalWrite(p,LOW); pinMode(p,OUTPUT);} stopMotors();
 pinMode(TRIG,OUTPUT); digitalWrite(TRIG,LOW); pinMode(ECHO,INPUT);
 pinMode(START_BTN,INPUT_PULLUP); pinMode(STOP_BTN,INPUT_PULLUP);
 digitalWrite(BUZZER,LOW); pinMode(BUZZER,OUTPUT);
 digitalWrite(SEG_ZERO,LOW); pinMode(SEG_ZERO,OUTPUT);
 lcd.begin(16,2); showLcd();
 Serial.begin(115200); Serial.println(F("Stopped. Press START; movement begins after 5 seconds."));
}
void loop(){
 unsigned long now=millis();
 if(buzzerUntil && (long)(now-buzzerUntil)>=0){digitalWrite(BUZZER,LOW); buzzerUntil=0;}
 bool start=digitalRead(START_BTN)==LOW, stop=digitalRead(STOP_BTN)==LOW;
 // Stop has priority, even if start is held. Require a new press after stopping.
 if(stop){stopMotors(); if(!prevStop) Serial.println(F("Stopped")); enter(State::Stopped); digitalWrite(BUZZER,LOW); buzzerUntil=0;}
 else if(start && !prevStart && state==State::Stopped){stopMotors(); enter(State::Startup); Serial.println(F("Starting in 5 seconds"));}
 prevStart=start; prevStop=stop;
 int digit = state==State::Stopped ? 0 : 1;
 if(digit!=digitShown){ digitShown=digit; digitalWrite(SEG_ZERO, digit==0 ? HIGH : LOW); }
 showLcd();
 switch(state){
  case State::Stopped: break;
  case State::Startup: if(now-since>=5000)enter(State::Drive); break;
  case State::Drive:
   if(now-lastPing<80)break; lastPing=now;
   {unsigned int cm=distanceCm();
    if(cm==0){stopMotors(); enter(State::Stopped); beep(); Serial.println(F("No echo: stopped, press start to retry"));}
    else if(cm<=25){stopMotors(); beep(); enter(State::Pause);}
    else drive(SPEED,SPEED);
   } break;
  case State::Pause: if(now-since>=200){turnLeft=!turnLeft; drive(turnLeft?-TURN_SPEED:TURN_SPEED,turnLeft?TURN_SPEED:-TURN_SPEED); enter(State::Turn);} break;
  case State::Turn: if(now-since>=450){stopMotors(); enter(State::Settle);} break;
  case State::Settle: if(now-since>=250)enter(State::Drive); break;
 }
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace, diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik