Setting a timeout, button priority

I have a functional code that runs a motor using an Arduino-controlled SyRen25 driver. There are 3 buttons that run the motor at 3 different speeds: low, medium, and high.

My first goal is to write a timeout function that stops the motor after a button is held down for 15 seconds.
My second goal is to have a priority function that honors the most recent button that was pressed. Each attempt I've made has had spotty results where the motor doesn't run at all.

Here's the code I have that works:

// Earthquake Table Lo/Med/High program
// Modified from Sweep example Copyright (c) 2012 Dimension Engineering LLC
// See license.txt for license details.

#include <SyRenSimplified.h>

  int ramp = 0;

  int pinSet[3] = {3, 4, 5}; // I know I probably don't need to use arrays, but this was just for practice
  int pinVal[3];

SyRenSimplified SR; //this is the SyRen25 driver object
                    //   http://www.dimensionengineering.com/datasheets/SyrenDIPWizard/start.htm
                    // Be sure to select Simplified Serial Mode for use with this library.
                    // This sample uses a baud rate of 9600.
                    //
                    // Connections:
                    //   Arduino TX->1  ->  SyRen S1
                    //   Arduino GND    ->  SyRen 0V
                    //   Arduino VIN    ->  SyRen 5V (OPTIONAL, if you want the SyRen to power the Arduino)
                    //
                    // If you want to use a pin other than TX->1, see the SoftwareSerial example.
                    //DIP switch setting should be [ON-OFF-ON-OFF-ON-ON] 
                                        
void setup()
{
  SyRenTXPinSerial.begin(9600); // This is the baud rate you chose with the DIP switches.
  pinMode(pinSet[0], INPUT); //low speed button
  pinMode(pinSet[1], INPUT); // medium speed button
  pinMode(pinSet[2], INPUT); // high speed button 
  
}


void loop()
{  
  pinVal[2] = digitalRead(pinSet[2]);
  pinVal[1] = digitalRead(pinSet[1]);
  pinVal[0] = digitalRead(pinSet[0]); 

  if(
    pinVal[0]==HIGH && ramp<60 || //low speed button
    pinVal[1]==HIGH && ramp<85 || //medium speed
    pinVal[2]==HIGH && ramp<127) // high speed 
{
    SR.motor(++ramp);
    delay(10); 
  }

  else if(
    pinVal[0]==LOW && 
    pinVal[1]==LOW && 
    pinVal[2]==LOW && 
    ramp>0){ 
    SR.motor(--ramp); //ramp down to 0 (stop) 
  }
  

}

I've tried a few different versions using timer = millis(); but they've been unsuccessful so I've removed it from the sketch completely. The documentation on millis() has not helped me.

If anyone has any guidance, I really appreciate it! Thank you in advance.

Give the poor buttons names! [1] doesn't mean anything. I notice you put a comment next to some of the lines, but as this program gets bigger it's going to be more difficult to remember which one is which.

You need to record the time when first starting moving. Then compare that time to the current time.

One thing that will work well for you is instead of identifying "start moving" just record the time when stopped. When all buttons are not pressed, record that time. That gets updated thousands of times per second while stopped. Then as soon as a button does become pressed, that time won't update any more.

Ok ok, hehe here's a more amicable code to name the poor little buttons
I'm not sure exactly how to record time at all. Like I said, the millis() documentation is not helpful and my understanding of how to use it to set the time and use it for functions is shaky.

// Earthquake Table Lo/Med/High program
// Modified from Sweep example Copyright (c) 2012 Dimension Engineering LLC
// See license.txt for license details.

#include <SyRenSimplified.h>

  int ramp = 0;

  //unsigned long timer;
  int pinSet[3] = {3, 4, 5}; 
  int Hval, Mval, Lval;
  
