Need simple sample program for controlling stepper motor with encoder

I am looking for a very simple sample program that controls a stepper motor with an encoder.

I want to understand stepper motor control and encoder feedback. I have been searching for sample programs, and most involve controlling multiple steppers, especially for cnc, involve g-code, etc. I am having a hard time finding something simple and basic that doesn't include a bunch of extra things I need to sort through and make it more difficult for me to understand the basics.

I basically want a simple sample where I can just tell the motor to move a certain number of steps in a certain direction, get the feedback from the encoder, and adjust the position if necessary.

I am using a CHR-3429K-1210-50-ABHL motor with a L298N Dual H Bridge Stepper Motor Driver on either a ESP8266 or ESP32.

Any suggestions would be helpful!

Thanks!

Any sug

If you can’t figure out the stepper sample and the encoder sample separately, why would you expect to more easily figure out the more complex combined code?

Why involve feedback? That complicates the project. Using steepers powerful enough feedback is unneccesary.

I started from zero, knowing not much. Being programming for 45 years I started.
Here is one of my early codes trying to control a stepper. Running one stepper or running 3 thinking of a future CNC was just making the same code support 3 steppers. Use stepper X.

//#include <Stepper.h>

#define fwd HIGH
#define bwd LOW
#define motorX 1
#define motorY 2
#define motorZ 3

void setup()
{
  // put your setup code here, to run once:
  pinMode(2, OUTPUT); //can be used as x-step
  pinMode(3, OUTPUT); //y-step
  pinMode(4, OUTPUT); //z-step
  pinMode(5, OUTPUT); //can be used as x-dir
  pinMode(6, OUTPUT); //y-dir
  pinMode(7, OUTPUT); //z-dir
  pinMode(8, OUTPUT); //enable active low
  pinMode(13, OUTPUT);
  digitalWrite(8, LOW);//enable motorpower
  Serial.begin(9600);
}

//void stepmotor(int, int);

