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();
}
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.
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.
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).
#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.
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.
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?
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;
}