L9110 Fan Module

🎮 ACTIVATE
Module Goal: command the air itself in BOTH directions. Two doors decide whether the wind blows toward you or away, and PWM decides how hard. The propeller makes it a fan; your code makes it whatever you imagine.
📦 CONCEPT BOX The driver and its two doors
Your ESP32's pins carry a whisper of current enough for an LED, never enough for a motor. Motors are hungry. The L9110 chip is the translator: it listens to two tiny control pins and opens or closes the big power path from 5V to the motor.
The secret is that there are two doors, and they route the current differently:
| INA | INB | What the motor does |
|---|---|---|
| open | closed | spins one way → wind blows AWAY |
| closed | open | spins the OTHER way → wind blows TOWARD you |
| closed | closed | calm motor coasts |
Why does the direction change? Because the two doors route the current through the motor's coil in opposite directions and a motor spins according to where the current comes from. Reverse the current, reverse the spin. This tiny arrangement of switches is called an H-bridge, and it is the heart of every motor driver you will ever meet including the big one on the car, next module.
Speed: open a door with PWM (your flicker trick) the door flickers open/closed hundreds of times per second, and the motor feels the average. Same 0–65535 scale as always
Goal:
your first commanded wind open door INA for two seconds.
Concepts:
Motor Driver · H-Bridge Door 1 · Digital Command
import machine # Controls the board's pins
import time # Controls pauses and timing
# Sets up pin26 as an output for the fan driver, door INA
# pin26 corresponds to D2 on the Sensor Shield V5.0
pin26 = machine.Pin(26, machine.Pin.OUT)
# Sets up pin25 as an output for the fan driver, door INB
# pin25 corresponds to D3 on the Sensor Shield V5.0
pin25 = machine.Pin(25, machine.Pin.OUT)
print("Wind test starting...") # The announcement
pin26.value(1) # Open door pin26: current flows, the motor spins!
pin25.value(0) # Door pin25 stays closed
print("Fan: ON wind is blowing!") # Narrate the state
time.sleep(2) # Two seconds of wind
pin26.value(0) # Close the door: the motor coasts to a stop
print("Fan: OFF calm air.") # Narrate the calm💡 Note: compare this wiring story with the lamp (Module 2): your code did the same thing set a pin to 1 but this time the pin only whispered an order, and the L9110 did the heavy lifting. Command and power travel in separate lanes; that separation is what lets a tiny brain drive mighty muscles.
🎮 The Wind That Turns Around
Goal:
discover the two-door secret INB blows the wind the OPPOSITE way. Put your hand on both sides of the fan.
Concepts:
H-Bridge Direction · Current Reversal · Two-Door Logic
import machine # Controls the board's pins
import time # Controls pauses and timing
# Sets up pin26 as an output for the fan driver, door INA
# pin26 corresponds to D2 on the Sensor Shield V5.0
pin26 = machine.Pin(26, machine.Pin.OUT)
# Sets up pin25 as an output for the fan driver, door INB
# pin25 corresponds to D3 on the Sensor Shield V5.0
pin25 = machine.Pin(25, machine.Pin.OUT)
print("Watch the wind change direction!") # The show begins
pin26.value(1) # Door pin26 opens: spin one way
pin25.value(0) # Door pin25 closed
print("pin26 active: wind blows AWAY from you") # Name the direction
time.sleep(2) # Two seconds blowing out
pin26.value(0) # Close pin26...
pin25.value(0) # ...everything closed
time.sleep(2) # One second of stillness
pin26.value(0) # pin26 stays closed
pin25.value(1) # Door pin25 opens: the motor spins the OTHER way!
print("pin25 active: wind blows TOWARD you") # The reversal!
time.sleep(2) # Two seconds blowing in
pin26.value(0) # Close pin26
pin25.value(0) # Close pin25
print("The fan breathed in and out. Direction = choice of door.") # The lesson💡 Note: hold your palm behind the fan, then in front the air arrives from different sides, but your code never touched the motor: it only chose which door to open. Remember the table: INA = away, INB = toward, both closed = calm. Two doors, three states that's the entire control language of an H-bridge, and it fits in your head already.
🎮 The Throttle Ramp
Goal:
speed control PWM on the open door. Ramp the wind from a whisper to a storm, step by step.
Concepts:
PWM on a Motor · The 0–65535 Scale · The Motor Dead Zone
import machine # Controls the board's pins
import time # Controls pauses and timing
# Sets up pwm26 as a PWM output for the fan driver, door INA
# pwm26 corresponds to D2 on the Sensor Shield V5.0
pwm26 = machine.PWM(machine.Pin(26))
# Sets up pwm25 as a PWM output for the fan driver, door INB
# pwm25 corresponds to D3 on the Sensor Shield V5.0
pwm25 = machine.PWM(machine.Pin(25))
print("=== THROTTLE RAMP ===") # The test begins
for i in range(1, 11, 1): # i counts 1, 2, 3 ... 10
power = i * 6553 # 6553, 13106 ... 65530 (steps of 10%)
pwm26.duty_u16(power) # Open pwm26 at this power
pwm25.duty_u16(0) # pwm25 stays closed
print("Throttle:", i * 10, "%") # The readout
time.sleep_ms(800) # Feel each step
pwm26.duty_u16(0) # Close pwm26
pwm25.duty_u16(0) # Close pwm25
print("Ramp complete. A motor is a lamp that pushes air!") # The lesson💡 Note: the flicker trick, third appearance lamp, fan, and soon the car. Same scale, same idea: the duty value is the fraction of time the door stays open. Now the detective experiment: at 10–60% the motor probably hums but doesn't turn that's its dead zone, caused by friction and gears. No formula predicts it; every motor wakes up at its own threshold. Real engineers find it by test and start their ramps above it.
🎮 The Breathing Fan
Goal:
a fan that INHALES and EXHALES like a living creature ramp in on one door, ramp out on the other.
Concepts:
Direction as a Pattern Element · Twin Ramps · Life-like Motion
import machine # Controls the board's pins
import time # Controls pauses and timing
# Sets up pwm26 as a PWM output for the fan driver, door INA
# pwm26 corresponds to D2 on the Sensor Shield V5.0
pwm26 = machine.PWM(machine.Pin(26))
# Sets up pwm25 as a PWM output for the fan driver, door INB
# pwm25 corresponds to D3 on the Sensor Shield V5.0
pwm25 = machine.PWM(machine.Pin(25))
print("The fan is alive watch it breathe...") # The scene
for breath in range(1, 4, 1): # Three complete breaths
print("Breath", breath, "- inhaling...") # Pull the air in
for i in range(3, 11, 1): # Power climbs 30% ... 100%
pwm26.duty_u16(0) # pwm26 stays closed
pwm25.duty_u16(i * 6553) # pwm25 opens wider each step
time.sleep_ms(120) # Smooth ramp
print("Breath", breath, "- exhaling...") # Push the air out
for i in range(3, 11, 1): # Power climbs on the OTHER door
pwm26.duty_u16(i * 6553) # pwm26 opens wider each step
pwm25.duty_u16(0) # pwm25 closes
time.sleep_ms(120) # Smooth ramp
pwm26.duty_u16(0) # Rest
pwm25.duty_u16(0) # Silence
print("The fan sleeps.") # The ending💡 Note: your choreographies so far varied time (disco) and brightness (rainbow) this one varies direction. Two ramps, identical math, different doors: that single choice turned a spinning fan into something that seems to breathe. Notice how each ramp starts at 30%, not 0 you already know why (the dead zone from 26.3; starting at 0 would just hum). Patterns feel alive when you add a new dimension and direction is a powerful one.
🎮 The Command Fan
Goal:
type OUT, IN, SLOW, FAST, OFF and the air obeys in either direction, at any strength. Your first program with two memories (direction + speed) and your first named recipe from the Functions category.
Concepts:
Two State Variables · A Named Recipe (function) · Command Menu · Safety-First Exit
import machine # Controls the board's pins
import time # Controls pauses and timing
# Sets up pwm26 as a PWM output for the fan driver, door INA
# pwm26 corresponds to D2 on the Sensor Shield V5.0
pwm26 = machine.PWM(machine.Pin(26))
# Sets up pwm25 as a PWM output for the fan driver, door INB
# pwm25 corresponds to D3 on the Sensor Shield V5.0
pwm25 = machine.PWM(machine.Pin(25))
speed = 45000 # Starting power (about 70%)
direction = "off" # Starting state: both doors closed
def apply(): # A NAMED RECIPE: applies speed + direction to the doors
if direction == "out": # Blowing away?
pwm26.duty_u16(speed) # pwm26 open at the chosen power
pwm25.duty_u16(0) # pwm25 closed
elif direction == "in": # Pulling in?
pwm26.duty_u16(0) # pwm26 closed
pwm25.duty_u16(speed) # pwm25 open at the chosen power
else: # Off
pwm26.duty_u16(0) # Both doors closed
pwm25.duty_u16(0)
print("=== COMMAND FAN ===") # The title
print("Commands: LEFT, RIGHT, SLOW, FAST, OFF (EXIT quits)") # The menu
while True: # Serve commands forever
cmd = input("Command: ") # Wait for your word
if cmd == "EXIT": # Quit
direction = "off" # SAFETY: stop the fan first!
apply() # Close both doors
break # Leave the loop
elif cmd == "LEFT": # Blow away
direction = "out" # Remember the direction
apply() # Apply the recipe
print("Fan: blowing OUT at", speed) # Confirm
elif cmd == "RIGHT": # Pull in
direction = "in" # Remember the direction
apply() # Apply the recipe
print("Fan: pulling IN at", speed) # Confirm
elif cmd == "SLOW": # Gentle breeze
speed = 25000 # New power
apply() # Re-apply with the new speed
print("Fan: SLOW breeze") # Confirm
elif cmd == "FAST": # Full storm
speed = 65535 # Maximum power
apply() # Re-apply
print("Fan: FAST storm!") # Confirm
elif cmd == "OFF": # Silence
direction = "off" # Remember
apply() # Close everything
print("Fan: OFF silence") # Confirm
else: # Anything unknown
print("Unknown command:", cmd) # The polite refusalSerial Monitor:
=== COMMAND FAN ===
Commands: OUT, IN, SLOW, FAST, OFF (EXIT quits)
Command: OUT
Fan: blowing OUT at 45000
Command: FAST
Fan: FAST storm!
Command: IN
Fan: pulling IN at 65535
Command: SLOW
Fan: SLOW breeze
Command: OFF
Fan: OFF silence
💡 Note: three professional ideas live here. ① Two memories: direction and speed are separate boxes, and any command can change one without touching the other change to FAST while blowing IN and nothing breaks. ② The named recipe: def apply(): is your first function a recipe with a name, saved so every command can reuse it. Without it, you would copy the same door logic six times. You write the recipe ONCE, then just say apply(). This is the Functions category earning its place. ③ Safety order: EXIT stops the fan before leaving the last state of any motor program should always be calm.
