LCD Encoder Have Slow Response When Connected with Arduino Uno and Stepper Motor

Hi guys,

I have been planning to do a barn door tracker with LCD controls using Arduino Uno. I've seen photos and videos of people successfully interfacing both LCD and stepper motor with Arduino Uno so i decided to try it myself. I am a complete newbie in Arduino and i need some guidance and help here. I have successfully connect the Arduino Uno with a 12V 2A power supply with a NEMA 17 stepper motor controlled by A4988 driver, and a RepRap Full Graphic Smart Controller LCD (which requires ug8lib).

I have used the test codes from different sources and combined them together into one. The problem i'm facing here is when i turn the encoder, the numbers in LCD should update immediately but they have very very slow response or no response at all. Please help.

This is my code.

#include "U8glib.h"
  
#define encoderPin1 8
#define encoderPin2 9
#define pinEncButt 10

volatile int lastEncoded = 0;
volatile long encoderValue = 0;
  
long lastencoderValue = 0;
  
int lastMSB = 0;
int lastLSB = 0;

U8GLIB_ST7920_128X64_1X u8g(18, 16, 17); // SPI Com: en=18,rw=16,di=17
 
void draw(void) {
  u8g.setFont(u8g_font_fub11);
  u8g.drawStr( 0, 12, "Test Encoder");
 
  u8g.setFont(u8g_font_fub20);
   
  char buf[8];
  sprintf(buf, "%d %d",digitalRead(pinEncButt), encoderValue); 
  u8g.drawStr( 10, 40, buf);
 
  u8g.setFont(u8g_font_6x12);
  u8g.drawStr( 0, 63, "www.mauroalfieri.it");
}

void updateEncoder(){
  int MSB = digitalRead(encoderPin1);
  int LSB = digitalRead(encoderPin2);
  
  int encoded = (MSB << 1) |LSB;
  int sum  = (lastEncoded << 2) | encoded;
  
  if(sum==0b1101 || sum==0b0100 || sum==0b0010 || sum==0b1011) encoderValue++;
  if(sum==0b1110 || sum==0b0111 || sum==0b0001 || sum==0b1000) encoderValue--;
  
  lastEncoded = encoded; //store this value for next time
}
 

int dirPin =4;
int stepperPin = 5;

void setup(void) 
{
 pinMode(dirPin, OUTPUT);
 pinMode(stepperPin, OUTPUT);
 
  Serial.begin(9600);
  if (u8g.getMode()==U8G_MODE_R3G3B2)        { u8g.setColorIndex(255); }
  else if (u8g.getMode()==U8G_MODE_GRAY2BIT) { u8g.setColorIndex(3); }
  else if (u8g.getMode()==U8G_MODE_BW)       { u8g.setColorIndex(1); }
  else if (u8g.getMode()==U8G_MODE_HICOLOR)  { u8g.setHiColorByRGB(255,255,255);}
   
  pinMode(encoderPin1, INPUT);
  pinMode(encoderPin2, INPUT);
  pinMode(pinEncButt,  INPUT);
 
  digitalWrite(encoderPin1, HIGH);
  digitalWrite(encoderPin2, HIGH);
  digitalWrite(pinEncButt,  HIGH);
 
  attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);

}

void step(boolean dir,int steps)
 {
 digitalWrite(dirPin,dir);
 delay(50);
 
 for(int i=0;i<steps;i++)
 {
   digitalWrite(stepperPin, HIGH);
   delayMicroseconds(600);//Adjust the speed of motor. Increase the value, motor speed become slower.
   digitalWrite(stepperPin, LOW);
   delayMicroseconds(600);
 }
}

void loop(void)
{
 //steps per revolution for 200 pulses = 360 degree full cycle rotation
 step(true,1000);//(direction ,steps per revolution). This is clockwise rotation.
 delay(500);
 step(false,1000);//Turn (direction ,steps per revolution). This is anticlockwise rotation.
 delay(500);

 Serial.println( digitalRead(pinEncButt) );
  u8g.firstPage();  
  do { draw(); } while( u8g.nextPage() );
    
  delay(50);
 
}

My setup is as the photo attached.

Thank you for the patience and i hope i could get some help here.

Have a great day ahead.

using Arduino Uno

#define encoderPin1 8
#define encoderPin2 9

attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);

interrupt 0 is pin2 and interrupt 1 is pin 3 on the uno.

Back up a bit, and write a a piece of code which just reads the encoder and writes the count to the screen.

How are you planning to use the encoder counts in your program?

The motor code is "blocking" and this section will prevent other things from happening