SyRenSimplified SR; // We'll name the SyRen object SR.
                    // For how to configure the SyRen, see the DIP Switch Wizard
                    //   http://www.dimensionengineering.com/datasheets/SyrenDIPWizard/start.htm
                    // Be sure to select Simplified Serial Mode for use with this library.
                    // This sample uses a baud rate of 9600.
                    //
                    // Connections to make:
                    //   Arduino TX->1  ->  SyRen S1
                    //   Arduino GND    ->  SyRen 0V
                    //   Arduino VIN    ->  SyRen 5V (OPTIONAL, if you want the SyRen to power the Arduino)
                    //
                    // If you want to use a pin other than TX->1, see the SoftwareSerial example.
                    //DIP switch setting should be [ON-OFF-ON-OFF-ON-ON] 
                                        
void setup()
{
  SyRenTXPinSerial.begin(9600); // This is the baud rate you chose with the DIP switches.
  pinMode(pinSet[0], INPUT); //low speed button
  pinMode(pinSet[1], INPUT); // medium speed button
  pinMode(pinSet[2], INPUT); // high speed button 
  
SR.motor(ramp); //should I declare this at the top of the loop instead? 
}


void loop()
{  
  Hval = digitalRead(pinSet[2]);
  Mval = digitalRead(pinSet[1]);
  Lval = digitalRead(pinSet[0]); 

  if(
    Lval==HIGH && ramp<60 ||
    Mval==HIGH && ramp<85 ||
    Hval==HIGH && ramp<127){
    SR.motor(++ramp);
    delay(10); 
  }

  else if(
    Lval==LOW && 
    Mval==LOW && 
    Hval==LOW && 
    ramp>0){ 
    SR.motor(--ramp); 
  }
  

}

If you want to test for something being done continuously for a period use code like this pseudo code

if button is not pressed {
  lastMomentButtonNotPressed = millis();
}

if (millis() - lastMomentButtonNotPressed >= desired interval) {
   // button has been pressed throughout the interval
   // do stuff
}

I don't understand this bit in your Original Post

My second goal is to have a priority function that honors the most recent button that was pressed

...R

Robin2:
If you want to test for something being done continuously for a period use code like this pseudo code

if button is not pressed {

lastMomentButtonNotPressed = millis();
}

if (millis() - lastMomentButtonNotPressed >= desired interval) {
  // button has been pressed throughout the interval
  // do stuff
}





I don't understand this bit in your Original Post

...R

What I mean is if people are pushing and holding multiple buttons at once, whichever button is pressed most recently should take priority.
I'm still not totally understanding millis() and how it functions in the code. Does defining a variable as millis() reset the timer to 0? According to the documentation, millis() will be constantly counting from 0 milliseconds from the moment the code initializes.
So I'm not understanding how to take those counted milliseconds and set a timer for each button press.

MorganS:
You need to record the time when first starting moving. Then compare that time to the current time.

One thing that will work well for you is instead of identifying "start moving" just record the time when stopped. When all buttons are not pressed, record that time. That gets updated thousands of times per second while stopped. Then as soon as a button does become pressed, that time won't update any more.

How does one actually record the time of a button press? From what I understand, millis() always reports the current time in the sketch. So if I'm trying to establish an interval of 15000 milliseconds, I need to find a way to make a variable for the button press that isn't affected by the delay() function.

It might seem obvious to you all, but I'm still a bit mystified....

I'm late to the party and haven't absorbed it all, but I'm wondering if you're not seeing how to use millis(), as distinct from not knowing what milllis() does.

You're right, it always tells us the current elapsed time since power-on or reset. The only time it's ever 0 is the instant that happens.

So forget about 0; think about differences between 2 times, just as if you looked at the clock when you put your cake in the oven. It's 17:27 here right now. If the cake takes 30 minutes*, you add 30 to 17:27 and take it out at 17:57. Keep looking at the clock....

You can have a cake in the oven, an egg on to boil, and the glue sticking the new sole onto your boots, all happening together. All with different start times. All with different durations. But all using the same clock on the wall which tells us the time elapsed since midnight.

  • I've never baked a cake.... no idea how long it takes

