Driving a 28BYJ-48 Stepper Motor & ULN2003 driver with a Raspberry Pi

Quick points about this motor & driver

They are wonderfully cheap and extremely accurate due to 1/64 gearing. They move by 0.087890625° per step! However, the gearing is made of plastic and will wear out overtime, especially if moving heavy objects. Lastly the motors can become a little toasty if you work them hard.

Circuit

Fritzing

Code

#!/usr/bin/python3
import RPi.GPIO as GPIO
import time

in1 = 17
in2 = 18
in3 = 27
in4 = 22

# careful lowering this, at some point you run into the mechanical limitation of how quick your motor can move
step_sleep = 0.002

step_count = 4096 # 5.625*(1/64) per step, 4096 steps is 360°

direction = False # True for clockwise, False for counter-clockwise

# defining stepper motor sequence (found in documentation http://www.4tronix.co.uk/arduino/Stepper-Motors.php)
step_sequence = [[1,0,0,1],
                 [1,0,0,0],
                 [1,1,0,0],
                 [0,1,0,0],
                 [0,1,1,0],
                 [0,0,1,0],
                 [0,0,1,1],
                 [0,0,0,1]]

# setting up
GPIO.setmode( GPIO.BCM )
GPIO.setup( in1, GPIO.OUT )
GPIO.setup( in2, GPIO.OUT )
GPIO.setup( in3, GPIO.OUT )
GPIO.setup( in4, GPIO.OUT )

# initializing
GPIO.output( in1, GPIO.LOW )
GPIO.output( in2, GPIO.LOW )
GPIO.output( in3, GPIO.LOW )
GPIO.output( in4, GPIO.LOW )

motor_pins = [in1,in2,in3,in4]
motor_step_counter = 0 ;

def cleanup():
    GPIO.output( in1, GPIO.LOW )
    GPIO.output( in2, GPIO.LOW )
    GPIO.output( in3, GPIO.LOW )
    GPIO.output( in4, GPIO.LOW )
    GPIO.cleanup()

# the meat
try:
    i = 0
    for i in range(step_count):
        for pin in range(0, len(motor_pins)):
            GPIO.output( motor_pins[pin], step_sequence[motor_step_counter][pin] )
        if direction==True:
            motor_step_counter = (motor_step_counter - 1) % 8
        elif direction==False:
            motor_step_counter = (motor_step_counter + 1) % 8
        else: # defensive programming
            print( "uh oh... direction should *always* be either True or False" )
            cleanup()
            exit( 1 )
        time.sleep( step_sleep )

except KeyboardInterrupt:
    cleanup()
    exit( 1 )

cleanup()
exit( 0 )

Stuff you might need for this to run:

sudo apt-get update --fix-missing && sudo apt-get install python3-rpi.gpio

Results

This motor takes 5.625*(1/64)° per step, this means 2048 steps for 180°:

and 4096 steps for 360°:

One thing that is super cool about the driver board are the LEDs. Projects are always cooler with blinky LEDs, but these guys also help show what is actually going on inside the stepper, and can help you find issues. They are supposed to light up in sequence.

Driving a Bipolar Stepper Motor with an L298N and a Raspberry Pi

A few things to know about the L298N

This is a rudimentary way to drive a stepper motor. You are activating the motor’s coils in sequence yourself to have it take steps. There exist cheap stepper drivers which only require 1 signal from the Pi to be instructed to execute a full step sequence.

There are 2 coils on a bipolar stepper motor, each with a + and a – side. As such, this method requires 4 pins from your Pi to drive the Motor Vs only 1 pin with a more advanced stepper driver. Equally true is that a more advanced stepper driver will require at least another pin for direction and they’ll usually have other pins for micro-stepping.

This method does not allow for micro-stepping, only full steps. This is fine enough for most applications.

Lastly, while the L298N method might appear less appealing because of the above points, I find it to be extremely robust (advanced stepper drivers can be very finicky). You can power your Pi or Arduino with it thanks to a 5V port (warning, it can only provide so many amps and a Pi running heavy processing will crash).

It can be used to drive 2 DC motors instead of 2 stepper coils of a bipolar stepper motor, and has 2 PWM pins to adjust power given to the motors. It’s a very versatile and enabling device to master. You’ll also learn a lot about how stepper motors work.

Finding your stepper motor coils

Documentation for electronics is often inaccurate, disparate, when it’s even available. It’s good to find or verify what wires you think your coils are on. To do so you can put an LED on 2 motor pins and spin it until it lights up. When it does, induction powered your LED so you know you are holding the 2 wires of a coil. The other 2 wires are obviously the other coil.

There is no good way I could find to verify which of a coil’s wire is positive or negative. In my trials, I found that it doesn’t matter for a bipolar stepper motor on an L298N. The worst that will happen is that your motor may turn in the other direction which is easy enough to fix by flipping wires.

Circuit

Fritzing

Code

#!/usr/bin/python3
import RPi.GPIO as GPIO
import time

out1 = 17
out2 = 18
out3 = 27
out4 = 22

# careful lowering this, at some point you run into the mechanical limitation of how quick your motor can move
step_sleep = 0.002

step_count = 200

