stepper motor control with lcd display

Hi everyone,

For my project I’m trying control the speed of a stepper motor using a pot and at the same time displaying the speed in rpm on an i2c lcd display. The code which I have gotten in the beginning is shown below:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2);

// Include Stepper motor library from Arduino's database
#include <AccelStepper.h>
#include <Wire.h>

// Define the stepper pins for the easy driver and create stepper object - stepper1. You can name the object as you wish.
AccelStepper stepper1(AccelStepper::DRIVER, 9, 8);


// Define control switch pins
#define  START_PIN  4
#define  STOP_PIN  3

// Define our analog potentiometer input pin (Analog 0)
#define  SPEED_PIN 0

// Define our maximum and minimum speed in steps per second (Scale potentiometer minimum and maximum using this as a reference)
#define  MAX_SPEED 5000
#define  MIN_SPEED 0.1

void setup() {
  
  lcd.init();
  lcd.backlight();
  lcd.setCursor(4, 0);
  lcd.print("RPM");
  stepper1.setMaxSpeed(5000.0 );
  pinMode(START_PIN, INPUT_PULLUP);
  pinMode(STOP_PIN, INPUT_PULLUP);
}
void loop() {
  static float current_speed = 10000;       // Holding current motor speed
  static int analog_read_counter = 1000;    // Counts down to 0 to fire analog read
  static char sign = 0;                     // Holds -1, 1 or 0 to turn the motor on/off and control direction
  static int analog_value = 0;              // Holds raw analog value.
 
   //If a switch is toggled down (low), set the sign value appropriately
  if (digitalRead(START_PIN) == 0) 
  {
    sign = 1;
  }
  else if (digitalRead(STOP_PIN) == 0) 
  {
    sign = 0;
  }
  
  if (analog_read_counter > 0) 
  {
    analog_read_counter--;
  }
  else
  {
    analog_read_counter = 20000;
    // Now read the pot (from 0 to 1023)
    analog_value = analogRead(SPEED_PIN);
    
    // Give the stepper a chance to step if it needs to
   stepper1.runSpeed();
    
    //  And scale the pot's value from min to max speeds
    current_speed = sign * (((analog_value/1023.0) * (MAX_SPEED - MIN_SPEED)) + MIN_SPEED);
    stepper1.setSpeed(current_speed);
    int speedrpm;
    speedrpm = ((current_speed)/1600)*60;
  if (speedrpm > 0)
    {
    lcd.setCursor(0, 0);
    lcd.print(speedrpm);
    }
    else if (speedrpm < 10)
    {
      lcd.setCursor(0, 0);
      lcd.print("000");
    }
 
  if(speedrpm < 100)
    {
      lcd.setCursor(0, 0);
      lcd.print("0");
      lcd.setCursor(1, 0);
      lcd.print(speedrpm);
      
    }
  }
    stepper1.runSpeed();
  }

The system works, however, the fundamental problems with generating step pulses in software like AccelStepper is that If you have another function (like updating an LCD) which takes much time at all, it will cause skips in the step pulses. I’m trying to avoid this by using a second arduino to share the workload and using the Wire library to create an I2C bus. The codes for the master and slave arduino are shown below:

Master arduino:

// Include Stepper motor library from Arduino's database
#include <AccelStepper.h>
#include <Wire.h>

// Define the stepper pins for the easy driver and create stepper object - stepper1. You can name the object as you wish.
AccelStepper stepper1(AccelStepper::DRIVER, 9, 8);


// Define control switch pins
#define  START_PIN  4
#define  STOP_PIN  3

// Define our analog potentiometer input pin (Analog 0)
#define  SPEED_PIN 0

// Define our maximum and minimum speed in steps per second (Scale potentiometer minimum and maximum using this as a reference)
#define  MAX_SPEED 10000
#define  MIN_SPEED 0.1

void setup() 
{
  // Maximum stepper value 
  stepper1.setMaxSpeed(10000.0);
  Wire.begin();
  // Using pullups for control pins
  pinMode(START_PIN, INPUT_PULLUP);
  pinMode(STOP_PIN, INPUT_PULLUP);
 
}