elvon_blunden:
I'm late to the party and haven't absorbed it all, but I'm wondering if you're not seeing how to use millis(), as distinct from not knowing what milllis() does.

You're right, it always tells us the current elapsed time since power-on or reset. The only time it's ever 0 is the instant that happens.

So forget about 0; think about differences between 2 times, just as if you looked at the clock when you put your cake in the oven. It's 17:27 here right now. If the cake takes 30 minutes*, you add 30 to 17:27 and take it out at 17:57. Keep looking at the clock....

You can have a cake in the oven, an egg on to boil, and the glue sticking the new sole onto your boots, all happening together. All with different start times. All with different durations. But all using the same clock on the wall which tells us the time elapsed since midnight.

  • I've never baked a cake.... no idea how long it takes

Right, I understand that much - subtracting current time from the recorded time, I'm just not sure how to actually write it into my code: recording the time of a button press, I suppose it would just be something like buttonPressed = millis() but does that go inside my if statements?

I'm referring to this guide to try and figure it out:
millis tutorial

0hm_Mic_0dd:
What I mean is if people are pushing and holding multiple buttons at once, whichever button is pressed most recently should take priority.

I'm still not sure I understand.

If there is only ButtonA then it is straightforward to tell if it has been held down continuously for 15 seconds (which, by the way is probably too long for an impatient human finger).

However if we introduce another ButtonB, what is supposed to happen if ButtonA has been held down for 6 seconds and then ButtonB is pressed?

If you need to learn about millis() have a look at Using millis() for timing. A beginners guide and at the demo Several Things at a Time

...R

elvon_blunden:
I'm late to the party

  • I've never baked a cake.... no idea how long it takes

Also, you're not late at all! I recommend cake-baking. It's fantastic :slight_smile:

Here's my updated code which verified and I'm going to go test it out shortly ... I've moved the SyRen-specific notes to the bottom for easier reading/parsing:

// Earthquake Table Lo/Med/High program
// Modified from Sweep example Copyright (c) 2012 Dimension Engineering LLC
// See license.txt for license details.

#include <SyRenSimplified.h> //library for SyRen driver

  int ramp = 0;
  int pinSet[3] = {3, 4, 5}; //pin array
  int Hval, Mval, Lval; //value of each button high or low
  unsigned long buttonPressed; //to mark time of button press
  unsigned long interval=15000; // 15 second timer on buttons pressed

  bool motorRunning = false; //logic function
  
SyRenSimplified SR; // We'll name the SyRen object SR.
                                        
void setup()
{
  SyRenTXPinSerial.begin(9600); // This is the baud rate you chose with the DIP switches.
  pinMode(pinSet[0], INPUT); //low speed button
  pinMode(pinSet[1], INPUT); // medium speed button
  pinMode(pinSet[2], INPUT); // high speed button 
  
SR.motor(ramp); //should I declare this at the top of the loop instead? 
}


void loop()
{  
  Hval = digitalRead(pinSet[2]);
  Mval = digitalRead(pinSet[1]);
  Lval = digitalRead(pinSet[0]); 

  unsigned long timeNow = millis(); //grabs current time

  if(
    Lval==HIGH && ramp<60 ||
    Mval==HIGH && ramp<85 ||
    Hval==HIGH && ramp<127){
    SR.motor(++ramp);
    delay(10); 
    buttonPressed=millis(); //record time of button press 
    motorRunning != motorRunning; //change boolean logic
  }

  else if(
    Lval==LOW && 
    Mval==LOW && 
    Hval==LOW && 
    ramp>0 ||
    timeNow - buttonPressed >= interval){ 
    SR.motor(--ramp); 
    delay(5); 
  }
}

