Problem delaying operations

Hi

I've been creating my first Arduino project to control a motor with two push buttons that toggle on/off and direction. i.e. button 1 goes fwd, button 2 goes in rev and a second press on each stops the motor.

The Arduino outputs a serial signal via pin 1 to a comercial driver board. This basically sends a speed and direction command using a library from the manufacturer.

Everything is functioning well but I'm having trouble getting my head around using millis to create a delay.

I want the motor to start at a slow speed wait 2-3 seconds then jump to a higher speed until stopped. I've tried lots of things to no avail and currently I've got an elapsedMillis library to provide a delay. The problem is the motor just doesn't change speed.

Hopefully you guys wont shoot me down in flames for being so inept.

Here's my code

 const int FWDbuttonPin = 2;
const int REVbuttonPin = 7;
const int FWDledPin = 12;
const int REVledPin = 13; 

#include <SyRenSimplified.h>

SyRenSimplified SR;

#include <elapsedMillis.h>

elapsedMillis timeElapsedFWD; 

elapsedMillis timeElapsedREV;

long interval = 2000;

int FWDledState = LOW;        
int FWDbuttonState;            
int FWDlastButtonState = LOW; 

int REVledState = LOW;         
int REVbuttonState;            
int REVlastButtonState = LOW;   

long lastDebounceTime = 0;  
long debounceDelay = 50;   


void setup() {
  SyRenTXPinSerial.begin(9600);  
  pinMode(FWDbuttonPin, INPUT);
  pinMode(FWDledPin, OUTPUT);
  
  pinMode(REVbuttonPin, INPUT);
  pinMode(REVledPin, OUTPUT);
  
  digitalWrite(FWDledPin, FWDledState);
  digitalWrite(REVledPin, REVledState);
  
  
}

void loop() {
  
 //Forward operation 
 //Debounce and toggle button 
  int FWDreading = digitalRead(FWDbuttonPin);

  if (FWDreading != FWDlastButtonState) {
    
    lastDebounceTime = millis();
  } 
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    
    if (FWDreading != FWDbuttonState) {
      FWDbuttonState = FWDreading;

      if (FWDbuttonState == HIGH) {
        FWDledState = !FWDledState;
      }
    }
  }
  
  digitalWrite(FWDledPin, FWDledState);
  
  //Send forward signal to motor
   if (FWDledState == HIGH){
     REVledState = LOW;//Make sure reverse signal is cancelled 
     SR.motor(-10);
     if (timeElapsedFWD > interval) 
     SR.motor(-50);
     timeElapsedFWD = 0;
     
  }

  FWDlastButtonState = FWDreading; 
  
 //Reverse operation 
 //Debounce and toggle button 
  int REVreading = digitalRead(REVbuttonPin);

  if (REVreading != REVlastButtonState) {
 
    lastDebounceTime = millis();
  } 
  
  if ((millis() - lastDebounceTime) > debounceDelay) {

    if (REVreading != REVbuttonState) {
      REVbuttonState = REVreading;

      if (REVbuttonState == HIGH) {
        REVledState = !REVledState;
      }
    }
  }

  digitalWrite(REVledPin, REVledState);
  
  //Send reverse signal to motor
  if (REVledState == HIGH){
     FWDledState = LOW;//Make sure forward signal is cancelled 
     SR.motor(10);
     if (timeElapsedREV > interval) 
     SR.motor(50);
     timeElapsedREV = 0;
   }

  REVlastButtonState = REVreading;
  
 //Neutral State 
  if (REVledState == LOW && FWDledState == LOW){
     SR.motor(0);
    
   }   
  
}

Thanks

I don't know what is in elapsedMillis.h so I can't comment on how you are using that.

The demo several things at a time which illustrates how to use millis() to manage time.

You might also get some ideas from the Thread planning and implementing a program. Organizing code into functions makes it much easier to separate the control logic (what, and when) from the action logic (how).

For example if you put all the button reading into one function a single debounce process would be sufficient - and I strongly suspect you will find that no explicit debouncing is needed.

You will also need a variable to keep track of where you are in the motor cycle - for example OFF, Acclerating and Full speed could be represented by the characters O, A and F.

I have in mind a code structure like this

