3

For my school project im making an arduino speedometer for a bicycle. I will be using a reed switch sensor to get the speed. There will also be an clock and a termometer, it will be on a 16x2 lcd screen. To get the speed I will need the user to type in his bike size, in zoll. I managed to let the user tap the button until his size and then double tap the button to confirm it. Alright, fine. Now I need the same button to FORGET his last purpose. Because I want to use the same button to change the screen between speed, clock and termometer. SPEED -> tap -> CLOCK -> tap -> TEMPERATURE -> tap -> SPEED ........

I hope you get the idea. After the user picks his size, if he keeps tapping the number it would just keep on growing. So, im trying to make the button forget, or make him go trough that loop only once.

Pick your size tap tap tap double tap, thank you. And stop! I want him to do that only once and never again. I will need the same principle when the user will adjust his clock, he will have to tap to set it, save the number but reset the buttons function.

#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal.h>
LiquidCrystal_I2C lcd(0x27, 16,2);
#define buttonPin 7        
int a = 16;
int b = 0;

void setup() {
  lcd.init();
  lcd.backlight();
  lcd.print("RADGROESSE IN");
  lcd.setCursor(0, 1);
  lcd.print("ZOLL EINGEBEN");

   // Set button input pin
   pinMode(buttonPin, INPUT);
   digitalWrite(buttonPin, HIGH );
}
void loop() {
   int b = checkButton();
   if (b == 1) clickEvent();
   if (b == 2) doubleClickEvent();
}
void clickEvent() {

   lcd.setCursor(14, 1);
    lcd.print(a);
    delay(100);
    lcd.print(" ");
    delay(100);
    a++;
    delay(100);
    if(a >= 23){
      a = 16;
      }
}
void doubleClickEvent() {
      b = a - 1;
      Serial.println("Button was idle for one second or more");
      lcd.clear();
      lcd.print("ZOLL = ");
      lcd.setCursor(7, 0);
      lcd.print(b);

}

//=================================================
//  MULTI-CLICK:  One Button, Multiple Events

// Button timing variables
int debounce = 20;          // ms debounce period to prevent flickering when pressing or releasing the button
int DCgap = 420;            // max ms between clicks for a double click event
// Button variables
boolean buttonVal = HIGH;   // value read from button
boolean buttonLast = HIGH;  // buffered value of the button's previous state
boolean DCwaiting = false;  // whether we're waiting for a double click (down)
boolean DConUp = false;     // whether to register a double click on next release, or whether to wait and click
boolean singleOK = true;    // whether it's OK to do a single click
long downTime = -1;         // time the button was pressed down
long upTime = -1;           // time the button was released

int checkButton() {   
   int event = 0;
   buttonVal = digitalRead(buttonPin);
   // Button pressed down
   if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce)
   {
       downTime = millis();
       singleOK = true;
       if ((millis()-upTime) < DCgap && DConUp == false && DCwaiting == true)  DConUp = true;
       else  DConUp = false;
       DCwaiting = false;
   }
   // Button released
   else if (buttonVal == HIGH && buttonLast == LOW && (millis() - downTime) > debounce)
   {       
           upTime = millis();
           if (DConUp == false) DCwaiting = true;
           else
           {
               event = 2;
               DConUp = false;
               DCwaiting = false;
               singleOK = false;
           }
   }
   // Test for normal click event: DCgap expired
   if ( buttonVal == HIGH && (millis()-upTime) >= DCgap && DCwaiting == true && DConUp == false && singleOK == true && event != 2)
   {
       event = 1;
       DCwaiting = false;
   }

   buttonLast = buttonVal;
   return event;
}

Thank you! I would really appreciate it!

3
  • When exactly does the user need to enter the bike size again? After restart / reset of Arduino? Or do you want the user to double click only once? Commented Mar 17, 2020 at 14:36
  • 1
    buttons do not have a memory ... there is nothing to forget ... the program has to respond to a button press depending on the state the program is in
    – jsotola
    Commented Mar 17, 2020 at 14:48
  • Yes, after the reset. I want the user to tap until his size and double tap to save the size. After that the bike size part shouldnt come across ever again, well of course if you reset it, but that wont be the case. After you choose the size the button will now work as a skip function. I basically want to change the function of a button. Commented Mar 17, 2020 at 15:01

2 Answers 2

3

jsotola wrote a very important term in the comments: state.

You should implement your project as a Finite State Machine (FSM), a very important and powerful, yet easy principle. You divide your program into different states. Each state contains code, that only gets executed, when the program is in this state. Depending on input events (like the user pressing the button), you do transitions between the states.

I already wrote a rather detailed answer about FSMs for this question. You can read it to get a better understanding of FSMs.

For your case I would suggest these states: INPUT_SIZE, DISPLAY_SPEED, DISPLAY_CLOCK and DISPLAY_TEMPERATURE. INPUT_SIZE is the initial state (which you might skip after the first power on, after you saved the size in persistent memory like EEPROM). There you execute your current button code. When you sense a double tap on the button, you change to the DISPLAY_SPEED state. From there on a button press to the DISPLAY_CLOCK state and so on.

In my linked answer I used a simple integer variable as state variable, which holds the current state. In fact it is better (easier to read), when you define an enum for the cases (enums are actually only named integer constants):

enum State {INPUT_SIZE, DISPLAY_SPEED, DISPLAY_CLOCK, DISPLAY_TEMPERATURE};

State state = INPUT_SIZE;

void loop(){
    switch(state){
        case INPUT_SIZE:
            // Button code here
            // on double tap use the following line
            state = DISPLAY_SPEED;
            break;
        case DISPLAY_SPEED:
            // code for displaying speed here
        ...
    }
}

That is just a little template for the structure. You need to fill it with life yourself.

1
  • Yes, exactly what I needed. Alright, thank you, I will study it, look online and try to implement it THANK YOU Commented Mar 17, 2020 at 15:08
4

What you likely need is a state machine. The behavior of the actions depends on the state the program is currently in.

In your case, the state machine might look like this:

State machine

What does it mean?

  • black circle: this is where the system powers on
  • rectangle: a state
  • arrow: a transition from one state to another
  • green text: action

For the problematic double click action, you can see:

  • it changes the state from "set size" to "speed"
  • in all other cases, it does not change the state (actually it changes the state to the same state)

I always recommend drawing the state machine before implementing it. It makes mistakes much more obvious.

There are several ways to implement it. The easiest one being a switch/case. More advanced implementations may use object orientation. When using switch/case, I prefer to have one method per state, like so:

void loop()
{
    switch(state):
        case setsize:
             setsize();
             break;
        case speed:
             speed();
             break;
        ...
}

If you know about function pointers, it gets even easier:

enum State {
  setsize = 0,
  speed,
  clock,
  temperature
};

State state;

void setup() {
  state = State::setsize;
}


typedef void (* functionPointer) ();
functionPointer process[] = {do_setsize, do_speed, do_clock, do_temperature};
void loop() {
  process[state]();
}

void do_setsize()
{
  state = State::speed;
}
void do_speed()
{
  state = State::clock;
}
void do_temperature()
{
  state = State::speed;
}
void do_clock()
{
  state = State::temperature;
}
1
  • Thank you. I managed to get it to work. Thank you! Commented Mar 18, 2020 at 14:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.