Stepper Motor runs extremly slow when used with Display and Sensor

Hello everyone,

I have a problem when running a stepper motor (Nema 17-04) with a lcd display and a light sensor. When running the Motor alone, it runs smoothly without any troubles. But as soon as i add other parts that i need for the project, it starts to rotate slower.

I have made a wiring diagramm and also recorded a short video to show the difference in the movement. I will also add both codes used to run the motor and the other parts.


#include <Wire.h>

#include <AccelStepper.h>


// Define pin connections
const int dirPin = 8;
const int stepPin = 9;

// Define motor interface type
#define motorInterfaceType 1

// Creates an instance
AccelStepper myStepper(motorInterfaceType, stepPin, dirPin);



void setup() {

  // set the maximum speed
	myStepper.setMaxSpeed(2000);

}

void loop() {

  myStepper.setSpeed(1000);
	myStepper.run();
}

#include <Wire.h>
#include "Adafruit_VL6180X.h"
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>

const int SetPotPin = A2;

// Define pin connections
const int dirPin = 8;
const int stepPin = 9;

// Define motor interface type
#define motorInterfaceType 1

// Creates an instance
AccelStepper myStepper(motorInterfaceType, stepPin, dirPin);

Adafruit_VL6180X vl = Adafruit_VL6180X();

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  lcd.begin();
  
  if (! vl.begin()) {
    lcd.println("Failed to find sensor");
    while (1);
  }
  lcd.println("Sensor found!");

  // set the maximum speed
	myStepper.setMaxSpeed(2000);

}

void loop() {
  //Read the value from the potentiometer
int SetPinValue =  analogRead(SetPotPin);
// Convert the value to a distance value from 0mm to 100mm
int set = map(SetPinValue, 0, 1023, 0, 100);
lcd.setCursor(0, 1);
lcd.print("Set: "); lcd.print(set); lcd.println("mm        ");

//take 10 readings from the sensor and calculate the mean
int sum = 0;
  for (int i = 0; i < 10; i++) {
  uint8_t sens = vl.readRange();
    sum += sens;
}
  uint8_t sens = sum / 10;

  uint8_t status = vl.readRangeStatus();

  if (status == VL6180X_ERROR_NONE) {
    lcd.setCursor(0, 0);
    lcd.print("Sens:"); lcd.print(sens); lcd.println("mm        ");
  }
  if  ((status >= VL6180X_ERROR_SYSERR_1) && (status <= VL6180X_ERROR_SYSERR_5)) {
    lcd.println("System error");
  }
  myStepper.setSpeed(1000);
	myStepper.run();
}

OnlyMotor (1).zip (5,2 MB)
MotorWithParts (1).zip (6,4 MB)

If any one knows a sloution for this problem please help.

Regards

Ali

Because the stepper can only step when loop() loops and the run function is called the step rate is dependent on how often loop() executes (loop time).

Printing to the LCD takes time. Do you really need to print to the LCD each time through loop?

I would only print to the LCD when the data changes and then only print the data. The labels would be printed in setup() and not need reprinted.

You have a for loop that takes 10 readings for an average. How long does that take? Can't you take one reading each time through loop and do the average after 10 times. That would reduce loop time.

1 Like

I need to print the vlaues of the sensor each time because im making a linear actuator with the stepper motor and i want to control the travel distance. Making the vlaues displayed on the LCD will help alot. Even if i completly stop the lcd, the motor still rotates slowly because of the sensor.

The 10 readings for the sensor is just for the accuracy and it doesn't take too much time.

I will do the adjustments to the lcd.

Is there any other Adjustments i can make?

How fast can human eyes read an LCD. More than 5 times a second if that?

Quantify " too much time". Even without the LCD if it takes 10ms the stepper speed is limited to 100 steps per second.

If you want to speed up the motor, you must speed up loop time.

So i did as you suggest. Now im taking only one reading and i moved the labels to void setupt. The Motor is rotating faster but it is still slower than without any other parts and the movement is not smooth (it vibrates).

Is there any other way to solve this?

When you make changes to your code, please post the new version so that we can keep up.

Sure. Here are the changes made:-


#include <Wire.h>
#include "Adafruit_VL6180X.h"
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>

const int SetPotPin = A2;

// Define pin connections
const int dirPin = 8;
const int stepPin = 9;

// Define motor interface type
#define motorInterfaceType 1

// Creates an instance
AccelStepper myStepper(motorInterfaceType, stepPin, dirPin);

Adafruit_VL6180X vl = Adafruit_VL6180X();

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  lcd.begin();
  lcd.setCursor(0, 0);
  lcd.println("Sens:");

  lcd.setCursor(0, 1);
  lcd.println("Set: "); 
  
  if (! vl.begin()) {
    lcd.println("Failed to find sensor");
    while (1);
  }
  lcd.println("Sensor found!");

  // set the maximum speed
	myStepper.setMaxSpeed(2000);

}

void loop() {
  //Read the value from the potentiometer
int SetPinValue =  analogRead(SetPotPin);
// Convert the value to a distance value from 0mm to 100mm
int set = map(SetPinValue, 0, 1023, 0, 100);

//lcd.print(set); lcd.println("mm        ");


  uint8_t sens = vl.readRange();


  uint8_t status = vl.readRangeStatus();

  if (status == VL6180X_ERROR_NONE) {

    //lcd.print(sens); lcd.println("mm        ");
  }
  if  ((status >= VL6180X_ERROR_SYSERR_1) && (status <= VL6180X_ERROR_SYSERR_5)) {
    lcd.println("System error");
  }
  myStepper.setSpeed(1000);
	myStepper.run();
}

As you can see i didn't change much. I just put the labels in the setup function and commented the line where it asks to print the vlaues on the display, just to check the speed. I also take one reading instead of 10.