void loop() 
{
  static float current_speed = 10000;       // Holding current motor speed
  static int analog_read_counter = 1000;    // Counts down to 0 to fire analog read
  static char sign = 0;                     // Holds -1, 1 or 0 to turn the motor on/off and control direction
  static int analog_value = 0;              // Holds raw analog value.
  int speedrpm = 0;
 
   //If a switch is toggled down (low), set the sign value appropriately
  if (digitalRead(START_PIN) == 0) 
  {
    sign = 1;
  }
  else if (digitalRead(STOP_PIN) == 0) 
  {
    sign = 0;
  }
  
  if (analog_read_counter > 0) 
  {
    analog_read_counter--;
  }
  else
  {
    analog_read_counter = 5000;
    // Now read the pot (from 0 to 1023)
    analog_value = analogRead(SPEED_PIN);
    
    // Give the stepper a chance to step if it needs to
    stepper1.runSpeed();
    
    //  And scale the pot's value from min to max speeds
    current_speed = sign * (((analog_value/1023.0) * (MAX_SPEED - MIN_SPEED)) + MIN_SPEED);
    stepper1.setSpeed(current_speed);
    speedrpm = ((current_speed)/1600)*60;
    Wire.beginTransmission(9);
    Wire.write(speedrpm);
    Wire.endTransmission();
   
   
 }


  // This will run the stepper at a constant speed
  stepper1.runSpeed();

Slave arduino:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2);
int speedrpm = 0;

void setup() {
  lcd.init();
  lcd.backlight();
  lcd.setCursor(4, 0);
  lcd.print("RPM");
  // Start the I2C Bus as Slave on address 9
  Wire.begin(9); 
  // Attach a function to trigger when something is received.
  Wire.onReceive(receiveEvent);
}
void receiveEvent(int bytes) {
  speedrpm = Wire.read();    // read one character from the I2C
}
void loop() {
  if (speedrpm > 0)
    {
    lcd.setCursor(0, 0);
    lcd.print(speedrpm);
    }
    else
    {
      lcd.setCursor(0, 0);
      lcd.print("000");
    }
    if(speedrpm < 100)
    {
      lcd.setCursor(0, 0);
      lcd.print("0");
      lcd.setCursor(1, 0);
      lcd.print(speedrpm);
      
    }
  }

Basically, the idea is to have a master arduino controlling the motor while simultaneously sending information to a slave arduino which then displaying the speed in rpm on an i2c LCD screen. That was my takes on the solution, however, it doesn’t work because I only recently learned that you can’t have 2 masters on one i2c bus and having the slave arduino controlling the i2c lcd display basically makes the slave arduino a master. Another approach which I’m thinking of is to have the slave arduino controlling the motor while sending data to the master arduino which then displaying the speed on the lcd; that will create only one master on the i2c bus.

I know this is a bit of an overkill but I’m also trying to implement a timer for the system, it will create too much workload for 1 arduino so I decided to follow this solution. I really want to know what you think of this solution and if there is any thoughts or suggestions, I would be extremely happy to hear. Thank you for reading.

Using two Arduino in a project is often a bad idea which makes things more difficult by causing more problems than it solves.

You would be better to look for a different stepper driver or library, which can be driven with Arduino PWM pins, which are controlled by the Atmega's timer hardware and can continue to run while the CPU is busy with other tasks.

Your first program is updating the LCD on every iteration of loop(). That's completely unnecessary. It would probably be sufficient to update the LCD 2 or 3 times per second.

Another thought is to write your own stepper code using millis() or micros() to determine when a step is due and make sure only to update the LCD immediately after a step has taken place - which gives the longest possible time before the next step.

You may be able to achieve the same effect with the AccelStepper library by checking the distanceToGo() and updating your LCD immediately after that value changes.

...R
Stepper Motor Basics
Simple Stepper Code
Simple acceleration code.