The deliverable this week was a Minimum Viable Product for the final project — enough of the system actually running, end-to-end, that the path to the full thing is clear. For my swarm-of-robots project, that meant proving two things in one demo: (1) the radio comms layer can pair two boards and stream packets, and (2) a XIAO ESP32-C3 can actually drive motorized hardware. I had (1) from Week 4 already. (2) was the new piece this week.
The MVP setup
I didn't have my own three robots built yet, so for the MVP I borrowed an existing two-wheeled robot from my TF Bobby. His robot uses a pair of A4988 stepper drivers running bipolar stepper motors — different motor/driver pair than what I'll eventually use on my own final-project robots, but mechanically the same idea (differential drive). I dropped a XIAO ESP32-C3 onto the existing wiring, wrote some quick forward-only stepper code to confirm the steppers responded to my microcontroller, and recorded the result.
The forward-only sketch I wrote here was deliberately the simplest thing that could possibly work — step both motors at a constant rate, watch the robot roll. I didn't save that exact file as its own sketch; the movement primitives got absorbed into the larger follower sketches I wrote in the weeks after, where they live alongside the RSSI logic.
The comms half (carried over from Week 4)
The radio side is the same three-sketch trio I built out for Week 4. I'm including them here as well so the MVP is self-contained on this page.
mvp_board_addresses.ino — utility: prints the XIAO's MAC address over serial. You flash it once per board on first setup, copy the MAC into the other board's peerAddress array, and never touch it again.
mvp_sender.ino — sender: registers the receiver's MAC as an ESP-NOW peer, then broadcasts an incrementing uint32_t packet_id at 5 Hz forever. No sensors, no logic — just a steady heartbeat the receiver can measure RSSI off of.
mvp_receiver.ino — receiver: ingests packets, smooths RSSI over a 10-sample window, and switches a MOSFET-driven DC motor on/off through a hysteresis pair (chase at −45 dBm, stop at −35 dBm). Detailed walkthrough is on the Week 4 page.
Side experiment: my own (eventually scrapped) DC-motor robot
Alongside the MVP I was prototyping the drive electronics for the robot I planned to build myself — a custom differential drive on cheap DC gearmotors driven through an L9110 dual H-bridge. The smoke-test sketch below cycles STOP → FORWARD → STOP → SPIN → STOP so I could confirm the wiring and direction sense before flashing the full follower on top of it.
mvp_motor_test.ino
// L9110 wiring + direction smoke test. No ESP-NOW, no RSSI — just cycles
// STOP → FORWARD → STOP → SPIN → STOP so you can confirm each command
// does what it claims before flashing the full follower.
//
// Expected behavior:
// FORWARD: both wheels turn the same way → robot drives forward
// SPIN: wheels turn opposite ways → robot pivots in place (clockwise)
// If FORWARD goes backward → swap leads on BOTH motors at the L9110 outputs.
// If SPIN drives forward/back → swap leads on ONE motor.
const int LEFT_IA = D2;
const int LEFT_IB = D3;
const int RIGHT_IA = D4;
const int RIGHT_IB = D5;
const int PWM_FREQ = 1000;
const int PWM_RES = 8;
const int SPIN_DUTY = 110;
const int DRIVE_DUTY = 180;
void motorsBegin() {
ledcAttach(LEFT_IA, PWM_FREQ, PWM_RES);
ledcAttach(LEFT_IB, PWM_FREQ, PWM_RES);
ledcAttach(RIGHT_IA, PWM_FREQ, PWM_RES);
ledcAttach(RIGHT_IB, PWM_FREQ, PWM_RES);
}
void motorsStop() {
ledcWrite(LEFT_IA, 0);
ledcWrite(LEFT_IB, 0);
ledcWrite(RIGHT_IA, 0);
ledcWrite(RIGHT_IB, 0);
}
void motorsForward() {
ledcWrite(LEFT_IA, DRIVE_DUTY);
ledcWrite(LEFT_IB, 0);
ledcWrite(RIGHT_IA, DRIVE_DUTY);
ledcWrite(RIGHT_IB, 0);
}
void motorsSpin() {
ledcWrite(LEFT_IA, SPIN_DUTY);
ledcWrite(LEFT_IB, 0);
ledcWrite(RIGHT_IA, 0);
ledcWrite(RIGHT_IB, SPIN_DUTY);
}
void setup() {
Serial.begin(115200);
delay(500);
motorsBegin();
motorsStop();
}
void loop() {
Serial.println("STOP (2s)");
motorsStop();
delay(2000);
Serial.println("FORWARD (2s) — both wheels same direction");
motorsForward();
delay(2000);
Serial.println("STOP (1s)");
motorsStop();
delay(1000);
Serial.println("SPIN (3s) — pivot in place");
motorsSpin();
delay(3000);
}
This L9110 + DC plan didn't survive contact with the rest of the project — by the time I was building the actual three-robot fleet I'd pivoted to 28BYJ-48 unipolar steppers driven through ULN2003 boards (cheaper per unit, runs on a single 5 V rail, simpler to clone three of). The L9110 sketch above is captured here as a historical artifact: useful as a reference for the differential-drive control pattern, not the hardware that actually shipped.
What this MVP actually proved
Two XIAOs can pair over ESP-NOW and stream usable RSSI (already shown Week 4, re-validated).
One XIAO can drive a real motorized robot platform — at least one I borrowed.
The remaining work for the final is mostly mechanical: build three of my own robots that can carry the same comms + motor stack at the same time.