Stepper motor and Servo motor - delay messing up the code :(

Hi, I’m doing this project where I have to rotate the servo motor depending on a reading from an LDR and and rotate a stepper motor depending on how much time has elapsed - pretty straight forward. I worked out the codes for the different function separately and it worked as expected but when I put them together as one code - havoc!!

The problem was with the servo code I had to use roughly around 1500 delay, which throws of the time elapsed for the stepper motor code, and also I used some delay in the stepper code too. (stepper motor moves after every 6s, originally I had delay in the stepper motor code too which I scraped).

Anyway trying to fix the problem as it is I fiddled with the stepper motor code by looking at the Serial.print of time elapsed and counting 6 seconds and modifying the if conditions. The code looks horrible- it does work, but I would like to know a better way of executing the code, and also whats the use of making a function separately and calling it the loop-what advantages are there.

Here is the final code:-

#include <Servo.h>
Servo myServo;

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#define I2C_ADDR    0x27
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7
LiquidCrystal_I2C  lcd(I2C_ADDR, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin);

#include <elapsedMillis.h>
elapsedMillis timeElapsed; //declare global if you don't want it reset every time loop runs

#include <Stepper.h>
float stepsPerRevolution = 63.683;
Stepper small_stepper(stepsPerRevolution, 8, 10, 9, 11);

const int sensorPin = A0;
const int sensorPin2 = A1;

//variables:
int sensorVal = 0;
int sensorVal2 = 0;
int sensorMin = 1023;
int sensorMax = 0;

int angle;
int angle2;
int ang = 0;


void setup() {
  Serial.begin(9600);
  myServo.attach(5);

  small_stepper.setSpeed(200);

  lcd.begin (20, 4);
  // Switch on the backlight
  lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home (); // go home
  lcd.print("Tilt angle:");
  lcd.setCursor(0, 1);
  lcd.print("Azimuth: ");


  while (millis() < 5000) {
    sensorVal = analogRead(sensorPin);

    // record the maximum sensor value
    if (sensorVal > sensorMax) {
      sensorMax = sensorVal;
    }

    // record the minimum sensor value
    if (sensorVal < sensorMin) {
      sensorMin = sensorVal;
    }
  }

  Serial.print("MAX: ");
  Serial.print(sensorMax);
  Serial.print(", MIN: ");
  Serial.print(sensorMin);



}
void loop() {


  int sensorVal = analogRead(sensorPin);
  int sensorVal2 = analogRead(sensorPin2);
  //Serial.print(timeElapsed);

  if (sensorVal > sensorVal2) {

    angle = map(sensorVal, sensorMin, sensorMax, 0, 89);
    angle = constrain(angle, 1, 90);

    myServo.write(angle);
    ang = angle;

    //    Serial.print("ONE: ");
    //    Serial.println(angle);

    delay(15);
  } else if (sensorVal2 > sensorVal) {

    angle2 = map(sensorVal2, sensorMin, sensorMax, 180, 90);
    angle2 = constrain(angle2, 90, 180);

    myServo.write(angle2);
    ang = angle2;

    //    Serial.print("TWO: ");
    //    Serial.println(angle2);
    delay(15);
  }

  lcd.setCursor(11, 0);
  if (ang < 10) lcd.print(" "); // 0-9 add one space
  if (ang < 100) lcd.print(" "); // 0-99 add one space
  if (ang < 1000) lcd.print(" "); // 0-999 add one space
  lcd.print(ang);
  lcd.print((char)223);


  delay(1000);

  Serial.println(timeElapsed);

  if (timeElapsed > 0 && timeElapsed < 12213) {
    lcd.setCursor(8, 1);
    lcd.print(" 0");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("January      ");

  }
  if (timeElapsed > 12213 && timeElapsed < 13219) {
    small_stepper.step(63.683);
  }
  if (timeElapsed > 13213 && timeElapsed < 18435) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print(" 5");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("February      ");

  }
  if (timeElapsed > 18435 && timeElapsed < 19436 ) {
    small_stepper.step(63.683);
  }
  if (timeElapsed > 19436 && timeElapsed < 24642) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print("10");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("March      ");

  }  if (timeElapsed > 24642 && timeElapsed < 25642) {
    small_stepper.step(63.683);
  }  if (timeElapsed > 25462 && timeElapsed < 30850) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print("15");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("April      ");

  }

  if (timeElapsed > 30850 && timeElapsed < 31850) {
    small_stepper.step(-63.683);
  }
  if (timeElapsed > 31850 && timeElapsed < 38237) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print("10");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("May      ");

  }
  if (timeElapsed > 38237 && timeElapsed < 39237) {
    small_stepper.step(-63.683);
  }
  if (timeElapsed > 39237 && timeElapsed < 45625) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print(" 5");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("June      ");

  }
  if (timeElapsed > 45625 && timeElapsed < 46625) {
    small_stepper.step(-63.683);
  }

  if (timeElapsed > 46625 && timeElapsed < 53013) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print(" 0");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("July      ");

  }
  if (timeElapsed > 53013) {

    timeElapsed = 7055;


  }



}