void loop() {
  curMillis = millis(); // one value for use throughout the loop
  readButtons(); 
  commandMotor();
}

The commandMotor() function would check the state of the buttons, the state of the motor and the time to decide whether to call another function that just has the code to send instructions to the motor - that might be called driveMotor()

...R

Thanks Robin2, I'm very grateful for the tips on structure, and the very clear examples. I'll give it a go.

I've tried to create a more structured sketch as per the examples given but I'm having trouble with the commandMotor() and readButtons() functions. At the moment it's just spinning the motor on connection but I'm hoping it's a problem with the poor coding, not the structure. If someone could confirm the basic layout is suitable in structure and suggest where I might have issues I'd be very grateful.

// Libraries 

#include <SyRenSimplified.h> // serial command library 
SyRenSimplified SR;

// Constants

const int FWDbuttonPin = 2; // pin assignment 
const int REVbuttonPin = 7;

const int FWD_Ramp_Interval = 2500; // slow motor speed periods, allows independent values 
const int REV_Ramp_Interval = 2500;

// Variables 

byte FWDbuttonstate = LOW;
byte REVbuttonstate = LOW;

int motorOff;
int motorSlowFWD;
int motorFastFWD;
int motorSlowREV;
int motorFastREV;
int motorstate = motorOff;


unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousButtonMillis = 0; // time when button press last checked


void setup() {

  SyRenTXPinSerial.begin(9600);

  pinMode(FWDbuttonPin, INPUT);
  
  pinMode(REVbuttonPin, INPUT);

  
}
  
void loop() {
  currentMillis = millis(); // one value for use throughout the loop
  readButtons(); // checks for button presses
  commandMotor(); // decides what mode to set the motor to
  driveMotor(); // sends the instructions to the motor
}

void readButtons() {

   if (digitalRead(FWDbuttonPin) == LOW) {
     FWDbuttonstate = ! FWDbuttonstate; // this changes it to LOW if it was HIGH 
                                          //   and to HIGH if it was LOW
   }
   if (digitalRead(REVbuttonPin) == LOW) {
     REVbuttonstate = ! REVbuttonstate; // this changes it to LOW if it was HIGH 
                                          //   and to HIGH if it was LOW
   }
   
 }

void commandMotor() {
  
  if (FWDbuttonstate == HIGH) { // forward operation    
    if (currentMillis <= FWD_Ramp_Interval) {
      
      motorstate = motorSlowFWD; 
      
      previousButtonMillis += FWD_Ramp_Interval;
      
    }
    
    if (previousButtonMillis >= FWD_Ramp_Interval) {
      
      motorstate = motorFastFWD;
    
    } 
    
    REVbuttonstate = LOW; // reset reverse state
    
  }
  
  if (REVbuttonstate == HIGH) { // reverse operation    
    if (currentMillis <= REV_Ramp_Interval) {
      
      motorstate = motorSlowREV; 
      
      previousButtonMillis += REV_Ramp_Interval;
      
    }
    
    if (previousButtonMillis >= FWD_Ramp_Interval) {
      
      motorstate = motorFastREV;
    
    } 
    
    FWDbuttonstate = LOW; // reset forward state
    
  }
  
    else { // neutral state
      
      motorstate = motorOff;
    
    }
  
}

void driveMotor() {
     
  if (motorstate = motorOff) { SR.motor(0); }
  if (motorstate = motorSlowFWD) { SR.motor(10); }
  if (motorstate = motorFastFWD) { SR.motor(50); }
  if (motorstate = motorSlowREV) { SR.motor(-10); }
  if (motorstate = motorFastREV) { SR.motor(-50); }

  }
  if (motorstate = motorOff) { SR.motor(0); }
  if (motorstate = motorSlowFWD) { SR.motor(10); }
  if (motorstate = motorFastFWD) { SR.motor(50); }
  if (motorstate = motorSlowREV) { SR.motor(-10); }
  if (motorstate = motorFastREV) { SR.motor(-50); }

= != ==

PaulS:

  if (motorstate = motorOff) { SR.motor(0); }

if (motorstate = motorSlowFWD) { SR.motor(10); }
 if (motorstate = motorFastFWD) { SR.motor(50); }
 if (motorstate = motorSlowREV) { SR.motor(-10); }
 if (motorstate = motorFastREV) { SR.motor(-50); }