void stepmotor(int (motor), int(dir), int (dist))
{
  if ( dir == fwd )
  {
    Serial.print("Fwd motor ");
    Serial.println(motor);
  }
  else
  {
    Serial.print("Bwd motor ");
    Serial.println(motor);
  }
  Serial.print(motor);
  Serial.print(" ");
  Serial.print(dir);
  Serial.print(" ");
  Serial.println(dist);
  switch (motor)
  {
    case motorX:
      {
        digitalWrite( 5, dir);//digital dir does not owerride analoWrite(5, dir)
        delayMicroseconds(100);
        break;
      }
    case motorY:
      {
        digitalWrite( 6, dir);
        delayMicroseconds(100);
        break;
      }
    case motorZ:
      {
        digitalWrite( 7, dir);
        delayMicroseconds(100);
        break;
      }
  }
  digitalWrite(8, LOW);//enable motorpower
  for (int count = 0; count < dist; count++)
  {
    if (count == 7999)       Serial.println(" 8000                   ");
    else if (count == 15999) Serial.println("16000                   ");
    else if (count == 23999) Serial.println("24000                   ");
    else if (count == 31999) Serial.println("32000                   ");
    if (motor == motorX )
    {
      digitalWrite( 2, HIGH);// digital x-step
      delayMicroseconds(200);
      digitalWrite( 2, LOW);// digital x-step
      delayMicroseconds(200);
    }
    if (motor == motorY)
    {
      digitalWrite( 3, LOW);
      delayMicroseconds(200);
      digitalWrite( 3, HIGH);
      delayMicroseconds(200
                       );
    }
    if (motor == motorZ)
    {
      digitalWrite( 4, LOW);
      delayMicroseconds(200);
      digitalWrite( 4, HIGH);
      delayMicroseconds(200);
    }
  }
  delay(1000);
  digitalWrite(8, HIGH);//enable motorpower
}
void loop() {
  // put your main code here, to run repeatedly:
  // wait for command to start
  Serial.print("Start: ");
  while (Serial.available()== 0) {};
  char dummy = Serial.read();
  Serial.println(dummy);
  Serial.println("Going forward");
  //  analogWrite( 0, Low);//tst
  //  analogWrite( 1, Low);//tst
  //  analogWrite( 2, Low);// x-step analog KLAR
  //  analogWrite( 3, Low);// y-step analog KLAR
  //  analogWrite( 4, Low);// z-step analog¨KLAR
  //  analogWrite( 5, Low);// x-dir  analog KLAR
  //  digitalWrite(2, LOW);// x-step digitalt
  //  digitalWrite(3, LOW);// y-step digitalt
  //  digitalWrite(4, LOW);// z-step digitalt
  //  digitalWrite(5,LOW); // x-dir digitalt
  //  digitalWrite(6,HIGH);// y-dir KLAR HIGH == CW LOW == CCW
  //  digitalWrite(7,HIGH);// z-dir KLAR HIGH == CW LOW == CCW KLAR
  //  digitalWrite(8,LOW);//
  //  digitalWrite(9,LOW);//
  //  digitalWrite(10,LOW);//
  //  digitalWrite(11,LOW);//
  //  digitalWrite(12,LOW);
  delay(1000);
  stepmotor( motorX, fwd, 4000 );
  delay(1000);
  stepmotor( motorY, fwd, 4000 );
  Serial.println("Forward done");
  digitalWrite(13, LOW);
  delay(1000);

  //  analogWrite( 0, High);//
  //  analogWrite( 1, High);//
  //  analogWrite( 2, High);//x-channel KLAR
  //  analogWrite( 3, High);//y-channel KLAR
  //  analogWrite( 4, High);//z-channel KLAR
  //  analogWrite( 5, High);//x-dir     KLAR
  //  digitalWrite(2, HIGH); //x-step!!!
  //  digitalWrite(3, HIGH); //y-step!!!
  //  digitalWrite(4, HIGH); //z-step!!!
  //  digitalWrite(5, HIGH); //X-DIR CW ON HIGH CCW ON LOW
  //  digitalWrite(6, HIGH); //Y-DIR CW ON HIGH CCW ON LOW KLAR
  //  digitalWrite(7, LOW); //Z-DIR CW ON HIGH, CCW ON LOW KLAR
  //  digitalWrite(8,HIGH);//
  //  digitalWrite(9,HIGH);//
  //  digitalWrite(10,HIGH);//
  //  digitalWrite(11,HIGH);//
  //  digitalWrite(12,HIGH);//
  stepmotor( motorX, bwd, 4000 );// går finfint +200, -200 steg
  delay(1000);
  stepmotor( motorY, bwd, 4000 );// går finfint +200, -200 steg
  digitalWrite(13, HIGH); //
  delay(1000);
}

btkramer: I want to understand stepper motor control and encoder feedback.

The motor in your link is NOT a stepper motor. It is a DC motor with an encoder. If you want precise movement then you need a stepper motor.

Start simple. These links may help Stepper Motor Basics Simple Stepper Code

If the stepper motor is properly sized so it is well capable of moving the load without missing steps then there will be no need for an encoder to check its performance. Also, keeping up with an encoder is hard work for an Arduino. And even if you do know the motor has missed a step there is not a lot you can do except shut the job down and restart everything. If the motor can't make a step without the encoder it won't be able to make it just because the encoder says it is needed.

For example, 3D printers keep precision to fractions of a millimetre for hours without the need for an encoder.

If you do decide to use a stepper motor and if you need precise step-timing it would probably be best NOT to use an ESP8266 as the regular calls to the WiFi part can upset the timing.

...R

Oops, wrong link. That was for something else. The motor I'm using for this is 42 Stepper Motor With Feedback Encoder Motor. This is just to play around on and learn a bit though. Later on, I plan on using this CNC HSS86 Hybrid Driver with Closed Loop Servo Stepper Motor 8.5N.m

I actually have a pretty good idea about what I'm doing, at least theoretically with this equipment. My background is in PLC programming and I've been doing this kind of work for about 18 years, but I am just getting into this kind of hobby level stuff. I never worked much with the hardware side before, and this hardware is very different than what I am used to anyways. I am also just getting into C, which is quite different from the ladder logic I am used to.