The problem was with the servo code I had to use roughly around 1500 delay

You did not have to use ANY delay(). The blink without delay example show how not to. You need to discard the idea that the servo must be sent to its final position in one blocking chunk of code.

As @PaulS has said, don't use delay(). Have a look at how millis() is used to manage timing without blocking in several things at a time. The demo includes moving a servo without blocking.

...R

Thanks so much for the insight. @PaulS could you please elaborate on the point "You need to discard the idea that the servo must be sent to its final position in one blocking chunk of code."

Thanks so much again really appreciate the help.

could you please elaborate on the point "You need to discard the idea that the servo must be sent to its final position in one blocking chunk of code."

Move the servo a small bit on each iteration of loop() if it is time to do so. This will prevent the code being locked into moving the servo at the expense of executing other code.

UKHeliBob: Move the servo a small bit on each iteration of loop() if it is time to do so. This will prevent the code being locked into moving the servo at the expense of executing other code.

Oh ok got it. But how to do that :S ?

Yami89:
But how to do that :S ?

This sketch runs 2 servos independently of each other, with delay()-less millis()-based interleaving of their motions. Each servo has settable mimimum and maximum degree limits, and a speed based on the ms between updates. The degrees moved per update is settable too.

You can get debug monitor prints optionally by commenting the DEBUG line out or in.

// Sweep for 2 servos

// This example code is in the public domain.

//idea is for two servi to go up and down ala sweep
//   BUT to interleave their motion

// in this one the servos can have diff ranges and speeds
// both of which hardcoded

//basically bwod thinking


#include <Servo.h>

Servo servoX; 
Servo servoY;            

byte posX;
byte posY;

byte posXmin = 10;
byte posXmax = 170;

byte posYmin = 50;
byte posYmax = 100;

int stepX = -1; //degrees per move.. gets *-1 immed for +1 first time
int stepY = -1;

unsigned long currentMillis = 0;
unsigned long previousMillisX = 0; // the time when the servo was last moved
unsigned long previousMillisY = 0;
int speedX = 25; //millisec per update, larger is slower
int speedY = 250;

//  (un)-comment the following
//#define DEBUG //serial prints
#define DEBUG1 //led13 //annoys me to have it on all the time
//#define LCD_PRESENT_BUT_NOT_USED

void setup()
{
  posX = posXmin;
  posY = posYmin;

  servoX.write(posX);  //initialise
  servoY.write(posY);

  servoX.attach(2);  //suits vacant pins on dfr0009 lcd 
  servoY.attach(3);

#ifdef DEBUG
  Serial.begin(9600);
  Serial.println("setup " + String(posX) + " " + String(posY));
  delay(1000);
#endif

#ifdef DEBUG1
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
#endif

#ifdef LCD_PRESENT_BUT_NOT_USED
  pinMode(10, OUTPUT); //turn backlight off
#endif
}


void loop()
{
  currentMillis = millis();
  moveX();
  moveY();

#ifdef DEBUG
  delay(100); // just to slow the monitor lines down for reading
#endif
}//loop

void moveX()
{
  if (currentMillis - previousMillisX >= speedX) //move if X time elapsed else nothing
  {
    previousMillisX = currentMillis;

    if ((posX <= posXmin) || (posX >= posXmax))
    {
      stepX = -1 * stepX; //change direction at limits
    }

    servoX.write(posX);
    posX = posX + stepX;

#ifdef DEBUG
    Serial.print("moveX " + String(posX) + " " + String(stepX));
#endif
  }//millis if
} //moveX

void moveY()
{
  if (currentMillis - previousMillisY >= speedY) //move if Y time elapsed else nothing
  {
    previousMillisY = currentMillis;

    if ((posY <= posYmin) || (posY >= posYmax))
    {
      stepY = -1 * stepY; //change direction at limits
    }

    servoY.write(posY);
    posY = posY + stepY;

#ifdef DEBUG
    Serial.println("  moveY " + String(posY) + " " + String(stepY));
#endif
  }//timing
}//moveY
    if ((posX == posXmin) || (posX == posXmax))
    posX = posX + stepX;

DANGER, Will Robinson !

What if stepX is anything except 1 or -1 ?

UKHeliBob:
What if stepX is anything except 1 or -1 ?

Yeah ok that could make posX too big next time it does a servo.write(). I do check for posX to be == the max, but should make that >= instead.

(Or were you meaning something else?)

were you meaning something else?

No, you got what I meant.

Hi guys, so after some playing around here is the code I came up with. One thing I noticed is that when I add the stepper function in the loop, the time elapsed gets all f*ed up. I’ve got to a point that it works but its no better than the first code I started with.

#include <Servo.h>
Servo myServo;

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#define I2C_ADDR    0x27
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7
LiquidCrystal_I2C  lcd(I2C_ADDR, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin);