# setting up
GPIO.setmode( GPIO.BCM )
GPIO.setup( out1, GPIO.OUT )
GPIO.setup( out2, GPIO.OUT )
GPIO.setup( out3, GPIO.OUT )
GPIO.setup( out4, GPIO.OUT )

# initializing
GPIO.output( out1, GPIO.LOW )
GPIO.output( out2, GPIO.LOW )
GPIO.output( out3, GPIO.LOW )
GPIO.output( out4, GPIO.LOW )


def cleanup():
    GPIO.output( out1, GPIO.LOW )
    GPIO.output( out2, GPIO.LOW )
    GPIO.output( out3, GPIO.LOW )
    GPIO.output( out4, GPIO.LOW )
    GPIO.cleanup()


# the meat
try:
    i = 0
    for i in range(step_count):
        if i%4==0:
            GPIO.output( out4, GPIO.HIGH )
            GPIO.output( out3, GPIO.LOW )
            GPIO.output( out2, GPIO.LOW )
            GPIO.output( out1, GPIO.LOW )
        elif i%4==1:
            GPIO.output( out4, GPIO.LOW )
            GPIO.output( out3, GPIO.LOW )
            GPIO.output( out2, GPIO.HIGH )
            GPIO.output( out1, GPIO.LOW )
        elif i%4==2:
            GPIO.output( out4, GPIO.LOW )
            GPIO.output( out3, GPIO.HIGH )
            GPIO.output( out2, GPIO.LOW )
            GPIO.output( out1, GPIO.LOW )
        elif i%4==3:
            GPIO.output( out4, GPIO.LOW )
            GPIO.output( out3, GPIO.LOW )
            GPIO.output( out2, GPIO.LOW )
            GPIO.output( out1, GPIO.HIGH )

        time.sleep( step_sleep )

except KeyboardInterrupt:
    cleanup()
    exit( 1 )

cleanup()
exit( 0 )

Stuff you might need for this to run:

sudo apt-get update --fix-missing && sudo apt-get install python3-rpi.gpio

 

This stepper motor is rated for rotating by 1.8° per step. This means that it takes 100 steps to rotate 180 degrees:

and 200 to do a full 360:

Power the Pi from the L298N?

As I alluded to earlier, the L298N comes with a 5V power output that is most convenient to power Pis and Arduinos as it allows you to eliminate one of the 2 power supplies to your system. Be advised that while it provides the right voltage, it does not provide enough amps to power a standard Raspberry Pi, and while it can power a Pi Zero, the later will not get enough power and crash if it works hard enough (having a camera and a live streaming server pushed it over the edge in my testing). It will power a Pi Zero on Wifi running Python which is enough for a lot of applications.

Here’s the circuit if you want to do this:

Fritzing

Raspberry Pi Servo Jitter

Here’s the final solution I came up with to finally get a servo motor to behave on a Pi. It may not seem like much but it took a lot of doing to gather all the right bits. This was tested with an SG90, SG92R, and an MG90S on a Pi Zero.

Scroll straight to the end for the solution.

The most common way for controlling a servo motor on a Pi with is through RPi.GPIO as such:

#!/usr/bin/python3
import RPi.GPIO as GPIO
import time

servo = 23

GPIO.setmode( GPIO.BCM )
GPIO.setup( servo, GPIO.OUT )

# info on frequency and PWM formula at https://rpi.science.uoit.ca/lab/servo/
pwm = GPIO.PWM( servo, 50 )
pwm.start( 2.5 )

print( "0 deg" )
pwm.ChangeDutyCycle( 2.5 )  # turn towards 0 degree
time.sleep( 3 )

print( "90 deg" )
pwm.ChangeDutyCycle( 7.5 )  # turn towards 90 degree
time.sleep( 3 )

print( "180 deg" )
pwm.ChangeDutyCycle( 12.5 ) # turn towards 180 degree
time.sleep( 3 )

pwm.stop()
GPIO.cleanup()

Stuff you might need for this to run:

sudo apt-get update && sudo apt-get install python3-rpi.gpio

It results in super jitter which is unacceptable for the holy mission of pen plotting.

As far as I understand, the jitter comes from the wave form RPi.GPIO produces for Pulse Width Modulation, which is made in software and so it’s not super stable (no dedicated resources to build it). From what I gather, pigpio is programmed to tap into the one hardware PWM that Pis have.

The solution thus is as such:

#!/usr/bin/python3
import RPi.GPIO as GPIO
import pigpio
import time

servo = 23

# more info at http://abyz.me.uk/rpi/pigpio/python.html#set_servo_pulsewidth

pwm = pigpio.pi()
pwm.set_mode(servo, pigpio.OUTPUT)

pwm.set_PWM_frequency( servo, 50 )

print( "0 deg" )
pwm.set_servo_pulsewidth( servo, 500 ) ;
time.sleep( 3 )

print( "90 deg" )
pwm.set_servo_pulsewidth( servo, 1500 ) ;
time.sleep( 3 )

print( "180 deg" )
pwm.set_servo_pulsewidth( servo, 2500 ) ;
time.sleep( 3 )

# turning off servo
pwm.set_PWM_dutycycle( servo, 0 )
pwm.set_PWM_frequency( servo, 0 )

Stuff you might need for this to run:

sudo apt-get update && sudo apt-get install python3-pigpio
sudo pigpiod

And the resulting super smooth motion and holds: