Smart IoT Motor Control with ESP32 & L298N
A wireless Bluetooth robot with PS3 controller input, dual DC motors, OLED score display, laser shooting system, and relay module — all in one project.
📋 Table of Contents
Project Overview
This project is a feature-rich wireless motor automation system built around the ESP32. Using a PlayStation 3 controller over Bluetooth, you can drive dual DC motors, activate a laser shooter, monitor a live score on an OLED display, and switch external loads via a relay — all from a battery-powered portable robot chassis.
Components Required
| Component | Function | Quantity | |
|---|---|---|---|
| 🧠 | ESP32 Dev Board | Main controller – Wi-Fi & Bluetooth | 1 |
| ⚡ | L298N Motor Driver | Controls 2 DC motors | 1 |
| ⚙️ | Dual DC Geared Motors | Robot wheel drive | 2 |
| 🖥 | 0.96" OLED SSD1306 (I²C) | Score & status display | 1 |
| 🎮 | PS3 Controller | Wireless Bluetooth control | 1 |
| 🔌 | Relay Module (1-CH) | External appliance switching | 1 |
| 🔴 | Laser Module (5mW) | Shooting mechanism | 1 |
| 💡 | LM393 Photosensor ×2 | Laser hit detection / scoring | 2 |
| 🔔 | Active Buzzer | Audio alerts | 1 |
| 🔋 | Li-ion Battery Pack (7.4V) | Portable power supply | 1 |
| 🔧 | Connecting Wires | Circuit wiring | Set |
Circuit Diagram
The ESP32 connects to the L298N via digital + PWM pins, to the OLED over I²C, and reads the two LM393 sensors on digital inputs. The laser is driven by a PWM analog output. The relay is driven from a separate digital pin.
Pin Connection Table
| Component | Component Pin | ESP32 GPIO | Type |
|---|---|---|---|
| OLED SSD1306 | VCC | 3V3 | Power |
| OLED SSD1306 | GND | GND | Ground |
| OLED SSD1306 | SDA | GPIO 21 | I²C Data |
| OLED SSD1306 | SCL | GPIO 22 | I²C Clock |
| LM393 Sensor 1 | DO (Digital Out) | GPIO 14 | Digital Input |
| LM393 Sensor 2 | DO (Digital Out) | GPIO 16 | Digital Input |
| Laser Module | Signal | GPIO 18 | PWM Output |
| L298N Motor Driver | ENA (Motor A speed) | GPIO 23 | PWM (Ch 3) |
| L298N Motor Driver | IN1 (Motor A dir) | GPIO 5 | Digital Output |
| L298N Motor Driver | IN2 (Motor A dir) | GPIO 19 | Digital Output |
| L298N Motor Driver | ENB (Motor B speed) | GPIO 15 | PWM (Ch 4) |
| L298N Motor Driver | IN3 (Motor B dir) | GPIO 4 | Digital Output |
| L298N Motor Driver | IN4 (Motor B dir) | GPIO 2 | Digital Output |
| Relay Module | IN (Signal) | GPIO (any free) | Digital Output |
PS3 Controller Input Map
The ESP32 connects to the PS3 DualShock controller over Bluetooth Classic using the Ps3Controller library. Use the D-pad for movement and special buttons for actions.
Ps3.begin("98:43:FA:59:56:AE") to match your ESP32's actual Bluetooth address. Use SixaxisPairTool on PC to pair the PS3 controller to your ESP32's MAC.
Step-by-Step Instructions
Set Up Arduino IDE for ESP32
In Arduino IDE, go to File → Preferences and add the ESP32 board URL: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json. Then install the ESP32 board package via Board Manager. Select ESP32 Dev Module.
Install Required Libraries
Open Library Manager (Ctrl+Shift+I) and install:
- Ps3Controller by jvpernis — Bluetooth PS3 input
- Adafruit SSD1306 — OLED driver
- Adafruit GFX Library — graphics primitives
Wire the OLED Display (I²C)
Connect the 0.96" SSD1306 OLED: VCC → 3V3, GND → GND, SDA → GPIO21, SCL → GPIO22. The default I²C address is 0x3C — if your OLED doesn't initialise, try 0x3D.
Connect the L298N Motor Driver
Wire the ESP32 PWM and direction pins to the L298N. Power the L298N from the 7.4V battery directly (the onboard 5V regulator on L298N can optionally power the ESP32 logic). Connect each motor to MOTOR A and MOTOR B terminals.
- ENA ← GPIO23 (PWM channel 3)
- IN1 ← GPIO5, IN2 ← GPIO19 (Motor A direction)
- ENB ← GPIO15 (PWM channel 4)
- IN3 ← GPIO4, IN4 ← GPIO2 (Motor B direction)
Set Up LM393 Photosensors (Laser Detection)
Mount two LM393 modules facing the laser module at your target positions. Connect DO pin of sensor 1 to GPIO14 and sensor 2 to GPIO16. VCC → 3V3, GND → GND. Adjust the potentiometer on each LM393 so the LED lights when laser hits it.
Wire the Laser Module
Connect the laser signal pin to GPIO18. The code uses analogWrite to control brightness: idle at PWM 30 (~2.3V) and full fire at PWM 230 (~3.2V). Never look directly into the laser.
Pair the PS3 Controller via Bluetooth
Download SixaxisPairTool on your PC. Connect the PS3 controller via USB and set the master Bluetooth address to your ESP32's MAC. Upload the code with your MAC in Ps3.begin("XX:XX:XX:XX:XX:XX"). Press PS button to connect.
Upload Code & Test
Upload the code at 115200 baud. Open Serial Monitor to see connection and movement logs. Test each D-pad direction, check motor rotation, and press ⭕ to test the laser. Adjust moveSpeed (default 200) and turnSpeed (default 180) for your motors.
Set Up the Shooting Game
Place the two LM393 sensors as targets. The OLED displays the score — each laser hit increments by 1 (with 2s cooldown). At score >50, "YOU LOSE" appears. Reset by power-cycling the ESP32. Adjust untouchableTime for difficulty.
Full Arduino Code (ESP32)
Complete production-ready sketch. Uses ESP32 LEDC PWM for motor speed, the Ps3Controller library for Bluetooth input, and Adafruit SSD1306 for OLED.
/* * Smart IoT Motor Control System * ESP32 Dev Board + L298N Motor Driver * * Author : MakeMindz (makemindz.com) * Control: PS3 DualShock via Bluetooth Classic * Display: 0.96" OLED SSD1306 (I2C 0x3C) * Game : Laser shooting with LM393 photosensors * * D-pad Up/Down/Left/Right → Motor direction * Circle (O) → Fire laser (2 seconds) * Score > 50 → Game Over ("YOU LOSE") */ #include <Wire.h> #include <Adafruit_SSD1306.h> #include <Adafruit_GFX.h> #include <Ps3Controller.h> // ── OLED Config ────────────────────────────── #define OLED_WIDTH 128 #define OLED_HEIGHT 64 #define OLED_ADDR 0x3C Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT, &Wire, -1); // ── Pin Definitions ────────────────────────── #define PHOTORESISTOR_1_PIN 14 #define PHOTORESISTOR_2_PIN 16 #define LASER_PIN 18 #define PWMA_PIN 23 #define AIN2_PIN 19 #define AIN1_PIN 5 #define PWMB_PIN 15 #define BIN2_PIN 2 #define BIN1_PIN 4 // ── PWM Parameters ─────────────────────────── const int motorFreq = 1000; // 1 kHz PWM frequency const int motorResolution = 8; // 8-bit (0–255) const int motorAChannel = 3; const int motorBChannel = 4; // ── Speed Settings ─────────────────────────── const int moveSpeed = 200; // 0–255, adjust for your motors const int turnSpeed = 180; // ── Game State ─────────────────────────────── int val1, val2; int score = 0; unsigned long lastScoreTime = 0; const unsigned long untouchableTime = 2000; // ── Laser State ────────────────────────────── bool laserOn = false; unsigned long laserOffTime = 0; const unsigned long laserFullPowerTime = 2000; // ── Motor Movement ─────────────────────────── void moveMotors(int mtrAspeed, int mtrBspeed, bool forward) { if (!forward) { digitalWrite(AIN1_PIN, HIGH); digitalWrite(AIN2_PIN, LOW); digitalWrite(BIN1_PIN, HIGH); digitalWrite(BIN2_PIN, LOW); } else { digitalWrite(AIN1_PIN, LOW); digitalWrite(AIN2_PIN, HIGH); digitalWrite(BIN1_PIN, LOW); digitalWrite(BIN2_PIN, HIGH); } ledcWrite(motorAChannel, mtrAspeed); ledcWrite(motorBChannel, mtrBspeed); } // ── PS3 Controller Callback ────────────────── void notify() { if (Ps3.data.button.up) moveMotors(moveSpeed, moveSpeed, true); else if (Ps3.data.button.down) moveMotors(moveSpeed, moveSpeed, false); else if (Ps3.data.button.left) moveMotors(0, moveSpeed, true); else if (Ps3.data.button.right) moveMotors(moveSpeed, 0, true); else moveMotors(0, 0, true); if (Ps3.data.button.circle && !laserOn) { laserOn = true; analogWrite(LASER_PIN, 230); // Full power ~3.2V laserOffTime = millis() + laserFullPowerTime; Serial.println("Laser → FIRE"); } } void onConnect() { Serial.println("PS3 Connected!"); } // ── PS3 Setup ──────────────────────────────── void setupPS3Controller() { Ps3.attach(notify); Ps3.attachOnConnect(onConnect); Ps3.begin("98:43:FA:59:56:AE"); // ← Change to your ESP32 MAC pinMode(PWMA_PIN, OUTPUT); pinMode(PWMB_PIN, OUTPUT); pinMode(AIN1_PIN, OUTPUT); pinMode(AIN2_PIN, OUTPUT); pinMode(BIN1_PIN, OUTPUT); pinMode(BIN2_PIN, OUTPUT); ledcSetup(motorAChannel, motorFreq, motorResolution); ledcSetup(motorBChannel, motorFreq, motorResolution); ledcAttachPin(PWMA_PIN, motorAChannel); ledcAttachPin(PWMB_PIN, motorBChannel); Serial.println("PS3 Controller Ready."); } // ── OLED Init ──────────────────────────────── void initDisplay() { if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) { Serial.println(F("SSD1306 failed!")); for (;;); } display.clearDisplay(); display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(15, 2); display.println("Shooting Game"); display.display(); delay(2000); } // ── Read Sensors ───────────────────────────── void readPhotoresistors() { val1 = digitalRead(PHOTORESISTOR_1_PIN); val2 = digitalRead(PHOTORESISTOR_2_PIN); } // ── Update Game State ──────────────────────── void updateGameState() { unsigned long now = millis(); if (now - lastScoreTime > untouchableTime) { if (val1 == 0 || val2 == 0) { score++; lastScoreTime = now; Serial.print("Score: "); Serial.println(score); } } } // ── Update OLED ────────────────────────────── void updateDisplay() { display.clearDisplay(); if (score > 50) { display.setTextSize(2); display.setCursor(0, 18); display.println("YOU LOSE"); } else { display.setTextSize(6); display.setCursor(28, 13); display.println(score); } display.display(); } // ── Laser Control ──────────────────────────── void controlLaser() { if (laserOn && millis() >= laserOffTime) { laserOn = false; analogWrite(LASER_PIN, 30); // Idle ~2.3V Serial.println("Laser → IDLE"); } } // ── Setup ──────────────────────────────────── void setup() { Serial.begin(115200); pinMode(PHOTORESISTOR_1_PIN, INPUT); pinMode(PHOTORESISTOR_2_PIN, INPUT); pinMode(LASER_PIN, OUTPUT); initDisplay(); setupPS3Controller(); analogWrite(LASER_PIN, 30); // Start in idle mode } // ── Main Loop ──────────────────────────────── void loop() { if (!Ps3.isConnected()) return; readPhotoresistors(); updateGameState(); updateDisplay(); controlLaser(); delay(10); // Reduce CPU load }
Simulation Links
🖥 Simulate Before You Build
Use Wokwi or Cirkit Designer to test motor logic, OLED rendering, and sensor interactions before assembling hardware. Note that Bluetooth PS3 pairing requires physical hardware.
💡 Tip: In Wokwi, paste the diagram.json into the Diagram tab, then paste the .ino code. Use keyboard input to simulate D-pad when PS3 is not available.
Comments
Post a Comment