#include <elapsedMillis.h>
elapsedMillis timeElapsed; //declare global if you don't want it reset every time loop runs

#include <Stepper.h>
float stepsPerRevolution = 63.683;
Stepper small_stepper(stepsPerRevolution, 8, 10, 9, 11);

const int sensorPin = A0;
const int sensorPin2 = A1;

//variables:
int sensorVal = 0;
int sensorVal2 = 0;
int sensorMin = 1023;
int sensorMax = 0;

int angle;
int angle2;
int ang = 0;

unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
long Smooth = 15;
long LongTime = 1000;


void setup() {
  Serial.begin(9600);
  myServo.attach(5);

  small_stepper.setSpeed(200);

  lcd.begin (20, 4);
  // Switch on the backlight
  lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home (); // go home
  lcd.print("Tilt angle:");
  lcd.setCursor(0, 1);
  lcd.print("Azimuth: ");


  while (millis() < 5000) {
    sensorVal = analogRead(sensorPin);

    // record the maximum sensor value
    if (sensorVal > sensorMax) {
      sensorMax = sensorVal;
    }

    // record the minimum sensor value
    if (sensorVal < sensorMin) {
      sensorMin = sensorVal;
    }

  }

  Serial.print("MAX: ");
  Serial.print(sensorMax);
  Serial.print(", MIN: ");
  Serial.print(sensorMin);

  timeElapsed = -5; // had to include because when the loop starts it has an addition of 5sec




}
void loop() {

  int sensorVal = analogRead(sensorPin);
  int sensorVal2 = analogRead(sensorPin2);


  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis2 >= 1000) {
    if ((sensorVal > sensorVal2) && (currentMillis - previousMillis >= Smooth)) {

      angle = map(sensorVal, sensorMin, sensorMax, 0, 89);
      angle = constrain(angle, 1, 90);

      myServo.write(angle);
      ang = angle;
      previousMillis = currentMillis;
      //    Serial.print("ONE: ");
      //    Serial.println(angle);

      //    delay(15);
    } else if ((sensorVal2 > sensorVal) && (currentMillis - previousMillis >= Smooth)) {

      angle2 = map(sensorVal2, sensorMin, sensorMax, 180, 90);
      angle2 = constrain(angle2, 90, 180);

      myServo.write(angle2);
      ang = angle2;
      previousMillis = currentMillis;

      //    Serial.print("TWO: ");
      //    Serial.println(angle2);
      //delay(15);
    }

    lcd.setCursor(11, 0);
    if (ang < 10) lcd.print(" "); // 0-9 add one space
    if (ang < 100) lcd.print(" "); // 0-99 add one space
    if (ang < 1000) lcd.print(" "); // 0-999 add one space
    lcd.print(ang);
    lcd.print((char)223);


    //delay(1000);
    previousMillis2 = currentMillis;
    stepper();
    Serial.println(timeElapsed);

  }

}


void stepper() {

  if (timeElapsed > 0 && timeElapsed < 6000) {
    lcd.setCursor(8, 1);
    lcd.print(" 0");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("January      ");

  }
  if (timeElapsed >= 6000 && timeElapsed < 6500) {
    small_stepper.step(63.683);
  }
  if (timeElapsed >=6500 && timeElapsed < 12000) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print(" 5");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("February      ");
  }
  if (timeElapsed >= 12000 && timeElapsed < 12500 ) {
    small_stepper.step(63.683);
  }
  if (timeElapsed >=12500 && timeElapsed < 18000) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print("10");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("March      ");
  }
  if (timeElapsed >= 18000 && timeElapsed < 18500) {
    small_stepper.step(63.683);
  }
  if (timeElapsed >= 18500 && timeElapsed < 24000 ) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print("15");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("April      ");

  }
  if (timeElapsed >=24000 && timeElapsed < 24500) {
    small_stepper.step(-63.683);
  }
  if (timeElapsed >= 24500 && timeElapsed < 36000) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print("10");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("May      ");
  }
  if (timeElapsed >= 36000 && timeElapsed < 36500) {
    small_stepper.step(-63.683);
  }
  if (timeElapsed >= 36500 && timeElapsed < 42000) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print(" 5");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("June      ");
  }
  if (timeElapsed >= 42000 && timeElapsed < 42500) {
    small_stepper.step(-63.683);
  }

  if (timeElapsed >=42500 && timeElapsed < 48000) {
    small_stepper.step(0);
    lcd.setCursor(8, 1);
    lcd.print(" 0");
    lcd.print((char)223);
    lcd.setCursor(0, 2);
    lcd.print("July      ");
  }
  if (timeElapsed >= 48000) {

    timeElapsed = 0;
  }



}

Without the stepper function in the loop still the time elapsed counts in 1000, 2000,3000, and so on. Isn’t there any way I could use the millis() command separately for different functions? - get my point?