// For how to configure the SyRen, see the DIP Switch Wizard
//   http://www.dimensionengineering.com/datasheets/SyrenDIPWizard/start.htm
// Be sure to select Simplified Serial Mode for use with this library.
// This sample uses a baud rate of 9600.
// Connections to make:
//   Arduino TX->1  ->  SyRen S1
//   Arduino GND    ->  SyRen 0V
//   Arduino VIN    ->  SyRen 5V (OPTIONAL, if you want the SyRen to power the Arduino)
// If you want to use a pin other than TX->1, see the SoftwareSerial example.
//DIP switch setting should be [ON-OFF-ON-OFF-ON-ON]

0hm_Mic_0dd:
I recommend cake-baking.

I'm a net consumer of cake, happy with that approach :wink:

Robin2:
I'm still not sure I understand.

If there is only ButtonA then it is straightforward to tell if it has been held down continuously for 15 seconds (which, by the way is probably too long for an impatient human finger).

However if we introduce another ButtonB, what is supposed to happen if ButtonA has been held down for 6 seconds and then ButtonB is pressed?

If you need to learn about millis() have a look at Using millis() for timing. A beginners guide and at the demo Several Things at a Time

...R

The idea is to keep guests from holding the buttons down for too long. We have a lot of children visitors who LOVE pushing all the buttons for as long as possible. So if they got impatient with 15 seconds, then good!
Thank you for those links, I will check them out!

Another updated version just with a few extra notes:

// Earthquake Table Lo/Med/High program
// Modified from Sweep example Copyright (c) 2012 Dimension Engineering LLC
// See license.txt for license details.

#include <SyRenSimplified.h>

  int ramp = 0;
  int pinSet[3] = {3, 4, 5}; //pin array
  int Hval, Mval, Lval; //value of each button high or low
  unsigned long buttonPressed; //to mark time of button press
  unsigned long interval=15000; // 15 second timer on buttons pressed

  bool motorRunning = false; //logic function, not sure if I even need this
  
SyRenSimplified SR; // We'll name the SyRen object SR.
                                        
void setup()
{
  SyRenTXPinSerial.begin(9600); // This is the baud rate you chose with the DIP switches.
  pinMode(pinSet[0], INPUT); //low speed button
  pinMode(pinSet[1], INPUT); // medium speed button
  pinMode(pinSet[2], INPUT); // high speed button 
  
SR.motor(ramp); //should I declare this at the top of the loop instead? 
}


void loop()
{  
  Hval = digitalRead(pinSet[2]);
  Mval = digitalRead(pinSet[1]);
  Lval = digitalRead(pinSet[0]); 

  unsigned long timeNow = millis(); //grabs current time

  if(
    Lval==HIGH && ramp<60 ||
    Mval==HIGH && ramp<85 ||
    Hval==HIGH && ramp<127){
    SR.motor(++ramp);
    delay(10); 
    buttonPressed=millis(); //record time of button press 
    motorRunning = true; //change boolean logic
  }

  else if(
    Lval==LOW && 
    Mval==LOW && 
    Hval==LOW && 
    ramp>0 ||
    timeNow - buttonPressed >= interval){ 
    SR.motor(--ramp); 
    delay(5); 
    motorRunning = false; 
  }
}

// For how to configure the SyRen, see the DIP Switch Wizard
//   http://www.dimensionengineering.com/datasheets/SyrenDIPWizard/start.htm
// Be sure to select Simplified Serial Mode for use with this library.
// This sample uses a baud rate of 9600.
// Connections to make:
//   Arduino TX->1  ->  SyRen S1
//   Arduino GND    ->  SyRen 0V
//   Arduino VIN    ->  SyRen 5V (OPTIONAL, if you want the SyRen to power the Arduino)
// If you want to use a pin other than TX->1, see the SoftwareSerial example.
//DIP switch setting should be [ON-OFF-ON-OFF-ON-ON]

It failed :confused:

It also glitched out and ran the motor with no buttons pressed :cry:
Maybe I do need to switch to using a boolean operator?

Will keep scouring the documentation...

I have a newer version that kiiiiinda works in that it does ramp down to 0 after 15 seconds, but it glitches out after the timeout and runs full-speed even with all 3 buttons unpressed.