I am looking for a simple program just to learn and experiment a bit with. In the more complex programs I was looking at I was having difficulty sorting out what parts of the program were for what, as I don't know much C, but I can replicate or modify it well enough once I know what it is what. Once I understand the basics, I can expand that out to something I can use, and also better understand the more complex programs.

I am kind of surprised at everyone's recommendation for not using an encoder when I haven't even said what I'm doing in this project. There are a lot of uses for an encoder, such as not having to home every time you start up, and there are more reasons for a stepper to miss steps than 'not enough power', such as from moving too fast. Before you say, 'well just slow it down then', consider that with an encoder you can easily compensate for missed steps with an encoder used for feedback and still maintain speed without sacrificing accuracy.

There are other situational uses too, including NOT wanting to use a motor powerful enough to not miss steps in any circumstance (because then things can get damaged or can give other undesired results), but still wanting to be aware of when it happens.

In regards to the ESP8266 not being powerful enough to both keep track of the encoder and poll the wifi, would the same be true of the ESP32? Otherwise, I can just disable the wifi, no? I really don't have much experience with these MCUs and I have honestly never really had to consider computing power before. Otherwise, could I have the ESP running the main program with a nano just keeping track of the encoder and sending the data back to the ESP, or would the latency be too high?

Thanks for the responses and supplied code. It does help.

btkramer: Oops, wrong link.

That makes it hard to give useful advice.

I am looking for a simple program just to learn and experiment a bit with.

Have you tried the examples in my link?

There are a lot of uses for an encoder, such as not having to home every time you start up

My german is very poor but I doubt if the motor comes with an absolute-position encoder and if it does not then you will have to home the motor to identify the ZERO position.

There are other situational uses too, including NOT wanting to use a motor powerful enough to not miss steps in any circumstance (because then things can get damaged or can give other undesired results), but still wanting to be aware of when it happens.

There are other ways to avoid damage - a big RED switch or its equivalent. IMHO choosing a motor that is only marginally capable is building up trouble for yourself.

In regards to the ESP8266 not being powerful enough to both keep track of the encoder and poll the wifi, would the same be true of the ESP32?

My reservation is that it may not be able to time the steps consistently if it is distracted by WiFi. Using an ESP8266 in conjunction with an Arduino gets around that problem as the Arduino could choose when it takes time out to communicate with the ESP8266.

By all means do some tests and post your results to allay my doubts about the ESP8266 behaviour. It would be useful information for others.

...R

Robin2: That makes it hard to give useful advice.

Sorry about that, copy paste error. Mea culpa.

Have you tried the examples in my link?

I checked it out, it gives me some info, but I am still looking for something with encoder feedback, failure state and position correction. Thanks though, I appreciate it.

My german is very poor but I doubt if the motor comes with an absolute-position encoder and if it does not then you will have to home the motor to identify the ZERO position.

Yes, you will of course have to home the machine to calibrate, but as I say, not every time you start up. Once you have homed it, it can keep track of it's location and you can save the location to the EEPROM which will persist through power cycles, and if the device gets moved or misses steps, it doesn't need to rehome. This can be a time saver depending on the application.

There are other ways to avoid damage - a big RED switch or its equivalent. IMHO choosing a motor that is only marginally capable is building up trouble for yourself.

You want your stepper to be properly suited to the project, no bigger. Powerful enough to do the job it's supposed to do without missing steps but also not so powerful that it powers through a problem that it shouldn't be powering through. Obviously, you want to give it a bit of a margin of error, but not more than is reasonable. This really depends on the application.

For instance, one of the things I am making is an automatic moving saw stop to measure miter cuts that would run up and down the workbench of my miter saw. I should use a fairly weak motor for this. One that is more than powerful enough to move the stop back and forth, but if it bangs into something substantial on the bench, it should pause, beep and need to be resumed after the obstruction is cleared. I can only do this with a relatively weak motor and an encoder to know I missed steps. If I used a giant stepper that wouldn't stop for anything, not only is it overkill and a waste of money, it could knock all kinds of stuff around that it shouldn't, potentially into the saw operator, god forbid while he was operating the saw, which could be quite dangerous.