step(true,1000);//(direction ,steps per revolution). This is clockwise rotation.
 delay(500);
 step(false,1000);//Turn (direction ,steps per revolution). This is anticlockwise rotation.
 delay(500);

I assume a rotary encoder with switch outputs like in this tutorial. Do you have the pullup resistors installed (no way to tell from the photo). If you enable the internal pullup resistors you won't need add them.
Use pinMode(pin, INPUT_PULLUP).

And the 500 ms delay in every iteration of loop() degrades responsiveness. The millis() for timing guide will help you get rid of delays.

What you are trying to achieve will require care to ensure that no part of the program blocks any other part. For example the motor movements must not block the encoder and neither must the updating of the display block the stepper motor or the encoder.

Have a look at the non-blocking stepper code in the second example in this Simple Stepper Code

...R
Stepper Motor Basics

cattledog:
interrupt 0 is pin2 and interrupt 1 is pin 3 on the uno.

Back up a bit, and write a a piece of code which just reads the encoder and writes the count to the screen.

How are you planning to use the encoder counts in your program?

The motor code is "blocking" and this section will prevent other things from happening

Does this means i have to change the interrupt value to 7 and 8 respectively? Thank you for the guidance on "blocking". I do not know of that at all.

groundFungus:
I assume a rotary encoder with switch outputs like in this tutorial. Do you have the pullup resistors installed (no way to tell from the photo). If you enable the internal pullup resistors you won't need add them.
Use pinMode(pin, INPUT_PULLUP).

And the 500 ms delay in every iteration of loop() degrades responsiveness. The millis() for timing guide will help you get rid of delays.

I'm not sure if a pullup resistor is installed or not cause it's a RepRap Full Graphic Smart Controller LCD unit which has the encoder directly. Thanks for the millis timing guide!

Robin2:
What you are trying to achieve will require care to ensure that no part of the program blocks any other part. For example the motor movements must not block the encoder and neither must the updating of the display block the stepper motor or the encoder.

Have a look at the non-blocking stepper code in the second example in this Simple Stepper Code

...R
Stepper Motor Basics

Thanks for the reference link! I was trying to build a barn door tracker with custom menu. That's why i wanted to have the encoder to select from the menu system.

I'm not sure if a pullup resistor is installed or not cause it's a RepRap Full Graphic Smart Controller LCD unit

I just checked my smart full graphic controller and the pullups are not installed on the board. You will need to supply them. Fortunately it is as easy as setting the pinMode for each of the encoder inputs (encoderPin1, encoderPin2, pinEncButt) to INPUT_PULLUP.

Does this means i have to change the interrupt value to 7 and 8 respectively?

No, Pins 7 and 8 are not external interrupt pins on the UNO. See attachInterrupt() - Arduino Reference

There are three paths for you to take.

  1. change the external interrupt pins used by the encoder to 2 and 3. No major changes to your code are required.Just change the A and B pins to 2 and 3 from 7 and 8. Use INPUT_PULLUP as pin mode. Change the wiring to go to the correct pins.
    .

  2. use pin change interrupts on pins 7 and 8 instead of external interrupts. If you don't have the external interrupt pins available, you can use pin change interrupts with more modifications to the code. A pin change interrupt library might be helpful to you.

  3. use "polling"(that is, digitalRead() of the pins each pass through loop) instead of interrupts to determine the changes in the two pins. This can work with manually operated encoders which move relatively slowly compared to the speed of the processor. However, using polling will require all code to be non blocking as Robin2 has stated. It will also require more modifcations to the code.

If you need to follow path 2 or 3, do some research and then come back with additional code or questions.

groundFungus:
I just checked my smart full graphic controller and the pullups are not installed on the board. You will need to supply them. Fortunately it is as easy as setting the pinMode for each of the encoder inputs (encoderPin1, encoderPin2, pinEncButt) to INPUT_PULLUP.

Thanks. I've changed them all to INPUT_PULLUP.

cattledog:
No, Pins 7 and 8 are not external interrupt pins on the UNO. See attachInterrupt() - Arduino Reference

There are three paths for you to take.

  1. change the external interrupt pins used by the encoder to 2 and 3. No major changes to your code are required.Just change the A and B pins to 2 and 3 from 7 and 8. Use INPUT_PULLUP as pin mode. Change the wiring to go to the correct pins.

I've selected path 1 to take since the interrupt pins on Uno is 2 and 3 and i don't really know any difficult codings. Thank you for your clear reference on the interrupt pins.

I now changed to motor movement and encoder function to change the direction of the motor. Now the LCD could work well on responsiveness, however, the motor shows lag/jittering movement instead of smooth movement with only 10 millis per step. I'm not sure whether is there any blocking in coding or not. I hope you guys could further guide me. By only uploading the sample code referred by Robin2, the motor could run smoothly. This code is a combination of LCD sample code and Robin2's sample code on stepper motor movement.

