This application detects a raised, open hand with MediaPipe and drives an L9110S H-bridge from a Raspberry Pi to control a stepper motor. The motor direction follows the detected hand tilt:
- Left tilt → clockwise rotation (by default)
- Right tilt → counterclockwise rotation
- Vertical (within
45°) → motor stops - Face or hand not detected → motor stops immediately
The L9110S driver board uses two H-bridges to control a bipolar stepper motor:
STEPPER_COIL_A_PIN1(GPIO17by default) → Driver boarda-1a(Coil A control 1)STEPPER_COIL_A_PIN2(GPIO18by default) → Driver boarda-1b(Coil A control 2)STEPPER_COIL_B_PIN1(GPIO22by default) → Driver boardb-1a(Coil B control 1)STEPPER_COIL_B_PIN2(GPIO23by default) → Driver boardb-2a(Coil B control 2)- Raspberry Pi
5 V(or external motor supply within 2.5–12 V) → Driver boardVCC - Raspberry Pi
GND→ Driver boardGND
Connect your bipolar stepper motor coils to the driver board motor outputs:
Raspberry Pi (BCM) Driver Board Stepper Motor
┌────────────────────┐ ┌────────────────────┐ ┌──────────┐
│ GPIO17 ────────────┼────────▶│ a-1a │ │ Coil A │
│ GPIO18 ────────────┼────────▶│ a-1b │──────▶│ │
│ GPIO22 ────────────┼────────▶│ b-1a │ │ Coil B │
│ GPIO23 ────────────┼────────▶│ b-2a │──────▶│ │
│ GND ─────────────┼────────▶│ GND │ └──────────┘
│ 5V* ──────────────┼────────▶│ VCC (2.5–12 V) │
└────────────────────┘ └────────────────────┘
▲ ▲
│ │
└─────────────── Shared Ground ────────────────┘
*Use an external supply within the driver's range when your motor needs more
current than the Pi's 5 V pin can deliver. Connect its positive lead to VCC
and its ground to the common ground line.
The driver board's motor output terminals (typically labeled OA1/OA2 for channel A and OB1/OB2 for channel B) should be connected to your stepper motor coils:
- Coil A: Connect to OA1 and OA2 terminals
- Coil B: Connect to OB1 and OB2 terminals
Important: Make sure to identify which wires of your stepper motor correspond to which coil. You may need to experiment or consult your motor's datasheet to determine the correct coil pairing.
Open main.py and adjust the constants near the top as needed:
STEPPER_COIL_A_PIN1/STEPPER_COIL_A_PIN2: BCM pins wired toa-1aanda-1b(controls coil A)STEPPER_COIL_B_PIN1/STEPPER_COIL_B_PIN2: BCM pins wired tob-1aandb-2a(controls coil B)LEFT_DIRECTION_IS_FORWARD: setFalseif you prefer right tilt to trigger clockwise rotationMOTOR_NEUTRAL_ANGLE: widen or narrow the neutral zone that keeps the motor stoppedSTEPPER_SPEED: motor speed in steps per second (1.0 to 200.0, default: 100.0)STEPPER_MODE: "full" for full-step mode, "half" for half-step mode (smoother but slower)
For better performance on Raspberry Pi (currently optimized for ~15-20 FPS):
- Frame Resolution: Reduced to 320×240 (from 640×480) - 4× less processing
- Hand Model: Set to
model_complexity=0(lightest) for faster processing - Face Mesh: Runs every 5th frame (set
FACE_MESH_UPDATE_INTERVAL=0to disable entirely) - Target FPS: Set to 15 FPS for smoother performance
For maximum speed, you can disable face mesh entirely by setting FACE_MESH_UPDATE_INTERVAL = 0.
You can also modify the speed by changing the speed parameter when creating the MotorController instance:
To automatically start the Receiver Control system when your Raspberry Pi boots:
start_receiver_control.sh- Startup script that runs the application using UVreceiver-control.service- Systemd service file for autostart
-
Copy the startup files to your Raspberry Pi:
# Copy these files to your Raspberry Pi's Reciever-Control-main directory # (Make sure you're in the project directory on your Pi)
-
Make the startup script executable:
chmod +x /home/frioaj1/Downloads/Reciever-Control-main/start_receiver_control.sh
-
Copy the service file to systemd:
sudo cp /home/frioaj1/Downloads/Reciever-Control-main/receiver-control.service /etc/systemd/system/
-
Reload systemd to recognize the new service:
sudo systemctl daemon-reload
-
Enable the service to start on boot:
sudo systemctl enable receiver-control.service -
Start the service immediately (optional):
sudo systemctl start receiver-control.service # Or start from the project directory: # cd /home/frioaj1/Downloads/Reciever-Control-main && ./start_receiver_control.sh
-
Check service status:
sudo systemctl status receiver-control.service
- Check if running:
sudo systemctl status receiver-control.service - View logs:
sudo journalctl -u receiver-control.service -f - Restart:
sudo systemctl restart receiver-control.service - Stop:
sudo systemctl stop receiver-control.service - Disable autostart:
sudo systemctl disable receiver-control.service
- The service runs as the
frioaj1user and assumes your project is in/home/frioaj1/Downloads/Reciever-Control-main - If using a different username or path, edit the service file accordingly
- The service includes display environment variables for GUI applications
- If you modify the script, restart the service:
sudo systemctl restart receiver-control.service - Note: The
frioaj1user needs sudo privileges to manage (start/stop/enable) the service - UV Requirement: Make sure UV is installed and available in the frioaj1 user's PATH. Install with:
curl -LsSf https://astral.sh/uv/install.sh | sh
motor_controller = StepperMotorController(
STEPPER_COIL_A_PIN1, STEPPER_COIL_A_PIN2,
STEPPER_COIL_B_PIN1, STEPPER_COIL_B_PIN2,
speed=150.0 # 150 steps per second
)Or use the set_speed() method at runtime:
motor_controller.set_speed(75.0) # 75 steps per secondpython main.pyOn non-Raspberry Pi systems the motor calls fall back to console logging so you can test the hand-tracking logic without GPIO access.
The system includes automatic motor shutdown for safety:
- Face or Hand Detection Loss: If either the face or hand disappears from view, or if the hand doesn't meet the detection criteria (open hand at head height), the motor immediately stops
- Speed Control: Motor speed is controlled via step rate (default: 100 steps/second) to prevent excessive current draw
- GPIO Error Handling: Graceful fallback to simulation mode if GPIO hardware is unavailable
- Coil De-energizing: When stopped, all motor coils are de-energized to prevent overheating