This kind of feedback based control is required, among other things, for any automated machinery that is not physically locked out by a cage or some such to prevent human contact with the machinery in industrial settings.

E-stops are certainly a must for any device that can do damage, but 99% of the time, even if you are watching it like a hawk, by the time you react and slap that button its already too late. And really, who watches their machine alertly for hours on end as it runs?

If the machine stops as soon as it knows it missed a step, it's nearly instant and has only moved an mm or less into trouble, and can save a lot of heartache. The problem can also be corrected and the machine can resume without losing it's work or the machine. I have had to replace machinery in factories without feedback control because it lost it's position and destroyed itself for want of a simple encoder. Needless to say, the new machine was closed loop.

I really don't understand why everyone here is so against encoders. I see it in other posts too. It doesn't cost that much extra, is not that much more difficult to program and only adds benefits to your project, is safer, and I can't think of a single instance where it harms. So what if you never miss steps and never need the encoder? What did you really lose?

My reservation is that it may not be able to time the steps consistently if it is distracted by WiFi. Using an ESP8266 in conjunction with an Arduino gets around that problem as the Arduino could choose when it takes time out to communicate with the ESP8266.

By all means do some tests and post your results to allay my doubts about the ESP8266 behaviour. It would be useful information for others.

That's what I was thinking. I don't doubt you are correct. I really have no experience with this hardware and software. Though I could disable the WiFi too. I don't really need it. I'll probably play around with both methods.

Any thoughts on the ESP32? Powerful enough to have my cake and eat it too?

Thanks again for the feedback.

Steppers and encoders. What are the results if the stepper stops between flags in the encoder?

Paul

Paul_KD7HB: Steppers and encoders. What are the results if the stepper stops between flags in the encoder?

Paul

I'm not sure I understand. You mean what's the worst that could happen in that case? At worst your calibration could end up being off by a half step. I don't know of many applications where that would be an issue. Especially in the case of the hobby level equipment we are controlling with arduinos. Even re-homing is not going to give you the half step accuracy needed to correct that incongruity. Again, at least not with the kind of motors, encoders and homing switches we are dealing with here.

btkramer: I checked it out, it gives me some info, but I am still looking for something with encoder feedback, failure state and position correction.

I can see the logic behind what you have in mind but you have probably realized by now that what you want to do is not common among Forum users so I guess you are going to have to write your own code.

There are plenty of examples on the Forum of using encoders.

What is the maximum number of encoder steps per second the Arduino must detect?

...R

Don't know if that may help you, but I posted a code for the arduino DUE to leverage both the hardware quadrature decoder and the Timer Counter Stepper Motor Register. This code simulates the output of an quadrature encoder connected to a stepper motor by reading directly the 2 outputs (PHA/PHB) of the Timer Counter.

