Can I use a stepper motor for this project?

I am starting a new project using a Raspberry Pi Pico driving a motor in order to spin a set of small gears. To get started and test the overall design (the custom gears in particular), I’ve decided to use an ordinary DC motor because it is well-suited for high rpm operation. But ideally I’d like to have more precise control over final (angular) position after each “spin” of the device, and for that I suspect I would need a stepper motor. However, it seems that stepper motors need a lot of voltage to get past a few rpms and I’m looking for around 60rpm operation. The final device must run off of its own self-contained power source, namely a 9V battery. I know that this somewhat limits the speed and torque I can get out of a motor, but that’s the design spec.

Basically, each time the user pushes a button, the motor needs to run for two or three seconds at a rotational speed of 1 rotation per second (60rpm). The main gear attached to the motor stem will drive three smaller gears (3:1 ratio). I’ve added a link to a little animation showing this in action.

I’m reaching out to the experienced makers here to guide me and tell me if my requirements can be met at a fairly low cost (under $50 for both motor and controller). And if so, what stepper motor + controller combo would you recommend?

Anim Demo

Hi zslane
If you are talking about the little block style 9V battery I don’t think you will get much run time out of it. Particularly if you are operating the Pico as well.

I have a couple of different brands here but I don’t see anything like a mAhr rating on any of them. I don’t think these batteries are that great in this respect.

Could be wrong, others might have had some experience with this scenario.
Cheers Bob

Hey @zslane,

Welcome to the forum!

You’re on the right track thinking about switching from a basic DC motor to a stepper if you want precise angular control. A DC motor will definitely give you the RPMs, but without an encoder it’s not easy to stop at the same position every time. Steppers, on the other hand, are designed for this kind of predictable movement and positioning.

For your stated goal of about 60 RPM (1 rotation per second), that’s very achievable with a standard NEMA 17 stepper motor (200 steps/rev, 12V 350mA). At that speed you’re only asking the Raspberry Pi Pico to generate around 200 step pulses per second, which is very straightforward.

To drive it, I’d recommend a simple and inexpensive A4988 stepper driver board. It works perfectly with the Pico (takes basic STEP/DIR signals), can handle much higher current than your motor requires, and also gives you the option of microstepping for smoother motion. Both parts together are just north of your ~$50 budget.

One thing to note: a little rectangular 9V battery won’t supply enough current for both the motor and the Pico. For a portable setup you’ll get much better results using a small Li-ion/LiPo pack (2S or 3S) or even a few rechargeable NiMH AAs in series. That way, your motor will have the torque and runtime it needs.

In short:

  • Motor: NEMA 17 (12V, 350mA)
  • Driver: A4988 stepper driver carrier
  • Power: 9–12V battery pack (not a 9V block battery)

That combo should give you the reliable, repeatable motion you’re looking for while keeping the parts cost reasonable.

1 Like

I am not very familiar with Li-ion and LiPo battery packs. What does the 2S or 3S designation mean? Is there a mAh rating I should be aiming for, or is this purely a case of “the more, the better”?

This device has some fairly tight space constraints: a box about 4” x 4" x 3” (width x depth x height), so I assume that will limit the size of the pack and therefore the mAh I can expect from it, though that might be mitigated somewhat by being rechargeable?

1 Like

Hey @zslane,

The “2S” and “3S” designations just describe how many cells are wired in series inside the pack:

  • 1S = a single cell (~3.7V nominal, 4.2V fully charged)
  • 2S = two cells in series (~7.4V nominal, 8.4V max)
  • 3S = three cells in series (~11.1V nominal, 12.6V max)

So for your case:

  • A 2S pack (≈7.4V) would run the stepper but with lower torque.
  • A 3S pack (≈11.1V) is closer to your motor’s rated 12V, which will give you more reliable performance at your target 60RPM. That’s the setup I’d recommend.

You’re correct that the physical size of the battery will dictate the mAh capacity you can expect. For your use case (Pico + NEMA 17 at around 350mA/phase), you really don’t need a huge pack because the motor will only be running in short bursts (a couple of seconds per button press).

A 1000–2000mAh 3S Li‑ion/LiPo pack will be plenty for testing and should give you a good amount of runtime inside your 4" × 4" × 3" enclosure. Since it’s rechargeable, you can easily top it up between sessions rather than oversizing the pack.

1 Like

One other little detail I’m confronted with is that the battery pack will need to power the Pico as well. It also looks like the A4988 needs a separate 5v logic voltage feed (it doesn’t draw it internally from the VMOT like some other controllers do). Presumably that means I would need to use something like a voltage regulator to draw off a 5v feed from the battery pack for the Pico (VSYS) and the A4988’s logic voltage input (VDD)?

1 Like

Hi @zslane,

