03 Robotics · Sensing · Data Logging

Sensor Rover

A four-wheel rover that reads its environment, changes how it drives in response, shows everything live on a dashboard, and logs it all to an SD card — a mobile sensing platform, not just a moving robot.

ESP32TB6612FNGTT motors LDR · LM35 · waterHC-SR04 ST7735 TFTmicroSD
Assembled four-wheel rover with ultrasonic sensor and dashboard
brighter light → faster · water or obstacle → stop · everything logged

The idea.

Build a rover that does more than move — one that senses, reacts, and reports.

The goal was a mobile sensing platform: detect changes in its surroundings, change its behaviour in response, and communicate what it's sensing in real time. Light sets its speed, a water sensor and an ultrasonic sensor act as safety stops, temperature and distance stream to an on-board dashboard, and every reading is logged to an SD card for later.

How it works.

The ESP32 reads every sensor on a non-blocking loop, turns light into a target speed, watches for hazards, and drives the display, the motors, and the SD log together.

Features

DRIVE

Light-controlled speed

A photoresistor sets the driving speed on a smoothed curve — brighter surroundings drive faster, darker ones slower — across a tunable light window shown live as a percentage.

SAFETY

Water & obstacle stops

A water sensor halts the rover the moment it detects water; an ultrasonic sensor stops it at 15 cm from an obstacle and releases at 20 cm, so it doesn't stutter on the threshold.

DASHBOARD

Live TFT readout

A boxed "Rover Status" dashboard shows raw and interpreted values — light, temperature, distance, water, speed mode — with segmented bars for light level and motor speed.

DATA

SD logging

Every sample is written to a CSV on a microSD card — raw and interpreted values, motor state, and mode — sharing the SPI bus with the display.

STATUS

RGB indicator

An RGB LED signals the rover's state at a glance — running, warning, or stopped — without needing to read the screen.

ARCHITECTURE

Non-blocking & fail-safe

Built on a millis() loop with sensor filtering, smooth speed ramping, motors held off until init completes, and safety overrides that always win.

Hardware.

Multiple sensors and outputs sharing one ESP32 and a single SPI bus — part of what made integration the real challenge.

🧠
ESP32The controller running the whole non-blocking loop
TB6612FNG + 4 TT motorsMotor driver running two wheels per channel
💡
LDR (photoresistor)Reads light level — the input that sets driving speed
🌡
LM35Continuous temperature sensing
💧
Water sensorSafety override — stops the rover on contact with water
📳
HC-SR04 ultrasonicDistance sensing and obstacle stop
🖥
ST7735 TFT (1.8")The live status dashboard
💾
microSD moduleCSV data logging, sharing SPI with the display
Components laid out on the rover chassis
Components laid out on the rover chassis

Testing setup.

Each sensor and output proved on its own, then integrated one at a time — the only sane way to bring this many subsystems together.

A. Motor & light

Mapped light levels to motor speeds so the rover changed pace with its surroundings.

Rover under test, light-driven speed
Rover under test, light-driven speed

B. Temperature

Read the LM35 and showed changing temperature live on the TFT.

C. Water detection

Verified the rover stops on water and updates the display to match.

D. Ultrasonic

Tested obstacle detection and distance measurement.

E. Dashboard & logging

Confirmed the TFT dashboard and SD logging ran together while driving.

Live Rover Status dashboard on the TFT
Live Rover Status dashboard on the TFT

Troubleshooting.

Three problems shaped the final rover: a sensor that lied, motors that wouldn't start, and a board that ran out of pins.

Case 01

The rover kept stopping for no reason

Symptom

The motors would cut out and the dashboard flipped to a water warning — with no water anywhere near the sensor.

Root cause

The water sensor read HIGH when wet and LOW when dry — the opposite of the usual module default. My code assumed the default, so a dry sensor looked "wet," firing the safety override and killing the motors.

Fix

Set the pin to INPUT_PULLDOWN and defined the wet state as HIGH to match how this sensor actually behaves.

Lesson

Don't trust a datasheet's "default" — measure what the specific part in your hand actually does before wiring logic around it.

Case 02

The wheels wouldn't start on their own

Symptom

From a standstill the motors buzzed but didn't move — they needed a physical finger-push to get going, even at full power.

Root cause

A power-delivery limit, not software. The battery pack's internal resistance plus the driver's drop — with two motors paralleled per channel — made the supply sag hard under the four-motor stall inrush, so the motors saw far less than the measured idle voltage at the moment of breakaway.

Fix

Added a battery, then replaced a single kick with a pulsed, alternating fwd/rev "rock" kickstart to break stiction; recommended a large capacitor across the motor supply and a low-internal-resistance pack as the real hardware cure.

Limitation

Software can only paper over a power problem so far. The marginal supply is the honest ceiling on reliable self-starting — and the reason a better battery is next.

Case 03

Adding the ultrasonic sensor ran the board out of pins

Symptom

The ESP32 board didn't break out enough usable GPIOs to add the ultrasonic sensor on top of everything else.

Root cause

The header exposed only one input-only pin and none of the other spare inputs, so the ultrasonic's echo line had nowhere clean to go.

Fix

Moved the echo line to a free GPIO and dropped the sound sensor to reclaim its pin, adding a resistor divider to step the echo's 5 V down to the ESP32's 3.3 V.

Trade-off

Gave up ambient-sound sensing to gain obstacle detection — the more useful safety feature won the pin.

Compromises & trade-offs.

Where the constrained board and the power budget forced a choice.

Sound sensor dropped for ultrasonic

Limited usable GPIOs meant one had to go; obstacle detection was worth more than ambient sound.

Marginal motor supply

The battery pack sags under stall inrush, so reliable self-start needs a better pack and a buffer capacitor — a hardware fix, not a code one.

Shared SPI bus

The display and SD card share one bus, which keeps wiring simple but means they take turns rather than run truly in parallel.

Separate power for logic and motors

Added charging for the logic side while the motors keep their own pack, so motor noise and sag don't disturb the ESP32 — at the cost of two supplies to manage.

What I learned.

Next steps.