This code gets the absolute position of the encoder while the Timer Counter Stepper Motor register is programmed to drive a stepper motor in opposite direction every 10 seconds (see replies #86, 87).

In fact the code is super simple because the DUE has hardware features for quadrature encoder reading and Stepper Motor handling.

https://forum.arduino.cc/index.php?topic=140205.75

Robin2: I can see the logic behind what you have in mind but you have probably realized by now that what you want to do is not common among Forum users so I guess you are going to have to write your own code.

I figured. That's why I was hoping for something super simple just to get me on the right path that I could extrapolate from on my own. Mainly I am looking for what libraries to use, what the commands I need in C are, syntax, etc. I more or less know how to write it otherwise. I guess I will need to spend a bit more time figuring that stuff out. I've managed to cobble together some fairly complex control code in languages I don't really know before by just modifying existing programs where I at least knew what it was doing and where. I was hoping to do the same here. Oh well, maybe I'll just break down and take a class on C.

What is the maximum number of encoder steps per second the Arduino must detect?

...R

Not sure yet. At this point I am just testing the small motor on the bench to work out the basics of the code and figure out how everything will operate. Then I need to figure out how big a motor I need and how fast I want it to move, ratio of motor speed to pulley size, etc. This is the part I don't have much experience with, as most systems I program are designed by a mechanical engineer and are already installed and ready to go by the time I get to them. Again, I understand the fundamentals pretty well, but calculating the requirements from scratch and selecting the proper hardware is new to me.

I don't think I will be pushing the limits of the arduino's capabilities though in terms of step counting.

Thanks for the help.

So, I have already worked out a simple program to read the input from a standalone manual rotary encoder and display the count on an OLED to use as the method to input the desired distance required. It’s actually not done yet, and the OLED text is currently just there as a placeholder. It is just displaying the count to serial for now. This works pretty well, unlike most of the encoder code samples I tried and debounces the input well, which I was surprised to find I needed to do. I ended up scrapping this input method though and decided on using a Nextion touch screen instead, but I could reuse this code.

I am not sure if I need to do this with the encoder built in to the stepper. This is what I have.

#include <elapsedMillis.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>//OLED                     
#include <Adafruit_SSD1306.h>//OLED

//OLED 1 Configure Start
#define OLED_RESET 4                          
Adafruit_SSD1306 display(OLED_RESET);         
//OLED 1 Configure End

//Rotary 0 Configure Start
#define ROTARY0_PIN1 5
#define ROTARY0_PIN2 2
#define ROTARY0_BTN 4
int ROTARY0_VAL = 0; 
int ROTARY0_BTNVAL = 0;
int BTN_RST = 0;
const char ttable[7][4] = {{0x0, 0x2, 0x4,  0x0}, {0x3, 0x0, 0x1, 0x40}, {0x3, 0x2, 0x0,  0x0}, {0x3, 0x2, 0x1,  0x0},  {0x6, 0x0, 0x4,  0x0}, {0x6, 0x5, 0x0, 0x80}, {0x6, 0x5, 0x4,  0x0},};
volatile char state0 = 0;
char rotary0_process() {
  char pinstate0 = (digitalRead(ROTARY0_PIN2) << 1) | digitalRead(ROTARY0_PIN1);
  state0 = ttable[state0 & 0xf][pinstate0];
  return (state0 & 0xc0);
}
//Rotary 0 Configure End


void setup() {
  Serial.begin(115200);
  setupOLED1();
  setupRotary1();
}


void loop() {
  loopOLED1();
  loopRotary1();
}

void setupOLED1(){
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //initialize with the I2C addr 0x3C (128x64)
  display.clearDisplay();
}


void loopOLED1(){
  display.setTextSize(1); //V Height 1=7,+10       
  display.setTextColor(WHITE);
  display.setCursor(30,3); //H, V
  display.print("->");
  display.setCursor(45,3); //H, V
  display.println("5ft");
  display.setCursor(45,13); //H, V 
  display.println("10in");
  display.setCursor(45,23); //H, V  
  display.println("13/16");  
  display.display();            
  display.clearDisplay();       
}

void setupRotary1() {
  pinMode(ROTARY0_PIN1, INPUT);
  pinMode(ROTARY0_PIN2, INPUT);
  pinMode(ROTARY0_BTN, INPUT_PULLUP);
}

void loopRotary1() {
   char result0 = rotary0_process();
     if (result0){
       result0 == 0x40 ? ROTARY0_VAL++ : ROTARY0_VAL--;
       Serial.print(ROTARY0_VAL);
       Serial.println(result0 == 0x40 ? "CW" : "CCW");
     }
   if ((digitalRead(ROTARY0_BTN) == LOW) && (BTN_RST == 0)){
    Serial.println("Button");
    BTN_RST = 1;  
   }
   if (digitalRead(ROTARY0_BTN) == HIGH){
    BTN_RST = 0;  
   }
}

Do you think the encoder portion will translate well to reading the encoder of the stepper?

btkramer: Do you think the encoder portion will translate well to reading the encoder of the stepper?

To be honest, NO - unless the motor is rotating very slowly and there are not many encoder pulses per revolution. I think it will be far too slow and too easy to miss encoder pulses

I would use an interrupt to detect encoder pulses. If you look at this DC motor control program you should get the idea - just keep in mind that it only produces 1 pulse per revolution and it makes no attempt to detect direction as I already know the direction.

It is very important to do as little as possible inside an ISR so that it complete very fast.

...R