Community project

Untitled Project

ESP32
Photo of Untitled Project
Generated with AI

КИТЫ ИНВЕСТИЦИЙ

Last updated August 11, 2026

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

Wiring diagram for Untitled Project

Gather all the parts

QtyComponent
1

DFRobot MDV 2x2A DC Motor Controller (L298N)

L298N motor driver module

L298N dual H-bridge motor driver carrier. 2A continuous per channel, 5-46V motor supply, ~1.8V dropout (BJT-based, hot at high currents). Drives 2 brushed DC motors or 1 bipolar stepper. Pair with PWM on EN pins for speed control.

1

DC Motor

3-6 V TT gearmotor

DC motor (requires motor driver like L298N)

1

DC Motor

3-6 V TT gearmotor

DC motor (requires motor driver like L298N)

1

DC Motor

5 V blower

DC motor (requires motor driver like L298N)

1

IRLZ44N Logic-Level N-Channel MOSFET

IRLZ44N

Logic-level N-channel MOSFET commonly used as a low-side switch for DC loads from 3.3V or 5V microcontroller GPIO. Use a gate resistor, a gate pulldown, and a flyback diode for inductive loads.

1

TCRT5000 IR Line Tracking Sensor Module

TCRT5000

Single-channel infrared reflective sensor module built around the Vishay TCRT5000 IR emitter/phototransistor pair. An onboard LM393 comparator with a sensitivity trim-pot drives a thresholded digital output (DO) for line-detection or obstacle-avoidance, while a separate analog pin (AO) exposes the raw reflected-IR reading. Runs on 3.3V-5V, senses reflective surfaces roughly 1-25mm away, and is the standard low-cost sensor on line-following and obstacle-avoiding robot kits. Sold as a generic 4-pin (VCC/GND/DO/AO) breakout under many brand names (HiLetgo, ACEIRMC, OSOYOO, etc.) rather than by a single official manufacturer.

1

TCRT5000 IR Line Tracking Sensor Module

TCRT5000

Single-channel infrared reflective sensor module built around the Vishay TCRT5000 IR emitter/phototransistor pair. An onboard LM393 comparator with a sensitivity trim-pot drives a thresholded digital output (DO) for line-detection or obstacle-avoidance, while a separate analog pin (AO) exposes the raw reflected-IR reading. Runs on 3.3V-5V, senses reflective surfaces roughly 1-25mm away, and is the standard low-cost sensor on line-following and obstacle-avoiding robot kits. Sold as a generic 4-pin (VCC/GND/DO/AO) breakout under many brand names (HiLetgo, ACEIRMC, OSOYOO, etc.) rather than by a single official manufacturer.

1

USB-C 5V Adapter

5 V, 2 A

USB-C wall adapter delivering regulated 5 V to the board's USB or VBUS rail. Default wired power source for desktop / stationary projects.

1

1N5819 Schottky flyback diode

1N5819

Suppresses inductive voltage spike from the blower fan when switched off.

1

100 ohm gate resistor

100 Ω

Series resistor between ESP32 fan-control GPIO and MOSFET gate.

1

10 kilohm gate pulldown resistor

10 kΩ

Keeps fan MOSFET off while the ESP32 starts.

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"

Functionusb_supplyESP32
power+5V5V
groundGNDGND

2. Connections between "driver" and "ESP32"

FunctiondriverESP32
powerVS5V
power5V5V
groundGNDGND
digitalIN1GPIO 4
digitalIN2GPIO 13
digitalIN3GPIO 14
digitalIN4GPIO 16
pwmENAGPIO 17
pwmENBGPIO 18
outputOUT1 → DC Motor VCCEXT
outputOUT2 → DC Motor GNDEXT
outputOUT3 → DC Motor VCCEXT
outputOUT4 → DC Motor GNDEXT

3. Connections between "blower" and "ESP32"

FunctionblowerESP32
powerVCC5V
groundGND → IRLZ44N Logic-Level N-Channel MOSFET DRAINEXT
powerIN15V
digitalIN2 → IRLZ44N Logic-Level N-Channel MOSFET DRAINEXT

4. Connections between "fan_mosfet" and "ESP32"

Functionfan_mosfetESP32
groundSOURCEGND

5. Connections between "gate_resistor" and "ESP32"

Functiongate_resistorESP32
dataAGPIO 25
dataB → IRLZ44N Logic-Level N-Channel MOSFET GATEEXT

6. Connections between "gate_pulldown" and "ESP32"

Functiongate_pulldownESP32
dataA → IRLZ44N Logic-Level N-Channel MOSFET GATEEXT
groundBGND

7. Connections between "flyback_diode" and "ESP32"

Functionflyback_diodeESP32
powerK5V
dataA → IRLZ44N Logic-Level N-Channel MOSFET DRAINEXT

8. Connections between "edge_left" and "ESP32"

Functionedge_leftESP32
powerVCC3V3
groundGNDGND
digitalDOGPIO 32

9. Connections between "edge_right" and "ESP32"

Functionedge_rightESP32
powerVCC3V3
groundGNDGND
digitalDOGPIO 33

10. Connections between "left_motor" and "ESP32"

Functionleft_motorESP32
digitalIN1 → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT1EXT
digitalIN2 → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT2EXT

11. Connections between "right_motor" and "ESP32"

Functionright_motorESP32
digitalIN1 → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT3EXT
digitalIN2 → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT4EXT

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.

Open in Schematik