That’s exactly right! the A4988 uses two separate supplies:

  • VMOT (motor supply): this is where you connect your battery pack (e.g. 9–12V from a 3S Li‑ion/LiPo). It directly powers the stepper coils.
  • VDD (logic supply): this is a separate 3.3V–5V input that powers the A4988’s logic side.

You can use a small 5V buck converter to feed into the Picos VSYS pin, and the A4988s VDD. Something like this is a very cheap option - Adjustable Switching power supply.

1 Like

When you talk about the Pico generating 200 “pulses” per second to drive the motor, are you referring to a HIGH/LOW output sequence as a “pulse”? So, something like this?

for _ in range(200):
    step_pin.high()
    step_pin.low()
    sleep_ms(5)
1 Like

Hi zslane
More or less I think BUT you need another “sleep” between "step_pin"s. If you don’t the pulse will be so fast to be unmanageable. Both "sleep"s should be the same to get an approximate 50% duty cycle.The value of the sleeps will determine the frequency and ultimately motor speed.

There are probably other easier ways to do this to get some better control of frequency thus speed but for a stepper the duty cycle should be 50%.
Cheers Bob

1 Like

Hey @zslane,

Yes, Each complete HIGH→LOW transition on the STEP pin is seen by the driver as a single step command for the motor.

For your code, you’ll want to make sure the pulse stays HIGH for at least the minimum time specified in the A4988 datasheet before pulling it LOW again. Something like this

step_pin.high()
sleep_us(xx)
step_pin.low()
sleep_ms(5)

Hi Ryan, zslane
If you want 200 pulses per second both “sleeps” theoretically should be 2.5mSec.The off time will be a tiny bit longer due to processing time around the loop
Cheers Bob

1 Like

@Robert93820 Yeah thats a better way of doing it.

@zslane it’ll likely be sleep_ms(2) and sleep_ms(3) as I believe timing in micropython can be a little coarse. This way the pulses are long enough to be recognized cleanly, and your motor speed will be consistent.

It’s probably overkill for this application, but you can do some research into hardware timers and PWM for generating precise step pulses.

According to a quick Micropython test I just did on my Pico, a sleep time of 233µs between each high and low signal yields an average iteration time (over 200 iterations) of 500.25µs. That’s close enough to 5ms for this project.

And if I read the datasheet (databook?) correctly, the minimum pulse width is only 1µs, so fortunately I’m not in any danger of generating pulses that aren’t long enough.

1 Like

Hi zslane

Sorry, decimal point 1 spot out.
500µSec is 0.5mSec 1000µSec = 1mSec.
Cheers Bob

Oh, yes, thank you. A whole order of magnitude off. Yikes.

So now I’ve got it at 2486µs between signals and it’s as close to 5ms on average as I’m going to get.

1 Like

Hi Ryan

Don’t know much about Micropython but this is very true of Arduino.There is a delay with “digitalWrite” while it does a bit of housekeeping before it even thinks about action. This is several microseconds. While looking at this I found a sneaky one, “digitalWriteFast” which seems to bypass the housekeeping.

I was trying to get a pulse generator using Rotary encoders with the handy Seesaw encoder board which communicates via I2C. The processing time and in particular interrogating the I2C inputs made this idea unusable. A contributor suggested I write directly to the PWM registers as the PWM runs all the time and only changes when the register changes. This was very successful and I was able to generate very accurate pulses (as accurate as the Arduino clock anyway).

I did put a post upon my measurements with this timing using digitalWrite with Oscilloscope pics etc quite some time ago. I think it was called “Arduino shortcomings” or something like that.

I did not post anything about my success with this as I did not actually build it up as a project, I already have a Function Generator so had no need. I might yet publish this as a general interest subject if I can find all the details.
Cheers Bob

Hey @Robert93820,

That’s a great breakdown, thanks for sharing your experience. You’re absolutely right, the overhead in functions like digitalWrite (and the equivalent in MicroPython) can really add up when you’re trying to chase precise timing.

I was able to track down your forum post “Arduino UNO R3 some limitations”. Very in-depth write-up there, I’ll have to have a better read over my lunch break today :slight_smile:

For applications that need exact timing, I imagine that either PWM or PIO are preferred ways to achieve the necessary precision. For my project, I don’t need such exact timing. The difference between hitting a pulse width of exactly 5ms and averaging something like 4.9ms per pulse won’t be perceivable by the user, so I’m not going to worry about it and just use simple for-loops and conventional Micropython pin.high()/pin.low() and sleep_us() calls.

I’ve also written up some code to implement a “spin up” phase so that the motor isn’t asked to operate at my top speed (60rpm) from a dead stop. However, my spin-up time is quite short so I don’t know how effective it will be.

1 Like