Problem contolling a stepper motor with serial communication

I'm trying to drive a stepper motor using a control signal from my notebook via USB.
I have an Arduino Mega 2560 connected to a DRV8825 stepper driver connected to a bipolar stepper motor according to this schematic: Pololu - Alternative minimal wiring diagram for connecting a microcontroller to a DRV8824/DRV8825 stepper motor driver carrier (full-step mode). (plus M1,M2 pulled up for 1/32 microstepping).

The following code is running on the arduino. I simplified it as fas as possible while still reproducing the problem. The "pwmPin" is the step pin on the driver.

int pwmPin = 11;
char pwmState = 0;
unsigned long period = 50;
unsigned long nextEdgeMicros;

void setup() {
 pinMode(pwmPin, OUTPUT);
 nextEdgeMicros = micros();
}

inline void pwm()
{
  if(micros() >= nextEdgeMicros)
  {
    nextEdgeMicros += period; // set next time
    pwmState = pwmState?0:1; // toggle PWM state
    digitalWrite(pwmPin, pwmState);
  }
}

void loop(){
 pwm();
}

With this code the motor is running smoothly. I know polling isn't the best way to do PWM. I also did all of this with timed interrupts, no difference.

Here is the problem, I want to update the "period" i.e. PWM frequency via USB. I start the following Python script on my notebook just for testing.

import serial
import time
ser = serial.Serial('/dev/ttyACM0', 9600)
while True:
	ser.write(' ')
	time.sleep(0.1)

This should make no difference. The notebook sends serial data which the arduino program just ignores. But it does, the motor stops. I dont have an oscilloscope so I cant say what exactly happens on the step pin. My guess is that the arduino program just stops pulsing the pin. But I have no idea why.

I also tested an arduino program that does Serial.read() and update the PWM period, no difference. It seems like the arduino program just stops.

Please help, I'm out of ideas.

Hi, welcome to the forum.

Opening a serial port on the computer usually resets the Arduino.
You have a rollover problem with micros(). I don't know if that could cause a lock-up. Perhaps it can.
The variable 'pwmState' is used in three different ways: as boolean true/false, as 0/1, as HIGH/LOW. It might be valid or not, my brain can't handle three different types of a single variable in a single code line :wink:

I don't know if the AccelStepper library is compatible with the Mega. Google with : mikem accelstepper

Thanks for your help. I tried the accelstepper library. Same result, the motor runs perfectly while no serial connection is open. As soon as I start that python script the motor stops (and makes a high frequency sound).

Here is the code:

#include <AccelStepper.h>

AccelStepper stepper( AccelStepper::DRIVER, 11, 52);

void setup() {
  stepper.setMaxSpeed(20000);
  stepper.setSpeed(20000); 
}

void loop(){ 
stepper.runSpeed();
}

From zero to 20000 ? That is not good for a stepper motor, even if you have micro-stepping.
The AccelStepper is to avoid that by smoothly increasing the speed.

The maximum for AccelStepper is about 4kHz, not 20kHz.
Could you try the speed 4000 and add a "stepper.setAcceleration(200.0);" and see what happens.

Peter_n:
Could you try the speed 4000 and add a "stepper.setAcceleration(200.0);" and see what happens.

It doesn't move at all.

#include <AccelStepper.h>

AccelStepper stepper( AccelStepper::DRIVER, 11, 52);

void setup() {
  stepper.setMaxSpeed(4000);
  stepper.setSpeed(0);
  stepper.setAcceleration(200.0);
}

void loop(){ 
stepper.run();
}

The 20000 has no unit so how can you tell it's too much? Looking at the motor that's about 2 rotations per second with 20000 and 0.5 rotations per second with 4000.

But itsn't that all besides the point? The problem is that it all breaks when the serial connection is opened.

You set the speed to 0.

In my opinion the point is to make it work.
I assume that the Serial port resets the Arduino Mega, and your frequency is too high to start a stepper motor, and there was a rollover problem with micros().

If you can make it work with AccelStepper, I'm curious what the Serial commands do. Perhaps they keep on resetting the Arduino and you have to disable that on the Mega board.

So the first step ( :wink: ) is to make the stepper work in a normal way.
Since your start frequency is very high, I thought that AccelStepper could make a smooth increase of the speed, but perhaps it can't get as high as 20kHz. But for now, it is only for testing.

I guess I found the problem, or at least narrowed it down. If I use the Arduino IDE Serial Monitor rather than the python serial script it works like a charm. I mapped ASCII a-z to different motor speeds and can now control it from the serial monitor.

Now I just have to find out why that python script didn't work.

Great, progress.

Did you read the serial data in the sketch ? I assumed that the problem was also when only opening the Serial port.
Do you have a oscilloscope ? To measure the DTR-100nF-/RESET path ? The DTR output of the usb-serial chip is used to reset the ATmega2560.

Finally, problem resolved.

One has to wait a bit between opening the connection and writing data to the arduino.

import serial
import time
ser = serial.Serial('/dev/ttyACM0', 9600)
time.sleep(2)
while True:
	ser.write(' ')
	time.sleep(0.4)

I'm glad you found the problem 8)
There is still a number of questions in my head, about the 20kHz steps and so on, but if you run into trouble, you could ask in a new topic.

A few points.

Rather than wait for a time to allow the Arduino to reset it is more reliable to wait for a message from the Arduino - perhaps Serial.println("Arduino is Ready") as first thing in setup(). See this Python demo.

I know polling isn't the best way to do PWM

You don't need to use a pwm pin for a stepper motor. Any I/O pin will work. And what you are doing is not PWM

Is your Arduino receiving the data correctly? If there is a step every 50 microsecs it will be busy.

You may already know everything in stepper motor basics

...R

PS ... I have just had another look at your code and I'm not sure this is correct

 if(micros() >= nextEdgeMicros)
  {
    nextEdgeMicros += period; // set next time
    pwmState = pwmState?0:1; // toggle PWM state
    digitalWrite(pwmPin, pwmState);
  }

I think it should be

  if(micros() >= nextEdgeMicros)
  {
    nextEdgeMicros += period; // set next time
    digitalWrite(pwmPin, HIGH);
    digitalWrite(pwmPin, LOW);
  }

so that it generates a step pulse each time. This may mean that you can double the value of period for the same stepper speed. I am assuming that the natural time taken by successive digitalWrite()s is sufficient to produce a wide-enough pulse.

...R