// testing a stepper motor with a Pololu A4988 driver board or equivalent

// this version uses millis() to manage timing rather than delay()
// and the movement is determined by a pair of momentary push switches
// press one and it turns CW, press the other and it turns CCW


#include "U8glib.h"
  
#define encoderPin1 2
#define encoderPin2 3

byte directionPin = 8;
byte stepPin = 9;

byte pinEncButt = 4;

unsigned long curMillis;
unsigned long prevStepMillis = 0;
unsigned long millisBetweenSteps = 10; // milliseconds

int flag =0;
int a=0;
int reading;
int zz=0;

volatile int lastEncoded = 0;
volatile long encoderValue = 0;
  
long lastencoderValue = 0;
  
int lastMSB = 0;
int lastLSB = 0;

U8GLIB_ST7920_128X64_1X u8g(18, 16, 17); // SPI Com: en=18,rw=16,di=17
 
void draw(void) {
  u8g.setFont(u8g_font_fub11);
  u8g.drawStr( 0, 12, "Test Encoder");
 
  u8g.setFont(u8g_font_fub20);
   
  char buf[8];
  sprintf(buf, "%d %d",digitalRead(pinEncButt), encoderValue); 
  u8g.drawStr( 10, 40, buf);
 
  u8g.setFont(u8g_font_6x12);
  u8g.drawStr( 0, 63, "www.mauroalfieri.it");
}

void updateEncoder(){
  int MSB = digitalRead(encoderPin1);
  int LSB = digitalRead(encoderPin2);
  
  int encoded = (MSB << 1) |LSB;
  int sum  = (lastEncoded << 2) | encoded;
  
  if(sum==0b1101 || sum==0b0100 || sum==0b0010 || sum==0b1011) encoderValue++;
  if(sum==0b1110 || sum==0b0111 || sum==0b0001 || sum==0b1000) encoderValue--;
  
  lastEncoded = encoded; //store this value for next time
}
 

void setup() { 
  
  Serial.begin(9600);
  Serial.println("Starting Stepper Demo with millis()");

  pinMode(directionPin, OUTPUT);
  pinMode(stepPin, OUTPUT);

   if (u8g.getMode()==U8G_MODE_R3G3B2)        { u8g.setColorIndex(255); }
  else if (u8g.getMode()==U8G_MODE_GRAY2BIT) { u8g.setColorIndex(3); }
  else if (u8g.getMode()==U8G_MODE_BW)       { u8g.setColorIndex(1); }
  else if (u8g.getMode()==U8G_MODE_HICOLOR)  { u8g.setHiColorByRGB(255,255,255);}
   
  pinMode(encoderPin1, INPUT_PULLUP);
  pinMode(encoderPin2, INPUT_PULLUP);
  pinMode(pinEncButt, INPUT_PULLUP);

    attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);

digitalWrite(pinEncButt,  HIGH);
  digitalWrite(directionPin, LOW);
}

void loop() { 
 
 curMillis = millis();
 readButtons();
 actOnButtons();
 singleStep();

u8g.firstPage();  
  do { draw(); } while( u8g.nextPage() );
}

void readButtons() {
reading = digitalRead(pinEncButt);
 if (reading == HIGH && flag == 0) {
    a=!a;
    flag=1;
 }
 else if(reading==LOW){
    flag=0;
 }

if (a==1)
{
 zz=1;;
}
else if (a==0)
{
 zz=0;
}
}



void actOnButtons() {

  if (zz == 1){
    digitalWrite(directionPin, HIGH);
  }
   else if (zz == 0){
    digitalWrite(directionPin, LOW);
  }
    
  Serial.println(digitalRead(directionPin));

 }
 


void singleStep() {
 if (curMillis - prevStepMillis >= millisBetweenSteps) {
 prevStepMillis += millisBetweenSteps;
 digitalWrite(stepPin, HIGH);
 digitalWrite(stepPin, LOW);
 }
}

I'm not sure whether is there any blocking in coding or not.

u8g.firstPage();  
  do { draw(); } while( u8g.nextPage() );
}

Unfortunately, the display code is blocking. I don't know how the execution time compares to the desired 10ms between stepper pulses, but it may be longer.

You may wish to update the screen, only when the values for the digitalRead(pinEncButt) or encoderValue have changed.

I'm not familiar with your display or the library, but it may be possible draw unchanging portions of the display
(for example "Test Encoder" and "www.mauroalfieri.it") in set up, or otherwise only one.