These changes did indeed make a difference in the speed of the rotation but it still far from good.

When you use .setSpeed() its faster to use .runSpeed().

EDIT: I don't think you need to read the pot every pass through loop, and you might want to put that on a millis timer.

Try this to see how long it takes to get one range reading. Untested.

#include <Wire.h>
#include "Adafruit_VL6180X.h"
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>

const int SetPotPin = A2;

// Define pin connections
const int dirPin = 8;
const int stepPin = 9;

// Define motor interface type
#define motorInterfaceType 1

// Creates an instance
AccelStepper myStepper(motorInterfaceType, stepPin, dirPin);

Adafruit_VL6180X vl = Adafruit_VL6180X();

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup()
{
   lcd.begin();
   lcd.setCursor(0, 0);
   lcd.println("Sens:");

   lcd.setCursor(0, 1);
   lcd.println("Set: ");

   if (! vl.begin())
   {
      lcd.println("Failed to find sensor");
      while (1);
   }
   lcd.println("Sensor found!");

   // set the maximum speed
   myStepper.setMaxSpeed(2000);

}

void loop()
{
   //Read the value from the potentiometer
   int SetPinValue =  analogRead(SetPotPin);
   // Convert the value to a distance value from 0mm to 100mm
   int set = map(SetPinValue, 0, 1023, 0, 100);

   //lcd.print(set); lcd.println("mm        ");

   unsigned long startTimer = micros(); // reading start time
   
   uint8_t sens = vl.readRange();
   uint8_t status = vl.readRangeStatus();

   unsigned long endtimer = micros(); // reading end time
   Serial.print("time for one range reading = "); // print time
   Serial.print(endTimer - startTimer);
   Serial.println("us");

   if (status == VL6180X_ERROR_NONE)
   {

      //lcd.print(sens); lcd.println("mm        ");
   }
   if  ((status >= VL6180X_ERROR_SYSERR_1) && (status <= VL6180X_ERROR_SYSERR_5))
   {
      lcd.println("System error");
   }
   myStepper.setSpeed(1000);
   myStepper.run();
}

What do you get in serial monitor for the time?

Add 100us for the analogRead() to get an idea of loop() time.

Use setMaxSpeed() with run().

Sorry for late answering i had no accesses to the project. so after checking the time for one range reading i get 3184 us for one reading.

as far as i understand, milis() return the time.

How can i implement milis here?

this is my last update for the code using millis() :-

#include <Wire.h>
#include "Adafruit_VL6180X.h"
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>

const int SetPotPin = A2;

// Define pin connections
const int dirPin = 8;
const int stepPin = 9;

// Define motor interface type
#define motorInterfaceType 1

// Creates an instance
AccelStepper myStepper(motorInterfaceType, stepPin, dirPin);

Adafruit_VL6180X vl = Adafruit_VL6180X();

LiquidCrystal_I2C lcd(0x27, 16, 2);

unsigned long LastUpdate = 0;
const unsigned long UpdateInterval = 200;

void setup() {
  lcd.begin();
  lcd.setCursor(0, 0);
  lcd.print("Sens:   ");

  lcd.setCursor(0, 1);
  lcd.print("Set:    ");
  
  if (!vl.begin()) {
    lcd.println("Failed to find sensor");
    while (1);
  }
  lcd.println("Sensor found!");

  // set the maximum speed
  myStepper.setMaxSpeed(1000);
}

void loop() {
  if (millis() - LastUpdate >= UpdateInterval) {
  //Read the value from the potentiometer
  int SetPinValue =  analogRead(SetPotPin);
  // Convert the value to a distance value from 0mm to 100mm
  int set = map(SetPinValue, 0, 1023, 0, 100);

  lcd.setCursor(5, 0);
  lcd.print(set, DEC);
  lcd.print("mm   ");

  uint8_t sens = vl.readRange();

  uint8_t status = vl.readRangeStatus();

  if (status == VL6180X_ERROR_NONE) {
    lcd.setCursor(5, 1);
    lcd.print(sens, DEC);
    lcd.print("mm   ");
  }
  else if ((status >= VL6180X_ERROR_SYSERR_1) && (status <= VL6180X_ERROR_SYSERR_5)) {
    lcd.println("System error");
  }

  LastUpdate = millis();
  }
  myStepper.setSpeed(2000);
  myStepper.run();
}

It is running faster now.

Is there any suggestions i can use to improve it?

so after checking the time for one range reading i get 3184 us for one reading.

I'm not clear about the timing of the rest of the loop, but if total loop speed is 4ms for example, then you can not achieve more than 250 steps/second. You will not be able to reach 1000 steps/second. How fast do you need the stepper to run?

You can study the data sheet for the VL6180X to see if the reading speed can be increased.

The sensor uses i2c, and you may be able to set the bus speed to 400KHz instead of 100KHz.

You may be able to modify the library and patch some stepper.run() commands into the two while loops used in the reading of the sensor by the library.

uint8_t Adafruit_VL6180X::readRange(void) {
  // wait for device to be ready for range measurement
  while (!(read8(VL6180X_REG_RESULT_RANGE_STATUS) & 0x01))
    {myStepper.run();} //add this

  // Start a range measurement
  write8(VL6180X_REG_SYSRANGE_START, 0x01);

  // Poll until bit 2 is set
  while (!(read8(VL6180X_REG_RESULT_INTERRUPT_STATUS_GPIO) & 0x04))
    {myStepper.run();}//add this

  // read range in mm
  uint8_t range = read8(VL6180X_REG_RESULT_RANGE_VAL);

  // clear interrupt
  write8(VL6180X_REG_SYSTEM_INTERRUPT_CLEAR, 0x07);

  return range;
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.