Community project
Untitled Project
This guide builds an autonomous edge-detecting robot vacuum powered by an ESP32 microcontroller. The robot uses dual DC motors for mobility, IR line tracking sensors to detect table edges, and a fan motor controlled by a logic-level MOSFET for suction. The L298N motor driver coordinates the drive motors while the IRLZ44N handles high-current fan switching with protection from a Schottky flyback diode.
Followers will receive a complete wiring diagram showing motor controller connections, sensor pinouts, and power distribution, a full parts list with recommended suppliers, the Arduino firmware for autonomous edge avoidance behavior, and step-by-step assembly instructions for chassis construction, sensor calibration, and safety testing.
Wiring diagram

Gather all the parts
Assemble it in 6 steps
1. Соберите шасси и пылесборник
Закрепите два TT‑редукторных мотора с колёсами по бокам лёгкого основания. Спереди поставьте скользящую опору. Перед центробежным вентилятором сделайте небольшой закрытый контейнер‑пылесборник с входной щелью и фильтром: крыльчатка должна тянуть воздух из контейнера и выбрасывать его наружу, а не быть открытой рядом с пальцами.
- Расположите входную щель и мягкую щётку на передней кромке корпуса.
- Оставьте фильтр доступным для очистки.
- Не запускайте вентилятор без защитного кожуха и фильтра.
- Этот компактный вариант собирает крошки и пыль, но не рассчитан на жидкость или тяжёлый мусор.
2. Установите датчики края
Закрепите edge_left и edge_right на нижней стороне передней части корпуса, направив их строго вниз. Между датчиком и столом оставьте примерно 4–8 мм; датчики должны быть разнесены к левому и правому углу.
- Провода датчиков закрепите так, чтобы они не касались колёс.
- На тёмном или глянцевом столе чувствительность придётся подстроить позже.
- Перед первым движением обязательно проверьте реакцию датчиков, держа робот над поверхностью.
3. Подключите моторный драйвер и ходовые моторы
Подайте +5 V на VS и 5V драйвера driver, а общий минус на GND. Соедините OUT1/OUT2 с двумя выводами left_motor, OUT3/OUT4 — с двумя выводами right_motor. К ESP32 подключите IN1→GPIO4, IN2→GPIO13, IN3→GPIO14, IN4→GPIO16, ENA→GPIO17, ENB→GPIO18.
- Если колесо вращается в обратную сторону, поменяйте местами только два провода соответствующего мотора на OUT‑выходах.
- Общий GND обязателен для ESP32, драйвера, датчиков и питания.
- L298N заметно греется и имеет падение напряжения; используйте только малые 3–6 V моторы и не закрывайте радиатор.
- Не берите питание моторов с контакта 3V3 ESP32.
4. Соберите силовой ключ вентилятора
Подключите + вентилятора blower к шине +5 V, его минус — к DRAIN ключа fan_mosfet. SOURCE ключа соедините с общим GND. У диода flyback_diode катод K подключите к +5 V, анод A — к DRAIN. От GPIO25 проведите сигнал через gate_resistor 100 Ω к GATE. Резистор gate_pulldown 10 kΩ соедините между GATE и GND.
- Полоса на корпусе 1N5819 обозначает катод — он должен смотреть к +5 V.
- Расположите диод близко к проводам вентилятора.
- Не перепутайте DRAIN и SOURCE MOSFET; это приведёт к неправильной работе или перегреву.
- Отключайте питание перед любыми изменениями проводки.
5. Подключите датчики и питание
Для каждого TCRT5000: VCC подключите к 3V3 ESP32, GND — к общему GND; DO левого датчика подключите к GPIO32, правого — к GPIO33. Питание usb_supply +5 V подключите к общей шине +5 V, GND — к общей земле. ESP32 также питается от этой регулируемой 5‑вольтовой USB линии через свой USB-разъём или 5V/VIN, но не подключайте одновременно два разных USB‑источника.
- Нужен стабильный адаптер минимум 5 V / 2 A; короткие толстые провода питания уменьшают просадки при старте моторов.
- Сначала включайте робот, удерживая его поднятым над столом.
- Все земли должны быть соединены вместе.
- Не подавайте более 5 V на шину питания.
6. Настройте безопасное обнаружение края
После прошивки через кнопку Deploy в Schematik удерживайте робота над поверхностью. Поверните подстроечный винт каждого TCRT5000 так, чтобы при наличии столешницы под датчиком DO был LOW, а при свисании за край — HIGH. Затем поставьте робота далеко от края и проверьте, что он едет вперёд, отступает и поворачивает при поднесении одного датчика к краю.
- Если робот поворачивает не туда, поменяйте местами провода одного мотора на выходе драйвера.
- Начните тест на большой пустой поверхности и держите руку рядом, чтобы остановить робот.
- Не тестируйте возле воды, стекла, людей, клавиатуры или края выше безопасной высоты.
- Никогда не оставляйте настольный робот без присмотра.
Review all connections
1. Connections between "usb_supply" and "ESP32"
2. Connections between "driver" and "ESP32"
3. Connections between "blower" and "ESP32"
4. Connections between "fan_mosfet" and "ESP32"
5. Connections between "gate_resistor" and "ESP32"
6. Connections between "gate_pulldown" and "ESP32"
7. Connections between "flyback_diode" and "ESP32"
8. Connections between "edge_left" and "ESP32"
9. Connections between "edge_right" and "ESP32"
10. Connections between "left_motor" and "ESP32"
11. Connections between "right_motor" and "ESP32"
Deploy the firmware
#include <Arduino.h>
// Hoisted type definitions
enum MotionState { CRUISE, BACK_UP, TURN_LEFT, TURN_RIGHT };
// Forward declarations
void setMotor(int in1, int in2, int enablePin, int speed, bool forward);
void stopMotors();
void driveForward(uint8_t speed);
void driveBackward(uint8_t speed);
void turnInPlace(bool right);
bool edgeDetected();
void beginAvoidance(unsigned long now);
const int LEFT_IN1 = 4;
const int LEFT_IN2 = 13;
const int RIGHT_IN1 = 14;
const int RIGHT_IN2 = 16;
const int LEFT_EN = 17;
const int RIGHT_EN = 18;
const int FAN_GATE = 25;
const int EDGE_LEFT = 32;
const int EDGE_RIGHT = 33;
const uint8_t DRIVE_SPEED = 155;
const uint8_t TURN_SPEED = 175;
const uint8_t FAN_SPEED = 220;
const unsigned long BACKUP_MS = 420;
const unsigned long TURN_MS = 520;
const unsigned long CRUISE_CHANGE_MS = 2600;
MotionState state = CRUISE;
unsigned long stateStarted = 0;
unsigned long cruiseStarted = 0;
bool turnRightNext = true;
void setMotor(int in1, int in2, int enablePin, int speed, bool forward) {
digitalWrite(in1, forward ? HIGH : LOW);
digitalWrite(in2, forward ? LOW : HIGH);
analogWrite(enablePin, speed);
}
void stopMotors() {
analogWrite(LEFT_EN, 0);
analogWrite(RIGHT_EN, 0);
digitalWrite(LEFT_IN1, LOW);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN1, LOW);
digitalWrite(RIGHT_IN2, LOW);
}
void driveForward(uint8_t speed) {
setMotor(LEFT_IN1, LEFT_IN2, LEFT_EN, speed, true);
setMotor(RIGHT_IN1, RIGHT_IN2, RIGHT_EN, speed, true);
}
void driveBackward(uint8_t speed) {
setMotor(LEFT_IN1, LEFT_IN2, LEFT_EN, speed, false);
setMotor(RIGHT_IN1, RIGHT_IN2, RIGHT_EN, speed, false);
}
void turnInPlace(bool right) {
setMotor(LEFT_IN1, LEFT_IN2, LEFT_EN, TURN_SPEED, right);
setMotor(RIGHT_IN1, RIGHT_IN2, RIGHT_EN, TURN_SPEED, !right);
}
bool edgeDetected() {
// Adjust each sensor trimmer so DO is HIGH only when the sensor sees no tabletop.
return digitalRead(EDGE_LEFT) == HIGH || digitalRead(EDGE_RIGHT) == HIGH;
}
void beginAvoidance(unsigned long now) {
state = BACK_UP;
stateStarted = now;
driveBackward(DRIVE_SPEED);
}
void setup() {
pinMode(LEFT_IN1, OUTPUT);
pinMode(LEFT_IN2, OUTPUT);
pinMode(RIGHT_IN1, OUTPUT);
pinMode(RIGHT_IN2, OUTPUT);
pinMode(LEFT_EN, OUTPUT);
pinMode(RIGHT_EN, OUTPUT);
pinMode(FAN_GATE, OUTPUT);
pinMode(EDGE_LEFT, INPUT);
pinMode(EDGE_RIGHT, INPUT);
stopMotors();
analogWrite(FAN_GATE, FAN_SPEED);
stateStarted = millis();
cruiseStarted = stateStarted;
}
void loop() {
const unsigned long now = millis();
if (state == CRUISE) {
if (edgeDetected()) {
beginAvoidance(now);
} else if (now - cruiseStarted >= CRUISE_CHANGE_MS) {
state = turnRightNext ? TURN_RIGHT : TURN_LEFT;
turnRightNext = !turnRightNext;
stateStarted = now;
turnInPlace(state == TURN_RIGHT);
} else {
driveForward(DRIVE_SPEED);
}
return;
}
if (state == BACK_UP) {
if (now - stateStarted >= BACKUP_MS) {
state = turnRightNext ? TURN_RIGHT : TURN_LEFT;
turnRightNext = !turnRightNext;
stateStarted = now;
turnInPlace(state == TURN_RIGHT);
}
return;
}
if (now - stateStarted >= TURN_MS) {
state = CRUISE;
cruiseStarted = now;
driveForward(DRIVE_SPEED);
}
}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.