// Earthquake Table Lo/Med/High program
// Modified from code examples Copyright (c) 2012 Dimension Engineering LLC
// See license.txt for license details.

#include <SyRenSimplified.h> //Library for SyRen driver

  int ramp = 0;
  int pinSet[3] = {3, 4, 5}; //pin array
  int Hval, Mval, Lval; //value of each button high or low
  unsigned long buttonPressed; //to mark time of button press
  const unsigned long interval=15000; // 15 second timer on buttons pressed

  bool timed_out = false; //logic function, not sure if I even need this
  
SyRenSimplified SR; // SyRen object
                                        
void setup()
{
  SyRenTXPinSerial.begin(9600); // This is the baud rate you chose with the DIP switches.
  pinMode(pinSet[0], INPUT); //low speed button
  pinMode(pinSet[1], INPUT); // medium speed button
  pinMode(pinSet[2], INPUT); // high speed button 

}

void loop()
{  
  Hval = digitalRead(pinSet[2]);
  Mval = digitalRead(pinSet[1]);
  Lval = digitalRead(pinSet[0]); 

  unsigned long timeNow = millis(); //grabs current time

  if(!timed_out &&(
    Lval==HIGH && ramp<60 ||
    Mval==HIGH && ramp<85 ||
    Hval==HIGH && ramp<127)){
    SR.motor(++ramp);
    delay(8);
    buttonPressed=millis(); //record time of button press 
  }
    
  else if(
    Lval==LOW && 
    Mval==LOW && 
    Hval==LOW && 
    ramp>0){ 
      SR.motor(--ramp);
      delay(5);
    timed_out = false; 
  }
  else if(timeNow - buttonPressed >= interval){
    SR.motor(--ramp);
    delay(5);
    timed_out = true; 
  }
} //end of void_loop

Please halp! :o

0hm_Mic_0dd:
The idea is to keep guests from holding the buttons down for too long.

I understand the objective but I still have no idea what you mean by the piece I quoted in Reply #8.

...R

Robin2:
I understand the objective but I still have no idea what you mean by the piece I quoted in Reply #8.

...R

To simplify things, let's forget the button priority thing for a moment and focus on the timeout function.

Goal: to make the motor stop turning after 15 seconds of a button being held down through the use of millis().

The code I posted most recently does function in that the motor times out and stops after 15 seconds, but glitches out right after timing out and runs full speed. I'm not sure how else to explain

  else if (timeNow - buttonPressed >= interval) {

SR.motor(--ramp);
    delay(5);
    timed_out = true;
  }

What happens when ramp reaches zero?

MorganS:
What happens when ramp reaches zero?

Oh I maybe just needed to add
&& ramp >0

inside that last else if statement. I'll try it now!

The ramp value can go negative and cause the motor to spin the opposite direction.

0hm_Mic_0dd:
To simplify things, let's forget the button priority thing for a moment and focus on the timeout function.

Goal: to make the motor stop turning after 15 seconds of a button being held down through the use of millis().

I showed a simple way to do that in Reply #3

The code I posted most recently does function in that the motor times out and stops after 15 seconds, but glitches out right after timing out and runs full speed. I'm not sure how else to explain

You have some complex compound IF statements followed by ELSE statements that are also complex.

For example this

 if(!timed_out &&(
    Lval==HIGH && ramp<60 ||
    Mval==HIGH && ramp<85 ||
    Hval==HIGH && ramp<127))

tests 7 things (if I have counted properly). With 7 things there are 128 possibilities of which only 1 will be "correct". Any of the other 127 things can trigger the ELSE. How can you possibly know which of them is responsible?

Simplify.

...R

I thought that if() condition was rather clever. Pressing any button will ramp up to the speed defined for that button. Pressing a faster button simultaneously will ramp up to faster. Releasing a button starts it ramping down.

The only wrinkle is if it's faster than the currently-held button, it doesn't ramp down to that speed. It stays constant.