This guide assumes that you have a Raspberry Pi connected to the Internet with CircuitPython installed.
- If you have not done this yet, follow this guide and come back to this page when you're set up.
Your Raspberry Pi has Python already.
Visit the Motor HAT setup page to setup your HAT and make sure that works first!
Then, installing the library to control the Pi Motor Hat kit is fairly simple:
From a terminal, enter the following to install the CircuitPython_MotorKit library
sudo pip3 install adafruit-circuitpython-motorkit
To communicate with Adafruit IO, you'll need to install the Adafruit IO Python library:
sudo pip3 install adafruit-io
Code Configuration
Before you run the code, you'll need to configure it for your Adafruit IO account.
Open whichever text editor you'd like from the Raspberry Pi's command line (this example uses the nano
editor) :
nano adafruit_io_steppers.py
To edit the file, set the ADAFRUIT_IO_KEY
variable to the secret Adafruit IO key you saved earlier.
Set the ADAFRUIT_IO_USERNAME
to your Adafruit IO username.
Then, save the file (CTRL+X, followed by Enter)
# SPDX-FileCopyrightText: 2019 Brent Rubell for Adafruit Industries # # SPDX-License-Identifier: MIT """ 'adafruit_io_steppers.py' ================================== Example of using CircuitPython and Adafruit IO to control two stepper motors over the internet. Dependencies: - Adafruit_Blinka (https://github.com/adafruit/Adafruit_Blinka) - Adafruit_CircuitPython_MotorKit (https://github.com/adafruit/Adafruit_CircuitPython_MotorKit) - Adafruit_IO_Python (https://github.com/adafruit/Adafruit_IO_Python) """ # Import Python Libraries import time import atexit import threading # import Adafruit IO REST client. from Adafruit_IO import Client, RequestError # Import CircuitPython Libraries from adafruit_motor import stepper as STEPPER from adafruit_motorkit import MotorKit # Set to your Adafruit IO key. # Remember, your key is a secret, # so make sure not to publish it when you publish this code! ADAFRUIT_IO_KEY = 'YOUR_IO_KEY' # Set to your Adafruit IO username. # (go to https://accounts.adafruit.com to find your username) ADAFRUIT_IO_USERNAME = 'YOUR_IO_USERNAME' # Create an instance of the REST client. aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY) # Delay between checking for `go` button press on Adafruit IO, in seconds ADAFRUIT_IO_DELAY = 1 # Stepper 1 Adafruit IO Feeds feed_step_1_steps = aio.feeds('stepper1steps') feed_step_1_direction = aio.feeds('stepper1direction') feed_step_1_step_size = aio.feeds('stepper1stepsize') # Stepper 2 Adafruit Feeds feed_step_2_steps = aio.feeds('stepper2steps') feed_step_2_direction = aio.feeds('stepper2direction') feed_step_2_step_size = aio.feeds('stepper2stepsize') # Steppers start button feed_steppers_status = aio.feeds('stepperstart') # create a default object, no changes to I2C address or frequency kit = MotorKit() # create empty threads (these will hold the stepper 1 and 2 threads) # pylint: disable=bad-thread-instantiation st1 = threading.Thread() st2 = threading.Thread() # recommended for auto-disabling motors on shutdown! def turnOffMotors(): kit.stepper1.release() kit.stepper2.release() atexit.register(turnOffMotors) stepstyles = [STEPPER.SINGLE, STEPPER.DOUBLE, STEPPER.INTERLEAVE, STEPPER.MICROSTEP] def stepper_worker(stepper, numsteps, direction, stepper_name, style, show_steps=False): print("Steppin!") stepper_steps = numsteps print(stepper_steps) for _ in range(numsteps): stepper.onestep(direction=direction, style=style) if show_steps: # print out the steps and send to IO stepper slider stepper_steps -= 1 print('Steps: ', stepper_steps) aio.send(feed_step_1_steps.key, stepper_steps) time.sleep(0.5) print("{0} Done Stepping".format(stepper_name)) # Reset slider on dashboard if stepper_name == "Stepper 1": aio.send(feed_step_1_steps.key, 0) elif stepper_name == "Stepper 2": aio.send(feed_step_2_steps.key, 0) while True: try: # attempt to poll the stepper status feed print('checking for GO button press...') stepper_start = aio.receive(feed_steppers_status.key) except RequestError.ThrottlingError: print('Exceeded the limit of Adafruit IO requests, delaying 30 seconds...') time.sleep(30) # Stepper 1 if not st1.isAlive() and int(stepper_start.value): stepper_1_steps = aio.receive(feed_step_1_steps.key) stepper_1_steps = int(stepper_1_steps.value) if stepper_1_steps > 0: # stepper slider is set # Get stepper configuration from io feeds stepper_1_direction = aio.receive(feed_step_1_direction.key) stepper_1_step_size = aio.receive(feed_step_1_step_size.key) print('Stepper 1 Configuration') print('\t%d steps' % stepper_1_steps) print('\tStep Size: ', stepper_1_step_size.value) print('\tStepper Direction: ', stepper_1_direction.value) # Set Stepper Direction if stepper_1_direction.value == 'Forward': move_dir = STEPPER.FORWARD elif stepper_1_direction.value == 'Backward': move_dir = STEPPER.BACKWARD # Stepper 1 Thread st1 = threading.Thread(target=stepper_worker, args=(kit.stepper1, stepper_1_steps, move_dir, "Stepper 1", stepstyles[STEPPER.SINGLE],)) st1.start() # Stepper 2 if not st2.isAlive() and int(stepper_start.value): stepper_2_steps = aio.receive(feed_step_2_steps.key) stepper_2_steps = int(stepper_2_steps.value) if stepper_2_steps > 0: # stepper slider is set # Get stepper configuration from io feeds stepper_2_direction = aio.receive(feed_step_2_direction.key) stepper_2_step_size = aio.receive(feed_step_2_step_size.key) print('Stepper 2 Configuration') print('\t%d steps' % stepper_2_steps) print('\tStep Size: ', stepper_2_step_size.value) print('\tStepper Direction: ', stepper_2_direction.value) # Set Stepper Direction if stepper_2_direction.value == 'Forward': move_dir = STEPPER.FORWARD elif stepper_2_direction.value == 'Backward': move_dir = STEPPER.BACKWARD # Stepper 2 Thread st2 = threading.Thread(target=stepper_worker, args=(kit.stepper2, stepper_2_steps, move_dir, "Stepper 2", stepstyles[STEPPER.SINGLE],)) st2.start() # delay polling the `go button` to avoid Adafruit IO timeouts print('Delaying for {0} seconds...'.format(ADAFRUIT_IO_DELAY)) time.sleep(ADAFRUIT_IO_DELAY)
Code Usage
From the Raspberry Pi terminal, run the code by typing the following:
python3 Adafruit_IO_Stepper_Control/code.py
and press enter
The terminal should output that it's checking the GO button, and delaying (to avoid sending too many fetch requests to Adafruit IO)
pi@stepper-pi:~ $ python3 dual_steppers.py checking for GO button press... Delaying for 1 seconds... checking for GO button press... Delaying for 1 seconds...
Next, navigate over to the Adafruit IO Dashboard to configure the first stepper.
Set the amount of steps by dragging the slider. The slider was configured to step in 10 step increments. You can re-configure to step more or less it by editing the block element.
Set the stepper direction by toggling the switch. The stepper can either move forward (default) or backward.
There are four essential types of steps you can use with your Motor HAT. All four kinds will work with any unipolar or bipolar stepper motor.
- Single Steps - this is the simplest type of stepping, and uses the least power. It uses a single coil to 'hold' the motor in place, as seen in the animated GIF above
- Double Steps - this is also fairly simple, except instead of a single coil, it has two coils on at once. For example, instead of just coil #1 on, you would have coil #1 and #2 on at once. This uses more power (approx 2x) but is stronger than single stepping (by maybe 25%)
- Interleaved Steps - this is a mix of Single and Double stepping, where we use single steps interleaved with double. It has a little more strength than single stepping, and about 50% more power. What's nice about this style is that it makes your motor appear to have 2x as many steps, for a smoother transition between steps
- Microstepping - this is where we use a mix of single stepping with PWM to slowly transition between steps. It's slower than single stepping but has much higher precision. We recommend 8 microstepping which multiplies the # of steps your stepper motor has by 8.
After the first stepper is configured, play with the controls for the second stepper.
When you're ready, press the green Go Button to send the presets to the Raspberry Pi and move the stepper motors.
The Raspberry Pi should configure and step the first stepper, then the second (if you configured it).
The terminal will also display the values it receives from Adafruit IO as it's stepping.
Stepper 1 Configuration 70 steps Step Size: STEPPER.DOUBLE Stepper Direction: Forward Steppin! Stepper 1 Done Stepping Stepper 2 Configuration 60 steps Step Size: STEPPER.DOUBLE Stepper Direction: Backward Steppin! Stepper 2 Done Stepping
When it's done stepping, the stepper driver's code resets the step amount feed to zero. The slider, connected to the step amount feed, resets too.
Adafruit IO can receive up to 120 data points a minute, the code aims to minimize the amount of data polling which occurs in the while True
loop.
First, it checks the stepper status feed, which is linked to the Go push-button on the dashboard. If the code exceeds the amount of requests it can send, it will wait thirty seconds for Adafruit IO to clear the timeout.
try: # attempt to poll the stepper status feed print('checking for GO button press...') stepper_start = aio.receive(feed_steppers_status.key) except ThrottlingError: print('Exceeded the limit of Adafruit IO requests, delaying 30 seconds...') time.sleep(30)
The code for controlling both stepper motors is similar - but doesn't happen simultaneously (code for the first stepper occurs first).
First, the code checks if the stepper thread is alive (if the stepper is moving or not) and if the stepper status feed (stepper_start
) was selected (the push-button was pressed). Then, it fetches the value from the Stepper Slider:
if not st1.isAlive() and int(stepper_start.value): stepper_1_steps = aio.receive(feed_step_1_steps.key) stepper_1_steps = int(stepper_1_steps.value)
If the value from the stepper slider is set, the code will fetch the stepper's direction and step size from Adafruit IO. Then, it'll call stepper_worker
and pass in the arguments from the configuration.
if stepper_1_steps > 0: # stepper slider is set # Get stepper configuration from io feeds stepper_1_direction = aio.receive(feed_step_1_direction.key) stepper_1_step_size = aio.receive(feed_step_1_step_size.key) print('Stepper 1 Configuration') print('\t%d steps' % stepper_1_steps) print('\tStep Size: ', stepper_1_step_size.value) print('\tStepper Direction: ', stepper_1_direction.value) # Set Stepper Direction if stepper_1_direction.value == 'Forward': move_dir = STEPPER.FORWARD elif stepper_1_direction.value == 'Backward': move_dir = STEPPER.BACKWARD # Stepper 1 Thread st1 = threading.Thread(target=stepper_worker, args=(kit.stepper1, stepper_1_steps, move_dir, "Stepper 1", stepstyles[STEPPER.SINGLE],)) st1.start()
Text editor powered by tinymce.