This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
|
oak:tutorials:stepper_motor [2017/01/14 10:37] Rover#18 created |
oak:tutorials:stepper_motor [2017/01/14 16:52] (current) Rover#18 minor edits in the remote commanding description |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ======Oak: Controlling a Stepper Motor with an A4988 Driver====== | + | ======Oak: Controlling a Stepper Motor====== |
| + | ** With the Help of an A4988 Stepper Motor Driver ** | ||
| - | {{ ::oak_a4988_stepper_motor_control.png?direct&500 |}} | + | {{ :oak:tutorials:oak_stepper_motor_setup.jpg?direct&500 | Oak commanding stepper motor via A4988 driver. A 6-wire 2-phase unipolar stepper motor is shown here, with its coil center tap leads (white/yellow) unconnected. The A4988 driver (red) is upside-down in this photo, showing the backside of the board, which is opposite of the view shown in the Fritzing diagram. The motor is powered from a common 12V DC wall wart transformer, with a 68uF cap connected at the jack. }} |
| - | This lesson teaches you how to: | + | **This lesson teaches you how to:** |
| - | * Control a stepper motor with an [[https://www.pololu.com/file/download/A4988.pdf?file_id=0J450|A4988]] stepper motor driver, | + | * Control a stepper motor using an [[https://www.pololu.com/file/download/A4988.pdf?file_id=0J450|A4988]] stepper motor driver, |
| - | * Expose functions and variables to the Particle Cloud, for remote control via [[http://digistu.mp/oakterm|OakTerm]], the Particle mobile app, or | + | * Expose functions and variables to the Particle Cloud, for remote commanding via either [[http://digistu.mp/oakterm|OakTerm]], the Particle mobile app, or a [[https://digistump.com/wiki/oak/tutorials/stepper_motor?do=edit#stepper_controller_web_interface|custom web interface]]. |
| - | * Build a custom html page to command the stepper motor over the internet | + | |
| Line 20: | Line 20: | ||
| | Electrolytic capacitor, ~100uF | 1 | 50-200 uF | | | Electrolytic capacitor, ~100uF | 1 | 50-200 uF | | ||
| - | ** *Note: ** Most stepper motors in the ≤2A range will work for this lesson, including both bipolar and unipolar stepper motors with current and voltage ratings below 2A and 8V. The A4988 is __not__ compatible with 5-lead unipolar motors. Refer to A4988 documentation and motor specifications when selecting a motor for this project. | + | ** *Note: ** Most common stepper motors in the ≤2A range will work for this lesson, including both bipolar and unipolar stepper motors with current and voltage ratings below 2A and 8V. The A4988 is __not__ compatible with 5-lead unipolar motors. Refer to A4988 documentation and motor specifications when selecting a motor for this project. |
| =====Concepts:===== | =====Concepts:===== | ||
| - | ** Stepper Motors: ** A complete description of stepper motors is beyond the scope of this lesson. Adafruit's article [[https://learn.adafruit.com/all-about-stepper-motors/what-is-a-stepper-motor|All About Stepper Motors]] is an excellent summary of how stepper motors work and how they are used. For the purpose of this lesson, the important thing to understand is that they //step// between discrete positions. For example, a stepper motor with 200 steps per revolution (typical), moves in 1.8° increments and may only turn to 200 specific positions (unless you use [[https://en.wikipedia.org/wiki/Stepper_motor#Microstepping|microstepping]] which the A4988 is capable of, but that's a topic for another day..). | + | ==== Stepper Motors ==== |
| + | A complete description of stepper motors is beyond the scope of this lesson. Adafruit's article [[https://learn.adafruit.com/all-about-stepper-motors/what-is-a-stepper-motor|All About Stepper Motors]] is an excellent summary of how stepper motors work and how to use them. For the purpose of this lesson, the important thing to understand is that, unlike a normal DC motor which turns smoothly, stepper motors //step// between discrete positions. For example, a stepper motor with 200 steps per revolution (typical), moves in 1.8° increments and may only turn to 200 specific positions in full-step mode. | ||
| - | ** How a stepper motor driver works: ** In order to force the motor to step from one position to the next, a driver circuit must energize the coil(s) in such a way to attract the rotor toward the next position and/or repel it from the current position. The direction the rotor steps depends on the polarity of the coil(s). Stepper motors are typically driven by individual current pulses or square waves, with each pulse corresponding to a single step. | + | By nature, stepper motors are exceptionally //precise//, but are not necessarily //accurate//. They cannot //accurately// turn to any arbitrary position you choose because they are confined to a finite set of start/stop positions. You can turn the motor to the step nearest to a target position, but that step might be as far as half a step away from the target, if the target position happens to be halfway between two steps. However, stepper motors are //precise//, in that they reliably return to specific (step) positions with very little error. [[https://en.wikipedia.org/wiki/Stepper_motor#Microstepping|Micro-stepping]] significantly improves accuracy by dividing each base step into smaller steps. The A4988 for example facilitates micro-stepping in half, quarter, eighth, and 1/16th steps. In the case of a 200 steps/rev motor, the full-step accuracy is +-0.9° (half of one step width), while the accuracy with 1/16th micro-stepping approaches 0.06° (it's actually a bit [[http://www.micromo.com/technical-library/stepper-motor-tutorials/microstepping-myths-and-realities|more complicated]], but you get the idea). This lesson does not use this capability, but the reader is encouraged to research and experiment with this as future work. |
| - | You could, in theory, drive a stepper motor in a single direction using a single field effect transistor (FET) controlled from a single GPIO pin from your Oak, but you'll probably want to be able to drive your motor in //both// directions. To do that, you'll need to power each phase with 4 FETs each, configured as [[https://en.wikipedia.org/wiki/H_bridge|H-bridge]]s. That's 8 FETs that you would need to signal in various combinations depending on direction. | + | ==== Driving a Stepper Motor ==== |
| + | In order to force the motor to step from one position to the next, a driver circuit must send a pulse of current through the coils in such a way to attract the rotor toward the next position and/or repel it from the current position. The direction the rotor steps depends on the polarity of the coils -- that is, the direction that our pulse of current flows through each coil. | ||
| - | The A4988 and similar stepper motor drivers include two H-bridge circuits, one for each phase, with additional logic that takes the burden of signaling the 8 individual H-bridge FETs and abstracts their coordinated operation to two signals: **step** and **direction**. Each pulse into the the step pin signals the A4988 to drive the motor 1 step in either the CW or CCW direction, depending on whether the direction pin is high or low. | + | GPIO pins from any microcontroller, including the Oak, are intended to be used as transistor-to-transistor logic (TTL) or for extremely low current applications such as powering (very dim) LEDs and making them [[https://digistump.com/wiki/oak/tutorials/blink|blink]] (we're way past that tutorial now, right?). GPIO pins are not capable of sourcing nearly enough current to drive a stepper motor directly, which is why it is necessary to employ an external driver circuit to route current to the motor at the will of the Oak. The Oak will be the brains of our project, the driver circuit provides the braun. |
| + | Why use a stepper driver //module//, rather than building a drive circuit from scratch? You could, in theory, drive a stepper motor in a //single// direction using a single field effect transistor (FET) controlled from a single GPIO pin from your Oak, but you'll probably want to be able to drive your motor in //both// directions. To do that, you would need to power each phase with 4 FETs in [[https://en.wikipedia.org/wiki/H_bridge|H-bridge]] configuration. That's 8 FETs total that you would need to signal in various combinations depending on which direction you want the motor to move. The Oak has enough GPIO pins to command such a circuit, but building the circuit and coordinating all 8 FETs from within your sketch would be extremely tedious. | ||
| - | =====CAUTIONS:===== | + | Enter, the A4988 stepper motor driver carrier. The A4988 and similar stepper motor drivers include two H-bridge circuits, one for each phase (coil) of your motor, with additional logic that takes the burden of signaling the 8 individual H-bridge FETs and abstracts their coordinated operation to two signals: **step** and **direction**. //How cool is that?!// |
| + | |||
| + | **In summary:** We can apply a single pulse to the step pin of the A4988 to ask it to step the motor one step in either the CW or CCW direction, depending on whether we've set the direction pin high or low. | ||
| + | |||
| + | |||
| + | |||
| + | =====Circuit:===== | ||
| + | |||
| + | ** Wait! Before connecting your Oak to the A4988 stepper motor driver, ** consider flashing the [[https://digistump.com/wiki/oak/tutorials/stepper_motor?do=edit#oak_stepper_driver_driver_sketch|sketch]] below and experimenting with it to become familiar with how it works. By default, this sketch uses pin1 for the step signal specifically to give you a visual indication of when the motor //should// step. Prior to connecting the Oak to the A4988, try commanding a step rate of 2 (steps/sec) and watch the LED flash twice per second, then try changing the speed. You can do similar experiments with commanding direction if you remap the direction signal to pin1 in the sketch in order to see the LED turn on or off as you command direction (-1 or 1). Be sure to restore your mapping to match the signals going to the A4988 before connect the devices later. | ||
| + | |||
| + | ====CAUTIONS:==== | ||
| This circuit includes parts that can get hot enough to burn you and parts which can cause damage if installed incorrectly. Exercise caution when assembling and operating this circuit. Proceed at your own risk. | This circuit includes parts that can get hot enough to burn you and parts which can cause damage if installed incorrectly. Exercise caution when assembling and operating this circuit. Proceed at your own risk. | ||
| - | ==== The A4988 driver can get very hot! ==== | + | === The A4988 driver can get very hot! === |
| * The A4988 driver can get hot enough to burn you, even during normal operation. | * The A4988 driver can get hot enough to burn you, even during normal operation. | ||
| * If you have an IR gun, use it to monitor the A4988. | * If you have an IR gun, use it to monitor the A4988. | ||
| Line 42: | Line 55: | ||
| * Heat dissipation in the A4988 is a function of your motor's impedance, external torque on the motor, supply voltage, and drive speed. When you first start moving the motor, command at slow speeds for limited periods of time and gradually work up to faster speeds while monitoring the temperature of the A4988 driver. Max speed might not be much more than 60 RPM (1 rev/sec, or 200 steps/sec for 200 step/rev motor). | * Heat dissipation in the A4988 is a function of your motor's impedance, external torque on the motor, supply voltage, and drive speed. When you first start moving the motor, command at slow speeds for limited periods of time and gradually work up to faster speeds while monitoring the temperature of the A4988 driver. Max speed might not be much more than 60 RPM (1 rev/sec, or 200 steps/sec for 200 step/rev motor). | ||
| - | ==== Do not install the capacitor backwards! ==== | + | === Do not install the capacitor backwards! === |
| * You must install the capacitor in the correct [[https://en.wikipedia.org/wiki/Electrolytic_capacitor#Polarity_marking|polarity]]. | * You must install the capacitor in the correct [[https://en.wikipedia.org/wiki/Electrolytic_capacitor#Polarity_marking|polarity]]. | ||
| * Otherwise, [[https://youtu.be/rr7bPmGTQUk|bad things]] happen. | * Otherwise, [[https://youtu.be/rr7bPmGTQUk|bad things]] happen. | ||
| - | ==== Do not apply motor power supply backwards! ==== | + | === Do not apply motor power supply backwards! === |
| - | * This circuit does not have reverse polarity protection. | + | * The A4988 does not have reverse polarity protection. |
| * Connecting power supply leads in backwards can destroy your A4988, or worse. | * Connecting power supply leads in backwards can destroy your A4988, or worse. | ||
| * Double check power supply polarity before you connect to the A4988. | * Double check power supply polarity before you connect to the A4988. | ||
| * From personal experience (oops), plugging in 12Vdc backwards for 3 seconds, the chip got hot but was not damaged. | * From personal experience (oops), plugging in 12Vdc backwards for 3 seconds, the chip got hot but was not damaged. | ||
| - | ==== You MUST set the current limiting trim pot for your motor ==== | + | === You MUST set the current limiting trim pot for your motor === |
| * The trim potentiometer on the A4988 is initially set to some random value. | * The trim potentiometer on the A4988 is initially set to some random value. | ||
| * The initial value may allow the A4988 to push more current than your motor can handle. | * The initial value may allow the A4988 to push more current than your motor can handle. | ||
| - | * Follow instructions below. | + | * Follow instructions in Step 3 below. |
| - | + | ||
| - | =====Circuit:===== | + | |
| - | + | ||
| - | ** Wait! Before connecting your Oak to the A4988 stepper motor driver, ** consider flashing the sketch below and experimenting with it to become familiar with how it works. By default, this sketch uses pin1 for the step signal specifically to give you a visual indication of when the motor should step. Try commanding a step rate of 2 (step/sec) and watch the LED flash twice per second, then try changing the speed. You can do similar experiments with commanding direction if you remap the direction signal to pin1 in the sketch in order to see the LED turn on or off as you command direction (-1 or 1). | + | |
| + | ==== Fritzing Diagram ==== | ||
| {{ :oak_a4988_stepper_motor_control.png?direct&500 |}} | {{ :oak_a4988_stepper_motor_control.png?direct&500 |}} | ||
| - | ** Step 1: Connect the electrolytic capacitor ** | + | ==== Build Procedure ==== |
| - | * Connect the ~100uF capacitor across the A4988 VMON and GND pins, being very careful to connect in the correct [[https://en.wikipedia.org/wiki/Electrolytic_capacitor#Polarity_marking|polarity]]. Electrolytics usually have a stripe or "-" indicator on the side that should connect to ground. If you connect the capacitor backwards, it may explode when you apply power. | + | === Step 1: Connect the electrolytic capacitor === |
| + | * Connect the ~100uF capacitor across the A4988 VMON and GND pins, being very careful to connect in the correct [[https://en.wikipedia.org/wiki/Electrolytic_capacitor#Polarity_marking|polarity]]. Electrolytics usually have a stripe or "-" indicator on the side that should connect to ground. If you connect the capacitor backwards, it may [[https://youtu.be/rr7bPmGTQUk|explode]] when you apply power. | ||
| - | ** Step 2: Connect motor power supply to the A4988 ** | + | === Step 2: Connect motor power supply to the A4988 === |
| * If you're using a wall-wart transformer, unplug it from the wall. If you're using a benchtop power supply, turn it off. | * If you're using a wall-wart transformer, unplug it from the wall. If you're using a benchtop power supply, turn it off. | ||
| * Connect supply+ to the A4988 VMOT pin, and supply- to the neighboring GND pin. | * Connect supply+ to the A4988 VMOT pin, and supply- to the neighboring GND pin. | ||
| * Be very careful not to get these backwards, otherwise your board will overheat. | * Be very careful not to get these backwards, otherwise your board will overheat. | ||
| - | ** Step 3: set coil current limits on A4988 driver ** | + | === Step 3: set coil current limits on A4988 driver (this is NOT optional) === |
| * The following steps outline this process. If any of this doesn't make sense, read the Current Limiting section on [[https://www.pololu.com/product/1182|Pololu's A4988 page]] and watch their very informative video on [[https://youtu.be/89BHS9hfSUk|youtube]]. | * The following steps outline this process. If any of this doesn't make sense, read the Current Limiting section on [[https://www.pololu.com/product/1182|Pololu's A4988 page]] and watch their very informative video on [[https://youtu.be/89BHS9hfSUk|youtube]]. | ||
| * You need to know the maximum per-phase limit for your motor (use rating current, if per-phase current is not provided in your motor's datasheet). | * You need to know the maximum per-phase limit for your motor (use rating current, if per-phase current is not provided in your motor's datasheet). | ||
| Line 79: | Line 90: | ||
| * If your A4988 was manufactured by Pololu in Jan 2017 or later: Vref = 8*Imax*0.068 | * If your A4988 was manufactured by Pololu in Jan 2017 or later: Vref = 8*Imax*0.068 | ||
| * Otherwise: Vref = 8*Imax*0.050 = 0.4*Imax | * Otherwise: Vref = 8*Imax*0.050 = 0.4*Imax | ||
| + | * Example Vref values: | ||
| + | |||
| + | ^ Target Current Limit (A) ^| 0.25 | 0.50 | 0.75 | 1.00 | 1.50 | 2.00 | | ||
| + | ^ Target Vref (V) (for older A4988 models*) ^| 0.10 | 0.20 | 0.30 | 0.40 | 0.60 | 0.80 | | ||
| + | ^ Target Vref (V) (for Pololu 2017+ models) ^| 0.14 | 0.27 | 0.41 | 0.54 | 0.82 | 1.09 | | ||
| + | |||
| * At this point, only your motor power supply and capacitor should be plugged into the A4988. | * At this point, only your motor power supply and capacitor should be plugged into the A4988. | ||
| - | * Apply motor power | + | * Apply motor power. |
| - | * Measure voltage between motor supply ground and the center of the trim pot on the A4988 | + | * Measure voltage between motor supply ground and the center of the trim pot on the A4988. |
| * Adjust the trim pot until the voltage equals your calculated Vref. | * Adjust the trim pot until the voltage equals your calculated Vref. | ||
| * Tip: to measure Vref as you adjust the trim pot at the same time, use an alligator clip to connect your multimeter Vin+ lead to the shaft of a small metal screwdriver which you use to turn the trim pot. | * Tip: to measure Vref as you adjust the trim pot at the same time, use an alligator clip to connect your multimeter Vin+ lead to the shaft of a small metal screwdriver which you use to turn the trim pot. | ||
| Line 87: | Line 104: | ||
| - | ** Step 4: Connect the stepper motor to the A4988 driver ** | + | === Step 4: Connect the stepper motor to the A4988 driver === |
| * You turned off or unplugged your motor power supply, right? | * You turned off or unplugged your motor power supply, right? | ||
| * You'll need to figure out which leads from the stepper motor go to which pins on the A4988. | * You'll need to figure out which leads from the stepper motor go to which pins on the A4988. | ||
| * The A4988 driver powers two phases (coils) simultaneously - one through pins A1 and A2, and the other through pins B1 and B2. Your stepper motor may have more leads than you need. If you have access to the datasheet for your motor, look up its type and wire coloring scheme. Otherwise, you'll need to measure resistance between pairs of leads to determine which pair connects to the ends of one coil (coil "A"), and which pair of other leads connects to the other coil (coil "B") (it doesn't matter which coil is A and which is B), per instructions in the following table. | * The A4988 driver powers two phases (coils) simultaneously - one through pins A1 and A2, and the other through pins B1 and B2. Your stepper motor may have more leads than you need. If you have access to the datasheet for your motor, look up its type and wire coloring scheme. Otherwise, you'll need to measure resistance between pairs of leads to determine which pair connects to the ends of one coil (coil "A"), and which pair of other leads connects to the other coil (coil "B") (it doesn't matter which coil is A and which is B), per instructions in the following table. | ||
| - | * Once you've identified end-leads for each coil, you can connect one coil's end leads to A1 and A2 (order doesn't matter), and the other coil's end leads to B1 and B2 (order doesn't matter). The [[https://www.pololu.com/product/1182/faqs|Pololu A4988 FAQ]] provides handy diagrams to help with this. | + | * Once you've identified end-leads for each coil, you can connect one coil's end leads to A1 and A2 (order doesn't matter), and the other coil's end leads to B1 and B2 (order doesn't matter). The [[https://www.pololu.com/product/1182/faqs|Pololu A4988 FAQ]] provides handy diagrams to help with this. |
| ^ # Leads ^ (Probable) Type ^ Connection Mapping ^ | ^ # Leads ^ (Probable) Type ^ Connection Mapping ^ | ||
| Line 99: | Line 116: | ||
| | 8 | 2-phase unipolar | This one is complicated; it has 2 coils per phase, and the coils can be configured in series (recommended by Pololu for use with A4988) or parallel. Refer to the connection guide in the [[https://www.pololu.com/product/1182/faqs|Pololu FAQ]]. | | | 8 | 2-phase unipolar | This one is complicated; it has 2 coils per phase, and the coils can be configured in series (recommended by Pololu for use with A4988) or parallel. Refer to the connection guide in the [[https://www.pololu.com/product/1182/faqs|Pololu FAQ]]. | | ||
| - | ** Step 5: Test motor connections ** | + | === Step 5: Connect the Oak to the A4988 === |
| - | * Your power supply is still turned off or unplugged, right? | + | * Motor power is off, right? |
| + | * If your Oak is on, turn it off. | ||
| * At this point, only the capacitor, the (turned off) motor power supply, and the stepper motor should be plugged into the A4988. | * At this point, only the capacitor, the (turned off) motor power supply, and the stepper motor should be plugged into the A4988. | ||
| - | * Try to twist the shaft of your motor with your fingers. It will feel stiff but it should turn. You'll feel it "tick" through the detent (step) positions. If you can't turn it at all, then you might have a problem; make sure none of your leads are shorted together, and make sure the A4988 is off. If you were able to turn the motor, then proceed. | ||
| - | * Turn on or plug in your motor power supply. | ||
| - | * You may or may not see/hear/feel the slightest twitch of the motor. | ||
| - | * Make sure nothing is getting hot. The A4988 might get //slightly// warm. | ||
| - | * Try to twist the motor shaft again with your fingers. The shaft should be __much__ harder, if not impossible to run. If that's true, then you're in good shape. The A4988 driver is actively counteracting the torque you apply to the motor, in order to hold motor position. | ||
| - | * Turn off or unplug your motor power supply. | ||
| - | |||
| - | ** Connect the Oak to the A4988 ** | ||
| - | * Motor power is off, right? | ||
| - | * Unplug your Oak from USB or its own power supply. | ||
| * Connect a jumper from the Oak's Vcc pin to the A4988 VDD pin. This powers the A4988 logic circuits. | * Connect a jumper from the Oak's Vcc pin to the A4988 VDD pin. This powers the A4988 logic circuits. | ||
| * Connect a jumper from the Oak's Gnd pin to the A4988 GND pin. | * Connect a jumper from the Oak's Gnd pin to the A4988 GND pin. | ||
| Line 117: | Line 125: | ||
| * Connect a jumper from the Oak's P1 pin to the A4988 STEP pin. | * Connect a jumper from the Oak's P1 pin to the A4988 STEP pin. | ||
| * Connect a jumper between the A4988's RESET and SLEEP pins. | * Connect a jumper between the A4988's RESET and SLEEP pins. | ||
| + | * Do __not__ power on your Oak yet. | ||
| + | |||
| + | === Step 6: Power up! (aka Initial Checkout) === | ||
| + | * With the motor power supply and Oak turned OFF, try to twist the shaft of your motor with your fingers. | ||
| + | * It will feel stiff but it should turn. You'll feel it "tick" through the detent (step) positions. | ||
| + | * If you can't turn it at all, then you might have a problem. Make sure none of your motor leads are shorted together, and make sure that both the A4988 and Oak are off. | ||
| + | * If you were able to turn the motor, then proceed. | ||
| + | * Turn on or plug in your motor power supply. | ||
| + | * Turn on or plug in your Oak. | ||
| + | * You may or may not see/hear/feel the slightest twitch of the motor. | ||
| + | * Make sure nothing is getting hot. The A4988 might get //slightly// warm. If it's hot at this point then you have a problem. | ||
| + | * Try to twist the motor shaft again with your fingers. The shaft should be __much__ harder, if not impossible to turn by hand. If that's true, then you're in good shape. The A4988 driver is actively counteracting the torque you apply to the motor, in order to hold motor position. Neat! | ||
| + | * Turn off or unplug your motor power supply. | ||
| + | * Check one last time if the A4988 is getting hot. If not, you're good to go, but you will want to keep an eye on the temperature once you start commanding the motor to move. | ||
| - | ** Power up! ** | ||
| - | * Turn on the Oak. | ||
| - | * Turn on or plug in motor power to the A4988. | ||
| - | * Make sure the A4988 is not getting too warm. | ||
| =====Code:===== | =====Code:===== | ||
| + | ==== Oak Stepper Driver Driver Sketch ==== | ||
| <code> | <code> | ||
| const int pinStep = 1; | const int pinStep = 1; | ||
| const int pinDirection = 0; | const int pinDirection = 0; | ||
| - | const int durationStepPulseMs = 1; //ms | + | const int durationStepPulseMicroseconds = 100; //us |
| const int maxStepsPerSec = 4; | const int maxStepsPerSec = 4; | ||
| Line 154: | Line 173: | ||
| Particle.function("motion",motion); | Particle.function("motion",motion); | ||
| Particle.function("setspeed",setSpeed); | Particle.function("setspeed",setSpeed); | ||
| - | Particle.function("setdir",setDirection); | + | Particle.function("setdir",selectDirection); |
| // Setup Particle variable accessors: | // Setup Particle variable accessors: | ||
| Line 160: | Line 179: | ||
| Particle.variable("direction",stepDirection); | Particle.variable("direction",stepDirection); | ||
| Particle.variable("do_motion",doMotion); | Particle.variable("do_motion",doMotion); | ||
| - | Particle.variable("step_period",stepPeriodMs); | + | Particle.variable("stepperiod",stepPeriodMs); |
| - | Particle.variable("step_wait",waitBetweenStepsMs); | + | |
| } | } | ||
| - | // the loop function runs over and over again forever | + | // Main loop |
| void loop() { | void loop() { | ||
| - | // | + | |
| + | // If doMotion is true AND we have valid speed and direction values, then move the motor! | ||
| if( doMotion && stepSpeed>0 && (stepDirection==-1 || stepDirection==1) ){ | if( doMotion && stepSpeed>0 && (stepDirection==-1 || stepDirection==1) ){ | ||
| - | doOneStep(); | + | doOneStep(); // send one pulse to the A4988 STEP pin |
| - | delay(waitBetweenStepsMs); | + | |
| | | ||
| + | // Pause here until it's time to send the next pulse | ||
| + | // To be more precise, we would account for cpu time and step pulse duration, | ||
| + | // both of which should be small relative to the step period until you get up | ||
| + | // to speeds over 500 steps per second. Future builds should correct this. | ||
| + | delay(stepPeriodMs); | ||
| + | |||
| + | // Otherwise, pause briefly before checking again | ||
| } else { | } else { | ||
| delay(100); | delay(100); | ||
| Line 180: | Line 205: | ||
| - | int motion(String startOrStop){ | + | // The "motion" function expects a string argument, either "start" or "stop" |
| - | if( strcmp("start",startOrStop.c_str())==0){ | + | // This function sets the doMotion flag which the main loop monitors. |
| + | int motion(String strStartOrStop){ | ||
| + | if( strcmp("start",strStartOrStop.c_str())==0){ | ||
| doMotion = true; | doMotion = true; | ||
| - | return 1; | + | } else if( strcmp("stop",strStartOrStop.c_str())==0){ |
| - | } else if( strcmp("stop",startOrStop.c_str())==0){ | + | |
| doMotion = false; | doMotion = false; | ||
| - | return 0; | ||
| } else { | } else { | ||
| doMotion = false; | doMotion = false; | ||
| Particle.publish("motion() error","expected \"start\" or \"stop\""); | Particle.publish("motion() error","expected \"start\" or \"stop\""); | ||
| - | return -1; | + | return -1; // return -1 to indicate error |
| } | } | ||
| + | return (doMotion)?1:0; //return 1 or 0 indicating doMotion true or false | ||
| } | } | ||
| + | // setSpeed expects one argument: steps per second, between -1000 and 1000 | ||
| + | // The steps per second argument is received as a string but may indicate | ||
| + | // integer or fractional, positive or negative (i.g. "-100", "10", "0.25") | ||
| + | // Negative speed will be interpreted as negative direction | ||
| int setSpeed(String strStepsPerSecond){ | int setSpeed(String strStepsPerSecond){ | ||
| // Attempt to convert the string to a double: | // Attempt to convert the string to a double: | ||
| Line 204: | Line 233: | ||
| sprintf(outStr, "Unable to convert \"%s\" to float", strStepsPerSecond.c_str()); | sprintf(outStr, "Unable to convert \"%s\" to float", strStepsPerSecond.c_str()); | ||
| Particle.publish("setSpeed() error", outStr); | Particle.publish("setSpeed() error", outStr); | ||
| + | stepSpeed = 0; | ||
| + | return -1; | ||
| + | } | ||
| + | // Reject speeds over 1000 steps/sec: | ||
| + | if (stepSpeed<-1000 || stepSpeed>1000){ | ||
| + | Particle.publish("setSpeed() error", "speed must be between -1000.0 and 1000.0 steps/sec"); | ||
| stepSpeed = 0; | stepSpeed = 0; | ||
| return -1; | return -1; | ||
| } | } | ||
| + | // Technically, signed speed is velocity. | ||
| + | // Deconvolve "speed" into magnitude and direction: | ||
| + | if( stepSpeed>0 ){ | ||
| + | stepDirection = 1; // set positive direction | ||
| + | } else { | ||
| + | stepSpeed = stepSpeed*-1.0; // make speed positive | ||
| + | stepDirection = -1; // set negative direction | ||
| + | } | ||
| + | |||
| + | // Set direction pin: | ||
| + | digitalWrite(pinDirection, (stepDirection>0) ? HIGH : LOW ); | ||
| + | | ||
| // Calculate step period (ms between steps): | // Calculate step period (ms between steps): | ||
| stepPeriodMs = (stepSpeed==0) ? 1000 : 1000/stepSpeed; //ms | stepPeriodMs = (stepSpeed==0) ? 1000 : 1000/stepSpeed; //ms | ||
| - | waitBetweenStepsMs = stepPeriodMs - durationStepPulseMs; | + | |
| return stepPeriodMs; | return stepPeriodMs; | ||
| } | } | ||
| - | + | // Configures the A4988 Diretion input according to received direction request. | |
| - | int setDirection(String dirRequest){ | + | // The direction request must be precisely "1" or "-1". |
| + | // Whether 1 corresponds to CW or CCW depends on which coils you connected to A and B. | ||
| + | // If the resulting convention does not suit your needs, you can either swap | ||
| + | // the A1/A2 connections with the B1/B2 connections at the A4988, or modify the code here. | ||
| + | // Note that setSpeed also configures the direction pin. | ||
| + | int selectDirection(String dirRequest){ | ||
| // Expect "1" or "-1" | // Expect "1" or "-1" | ||
| if( strcmp(dirRequest.c_str(),"-1")==0 || strcmp(dirRequest.c_str(),"1")==0 ){ | if( strcmp(dirRequest.c_str(),"-1")==0 || strcmp(dirRequest.c_str(),"1")==0 ){ | ||
| Line 226: | Line 278: | ||
| | | ||
| // Set direction pin: | // Set direction pin: | ||
| - | digitalWrite(pinDirection, (stepDirection==1)?HIGH:LOW ); // set direction pin | + | digitalWrite(pinDirection, (stepDirection>0) ? HIGH : LOW ); |
| + | |||
| + | // Make the sign of the speed parameter match the selected direction: | ||
| + | stepSpeed = (stepDirection>0) ? stepSpeed : stepSpeed*-1.0; | ||
| | | ||
| return stepDirection; | return stepDirection; | ||
| } | } | ||
| + | |||
| Line 235: | Line 291: | ||
| void doOneStep(){ | void doOneStep(){ | ||
| digitalWrite(pinStep, HIGH); // start step pulse | digitalWrite(pinStep, HIGH); // start step pulse | ||
| - | delay(durationStepPulseMs); // hold step pulse | + | delayMicroseconds(durationStepPulseMicroseconds); // hold step pulse |
| digitalWrite(pinStep, LOW); // stop step pulse | digitalWrite(pinStep, LOW); // stop step pulse | ||
| } | } | ||
| - | |||
| </code> | </code> | ||
| + | |||
| + | ==== Remote Commanding ==== | ||
| The sketch above exposes 3 functions to the Particle Cloud. You can access these functions via [[http://digistu.mp/oakterm|OakTerm]], the Particle mobile app, or via direct http GET/POST exchanges. | The sketch above exposes 3 functions to the Particle Cloud. You can access these functions via [[http://digistu.mp/oakterm|OakTerm]], the Particle mobile app, or via direct http GET/POST exchanges. | ||
| * **motion(start)** starts motion, if you previously configured speed and direction. | * **motion(start)** starts motion, if you previously configured speed and direction. | ||
| * **motion(stop)** stops motion. | * **motion(stop)** stops motion. | ||
| - | * **setspeed(**[stepsPerSecond]**)** sets the motor speed in steps per second. | + | * **setspeed**(stepsPerSecond) sets the motor speed in steps per second. |
| - | * This accepts floating point values. For example, value 0.5 steps 1 step per 2 seconds. | + | * Accepts floating point values between -1000 and 1000. |
| - | * You can command a different speed while the motor is running. | + | * Example: speed of 0.5 steps/sec will step once per 2 seconds. |
| - | * setdir(-1 or 1) sets motor direction. | + | * Negative values set direction negative. |
| - | * Whether 1 corresponds to CW or CCW depends on which coil you hooked up to A and B. | + | * You can command a different speed while the motor is running. |
| - | * You can command a different direction while the motor is running. | + | * **setdir**(-1 or 1) sets motor direction. |
| + | * Whether 1 corresponds to CW or CCW depends on which coil you hooked up to A and B. | ||
| + | * You can command a different direction while the motor is running. | ||
| Line 268: | Line 327: | ||
| <form action="https://api.particle.io/v1/devices/MY_DEVICE_ID/setspeed" method="POST" target="replyFrame"> | <form action="https://api.particle.io/v1/devices/MY_DEVICE_ID/setspeed" method="POST" target="replyFrame"> | ||
| <input type="hidden" name="access_token" value="MY_ACCESS_TOKEN" /> | <input type="hidden" name="access_token" value="MY_ACCESS_TOKEN" /> | ||
| - | Set speed: | + | Set speed (-1000.0 : 1000.0): |
| <input type="text" name="speed" value=""> | <input type="text" name="speed" value=""> | ||
| <input type="submit" value="Submit"> | <input type="submit" value="Submit"> | ||
| Line 318: | Line 377: | ||
| =====Conclusion:===== | =====Conclusion:===== | ||
| - | Where to go from here, potential uses, etc. | + | **Congratulations!** You should now have a basic understanding of how stepper motors work and how to operate them, and you have a really fun platform to play with and expand on. Stepper motors are ubiquitous in the world of robotics, and I hope this tutorial serves as your gateway into this field. You can easily expand this setup to control up to 5 stepper motors simultaneously (or more, if you find a creative way to mux or share the direction signals). |
| + | |||
| + | As you operate your motor, **monitor the temperature of the A4988 motor driver** until you're confident that you have correctly configured the circuit and current limit, and that you're not asking the driver to move the motor too quickly. Consult your datasheet, or use [[http://www.daycounter.com/Calculators/Stepper-Motor-Calculator.phtml|this]] handy calculator to estimate max speed for your motor. If the A4988 gets hot, trim the current limit using the instructions provided in Build Step 3, and/or command slower speeds. | ||
| + | |||
| + | As noted in the stepper motor overview above, stepper motors can only reach a finite set of positions. A stepper motor with 200 steps/rev, for example, can only position within 0.9° from an arbitrary target position, when operated in full-step mode. If you require better accuracy, consider researching and experimenting with [[https://en.wikipedia.org/wiki/Stepper_motor#Microstepping|micro-stepping]]. The A4988 facilitates micro-stepping to 1/16th-step resolution, and enabling this function is quite simple. If you want to use 1/4, 1/8, or 1/16th step sizes exclusively, then you can configure the A4988 for this with 1, 2, or 3 additional jumpers or resistors, respecively. If you want to toggle between step sizes, then connect 3 Oak GPIO pins to the A4988 MS1, MS2, and MS3 pins and update the sketch to manage these pins. | ||
| + | |||
| + | Hope you found this useful! | ||