= != ==

I see the problem but now I'm just getting a stange noise from the motor :frowning:

I see the problem but now I'm just getting a stange noise from the motor

But, did you fix it?

Listen to the noise for a while. After a while it will no longer seem strange.

If that isn't useful advice (and I don't believe for a minute that it is), we need a lot more information starting with a link to the library you are using, a link to the motor/driver, and a schematic so we can see how the motor is powered.

if (currentMillis <= FWD_Ramp_Interval) {

This is NOT the way to use millis() - and it will do nothing useful - it is unlikely ever to the true

It should be

if (currentMillis - previousButtonMillis <= FWD_Ramp_Interval) {

Make these and @PaulS's corrections and then post with your revised code. (Don't update code in an existing Post).

...R

Thanks guys,

New code

// Libraries 

#include <SyRenSimplified.h> // serial command library 
SyRenSimplified SR;

// Constants

const int FWDbuttonPin = 2; // pin assignment 
const int REVbuttonPin = 7;

const int FWD_Ramp_Interval = 2500; // slow motor speed periods, allows independent values 
const int REV_Ramp_Interval = 2500;

// Variables 

byte FWDbuttonstate = LOW;
byte REVbuttonstate = LOW;

int motorOff;
int motorSlowFWD;
int motorFastFWD;
int motorSlowREV;
int motorFastREV;
int motorstate = motorOff;


unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousButtonMillis = 0; // time when button press last checked


void setup() {

  SyRenTXPinSerial.begin(9600);

  pinMode(FWDbuttonPin, INPUT);
  
  pinMode(REVbuttonPin, INPUT);

  
}
  
void loop() {
  currentMillis = millis(); // one value for use throughout the loop
  readButtons(); // checks for button presses
  commandMotor(); // decides what mode to set the motor to
  driveMotor(); // sends the instructions to the motor
}

void readButtons() {

   if (digitalRead(FWDbuttonPin) == LOW) {
     FWDbuttonstate = ! FWDbuttonstate; // this changes it to LOW if it was HIGH 
                                          //   and to HIGH if it was LOW
   }
   if (digitalRead(REVbuttonPin) == LOW) {
     REVbuttonstate = ! REVbuttonstate; // this changes it to LOW if it was HIGH 
                                          //   and to HIGH if it was LOW
   }
   
 }

void commandMotor() {
  
  if (FWDbuttonstate == HIGH) { // forward operation    
    if (currentMillis - previousButtonMillis <= FWD_Ramp_Interval) {
      
      motorstate = motorSlowFWD; 
      
      previousButtonMillis += FWD_Ramp_Interval;
      
    }
    
    if (currentMillis - previousButtonMillis >= FWD_Ramp_Interval) {
      
      motorstate = motorFastFWD;
    
    } 
    
    REVbuttonstate = LOW; // reset reverse state
    
  }
  
  if (REVbuttonstate == HIGH) { // reverse operation    
    if (currentMillis - previousButtonMillis <= REV_Ramp_Interval) {
      
      motorstate = motorSlowREV; 
      
      previousButtonMillis += REV_Ramp_Interval;
      
    }
    
    if (currentMillis - previousButtonMillis >= REV_Ramp_Interval) {
      
      motorstate = motorFastREV;
    
    } 
    
    FWDbuttonstate = LOW; // reset forward state
    
  }
  
    else { // neutral state
      
      motorstate = motorOff;
    
    }
  
}

void driveMotor() {
     
  if (motorstate == motorOff) { SR.motor(0); }
  if (motorstate == motorSlowFWD) { SR.motor(10); }
  if (motorstate == motorFastFWD) { SR.motor(50); }
  if (motorstate == motorSlowREV) { SR.motor(-10); }
  if (motorstate == motorFastREV) { SR.motor(-50); }

  }

Library link here (SyRen Simplified Serial is the relevant section)

Schematic attached.

The motor driver provides 5V in and requires only a serial connection through pin1. I hope this makes sense.

Using that library requires that your device be connected to the hardware serial pin. That means that, unless you have a Leonardo, you can no longer debug your sketch.

Given that there is little serial data that needs to be sent to the controller, the developer(s) of that library should be ashamed of themselves for not using SoftwareSerial and some pin(s) other than pin 1.

The library is incredibly simple. Delete it. Create an instance of SoftwareSerial that uses two other pins, and move the controller to the TX pin that you pass to the SoftwareSerial constructor.

Implement the one real useful method in the class in your sketch, and forget that those morons were trying to "help" you.

Erm....Ok, so I think I just about understand. I can use the standard SoftwareSerial Library to send data via a defined tx pin. When you say "useful method in the class in your sketch" I guess you mean the basic SR.motor command.

When you say "useful method in the class in your sketch" I guess you mean the basic SR.motor command.

Aside from the constructors, the only other method is motor(). There is one useful version, and two useless ones.

PaulS:
Aside from the constructors, the only other method is motor(). There is one useful version, and two useless ones.

Sorry, getting a little confused by the terminology. Is method() the same as member()? If I delete the library SyRenSimplified.h how do I send the member function motor() to the controller?

I chose the serial option because I thought it was simple. The Controller will accept a PWM signal instead if it is easier. I've amended the code to use pin 10 and 11 as RX and TX and send the motor () commands.

// Libraries 

#include <SyRenSimplified.h>
#include <SoftwareSerial.h> // serial command library 
SoftwareSerial SWSerial(10, 11);
SyRenSimplified SR(SWSerial);

// Constants

const int FWDbuttonPin = 2; // pin assignment 
const int REVbuttonPin = 7;

const int txPin = 11;
const int rxPin = 10;

const int FWD_Ramp_Interval = 2500; // slow motor speed periods, allows independent values 
const int REV_Ramp_Interval = 2500;

// Variables 

byte FWDbuttonstate = LOW;
byte REVbuttonstate = LOW;

int motorOff;
int motorSlowFWD;
int motorFastFWD;
int motorSlowREV;
int motorFastREV;
int motorstate = motorOff;


unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousButtonMillis = 0; // time when button press last checked


void setup() {

  SWSerial.begin(9600);

  pinMode(FWDbuttonPin, INPUT);
  
  pinMode(REVbuttonPin, INPUT);

  
}
  
void loop() {
  currentMillis = millis(); // one value for use throughout the loop
  readButtons(); // checks for button presses
  commandMotor(); // decides what mode to set the motor to
  driveMotor(); // sends the instructions to the motor
}

void readButtons() {

   if (digitalRead(FWDbuttonPin) == LOW) {
     FWDbuttonstate = ! FWDbuttonstate; // this changes it to LOW if it was HIGH 
                                          //   and to HIGH if it was LOW
   }
   if (digitalRead(REVbuttonPin) == LOW) {
     REVbuttonstate = ! REVbuttonstate; // this changes it to LOW if it was HIGH 
                                          //   and to HIGH if it was LOW
   }
   
 }

void commandMotor() {
  
  if (FWDbuttonstate == HIGH) { // forward operation    
    if (currentMillis - previousButtonMillis <= FWD_Ramp_Interval) {
      
      motorstate = motorSlowFWD; 
      
      previousButtonMillis += FWD_Ramp_Interval;
      
    }
    
    if (currentMillis - previousButtonMillis >= FWD_Ramp_Interval) {
      
      motorstate = motorFastFWD;
    
    } 
    
    REVbuttonstate = LOW; // reset reverse state
    
  }
  
  if (REVbuttonstate == HIGH) { // reverse operation    
    if (currentMillis - previousButtonMillis <= REV_Ramp_Interval) {
      
      motorstate = motorSlowREV; 
      
      previousButtonMillis += REV_Ramp_Interval;
      
    }
    
    if (currentMillis - previousButtonMillis >= REV_Ramp_Interval) {
      
      motorstate = motorFastREV;
    
    } 
    
    FWDbuttonstate = LOW; // reset forward state
    
  }
  
    else { // neutral state
      
      motorstate = motorOff;
    
    }
  
}

void driveMotor() {
     
  if (motorstate == motorOff) { SR.motor(0); }
  if (motorstate == motorSlowFWD) {SR.motor(10); }
  if (motorstate == motorFastFWD) { SR.motor(50); }
  if (motorstate == motorSlowREV) { SR.motor(-10); }
  if (motorstate == motorFastREV) { SR.motor(-50); }

  }
#include <SyRenSimplified.h>

Delete this crap.

SyRenSimplified SR(SWSerial);

And this.

  if (motorstate == motorOff) { SR.motor(0); }
  if (motorstate == motorSlowFWD) {SR.motor(10); }
  if (motorstate == motorFastFWD) { SR.motor(50); }
  if (motorstate == motorSlowREV) { SR.motor(-10); }
  if (motorstate == motorFastREV) { SR.motor(-50); }

Here, you need to call a function YOU write that sends to the motor using SWSerial.write().

SO I feel I'm getting somewhere but just can't get millis to delay as I want, tearing my hair out.

I've stripped back the motorCommand() function to just delay 5s before starting in each direction, I'll add the initial slower speed when I've got the basics right.

The motor starts, stops and changes direction as it should but the delay is not working. If anyone has any patience left with me please help.

// Libraries 


#include <SoftwareSerial.h> // serial command library 
SoftwareSerial SWSerial(10, 11);


// Constants

const int FWDbuttonPin = 2; // pin assignment 
const int REVbuttonPin = 7;

const int txPin = 11;
const int rxPin = 10;

const int FWDled = 12;
const int REVled = 13;


const int FWD_Ramp_Interval = 5500; // slow motor speed periods, allows independent values 
const int REV_Ramp_Interval = 5500;


long debounceDelay = 50; 

// Variables 

int FWDbuttonState = LOW;
int FWDlastButtonState = LOW; 
int REVbuttonState = LOW;
int REVlastButtonState = LOW;

int FWDoutState = LOW;           //   LOW = off
int REVoutState = LOW;

unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousFWDButtonMillis = 0; 
unsigned long previousREVButtonMillis = 0; 
long lastDebounceTime = 0;  

void setup() {

  SWSerial.begin(9600);

  pinMode(FWDbuttonPin, INPUT);
  pinMode(FWDled, OUTPUT);
  pinMode(REVbuttonPin, INPUT);
  pinMode(REVled, OUTPUT);
  
}
  
void loop() {
  currentMillis = millis(); // one value for use throughout the loop
  readButtons(); // checks for button presses
  switchLeds();
  motorCommand();

}

void readButtons() {

    int FWDreading = digitalRead(FWDbuttonPin);

  if (FWDreading != FWDlastButtonState) {
    
    lastDebounceTime = currentMillis;
  } 
  
  if ((currentMillis - lastDebounceTime) > debounceDelay) {
    
    if (FWDreading != FWDbuttonState) {
      FWDbuttonState = FWDreading;

      if (FWDbuttonState == HIGH) {
        FWDoutState = !FWDoutState;
      }
    }
  }
  
  if (FWDoutState == HIGH){
     REVoutState = LOW;//Make sure reverse signal is cancelled 
  }
  FWDlastButtonState = FWDreading; 
  
  int REVreading = digitalRead(REVbuttonPin);

  if (REVreading != REVlastButtonState) {
    
    lastDebounceTime = currentMillis;
  } 
  
  if ((currentMillis - lastDebounceTime) > debounceDelay) {
    
    if (REVreading != REVbuttonState) {
      REVbuttonState = REVreading;

      if (REVbuttonState == HIGH) {
        REVoutState = !REVoutState;
      }
    }
  }
  
  if (REVoutState == HIGH){
     FWDoutState = LOW;//Make sure reverse signal is cancelled 
  }
  
  REVlastButtonState = REVreading;
  

}

  
void switchLeds() {

 digitalWrite(FWDled, FWDoutState);
 digitalWrite(REVled, REVoutState);

}

void motorCommand() {
  
  if (FWDoutState == HIGH) {
  
  if ((currentMillis - previousFWDButtonMillis) >= FWD_Ramp_Interval) {
   {SWSerial.write(137);    // wait 5 seconds then go fwd
   previousFWDButtonMillis += FWD_Ramp_Interval;
   }
    
  }
 }
 if (REVoutState == HIGH) {
  
  if ((currentMillis - previousREVButtonMillis) >= REV_Ramp_Interval) {
   {SWSerial.write(117);    //wait 5 seconds then go rev
   previousREVButtonMillis += REV_Ramp_Interval;
   }
    
  }
 }
    else { 
      SWSerial.write(127); }
  
  
}