Control both the speed and direction of a DC motor from a single potentiometer, using an L293D motor driver chip.
A small DC motor has two pins; connecting them to power spins it one way, and reversing the polarity spins it the other way.

A relay can switch a motor on/off. The L293D is a 4-channel motor driver chip that can additionally control speed (via PWM) and direction (by swapping which output is HIGH and which is LOW) — without ever exposing the ESP32-S3 to the motor’s higher-power circuit.

| Pin | Description |
|---|---|
| In x (2, 7, 10, 15) | Digital signal input for channel x |
| Out x (3, 6, 11, 14) | Output for channel x — mirrors its In pin’s level, connected to +Vmotor or 0V |
| Enable1 (1) | Enables channels 1 & 2 — HIGH to enable |
| Enable2 (9) | Enables channels 3 & 4 — HIGH to enable |
| 0V (4, 5, 12, 13) | Ground |
| +V (16) | Logic supply, 3.0–36V |
| +Vmotor (8) | Motor power supply, up to 36V |
Wiring a motor to channels 1 & 2: feeding opposite levels to In1/In2 sets the rotation direction, and a PWM signal on Enable1 controls the speed. (Channels 3 & 4 work the same way via In3/In4/Enable2.)

Never power the motor directly from the ESP32-S3 — use a separate (or 9V battery) supply for the L293D’s
+Vmotor, sharing ground with the ESP32-S3.
Disconnect all power before building the circuit. Reconnect once verified.

Connections:

File: 04_output/code/Motor_And_Driver.py
from machine import ADC,Pin,PWM
import time
import math
in1Pin=Pin(13, Pin.OUT)
in2Pin=Pin(14, Pin.OUT)
enablePin=Pin(12, Pin.OUT)
pwm=PWM(enablePin,10000)
adc=ADC(Pin(1))
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_12BIT)
def driveMotor(dir,spd):
if dir:
in1Pin.value(1)
in2Pin.value(0)
else :
in1Pin.value(0)
in2Pin.value(1)
pwm.duty(spd)
try:
while True:
potenVal = adc.read()
rotationSpeed = potenVal - 2048
if (potenVal > 2048):
rotationDir = 1;
else:
rotationDir = 0;
rotationSpeed=int(math.fabs((potenVal-2047)//2)-1)
driveMotor(rotationDir,rotationSpeed)
time.sleep_ms(10)
except:
adc.deinit()
pwm.deinit()
04_output/code/.Motor_And_Driver.py.in1Pin=Pin(13, Pin.OUT)
in2Pin=Pin(14, Pin.OUT)
enablePin=Pin(12, Pin.OUT)
pwm=PWM(enablePin,10000)
adc=ADC(Pin(1))
in1Pin/in2Pin set direction; PWM on enablePin sets speed; the ADC reads the potentiometer, exactly as in Read the Voltage of a Potentiometer.
def driveMotor(dir,spd):
if dir:
in1Pin.value(1)
in2Pin.value(0)
else :
in1Pin.value(0)
in2Pin.value(1)
pwm.duty(spd)
Setting In1/In2 to opposite levels picks the direction; the PWM duty cycle on Enable1 controls how fast it spins.
potenVal = adc.read()
if (potenVal > 2048):
rotationDir = 1
else:
rotationDir = 0
rotationSpeed=int(math.fabs((potenVal-2047)//2)-1)
driveMotor(rotationDir,rotationSpeed)
The ADC’s range (0–4095) is split at its midpoint (2048): values above it mean one direction, values below mean the other. Subtracting 2047, taking the absolute value, and halving it converts “distance from center” into a speed — so the motor is stopped when the knob is centered and speeds up as it’s turned further toward either end.
Adapted from Python_Tutorial.pdf Project 17.2