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
outputOUT1DC Motor VCCEXT
outputOUT2DC Motor GNDEXT
outputOUT3DC Motor VCCEXT
outputOUT4DC Motor GNDEXT

3. Connections between "blower" and "ESP32"

FunctionblowerESP32
powerVCC5V
groundGNDIRLZ44N Logic-Level N-Channel MOSFET DRAINEXT
powerIN15V
digitalIN2IRLZ44N 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
dataBIRLZ44N Logic-Level N-Channel MOSFET GATEEXT

6. Connections between "gate_pulldown" and "ESP32"

Functiongate_pulldownESP32
dataAIRLZ44N Logic-Level N-Channel MOSFET GATEEXT
groundBGND

7. Connections between "flyback_diode" and "ESP32"

Functionflyback_diodeESP32
powerK5V
dataAIRLZ44N 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
digitalIN1DFRobot MDV 2x2A DC Motor Controller (L298N) OUT1EXT
digitalIN2DFRobot MDV 2x2A DC Motor Controller (L298N) OUT2EXT

11. Connections between "right_motor" and "ESP32"

Functionright_motorESP32
digitalIN1DFRobot MDV 2x2A DC Motor Controller (L298N) OUT3EXT
digitalIN2DFRobot 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