4-Digit Touch Sensor debounce

Good day,
I am quite new to the programming of Arduino's and I am trying to get my head around programming and this new strange language. I have a 4 button touch sensor and have a rather chunky code that can read and display buttons touched, there seems to be no debounce as repeated input is seen in serial monitor, vs being determined as a single press. How does one determine between one press, a press and hold, press for on again for off The code is to read button status and then turn on/off internal led.

t*/
int ledPin = 13;
int button1 = 5;
int button2 = 4;
int button3 = 3;
int button4 = 2;

void setup() {
    Serial.begin(9600);
    pinMode(button1, INPUT);
    pinMode(button2, INPUT);
    pinMode(button3, INPUT);
    pinMode(button4, INPUT);
    pinMode(ledPin, OUTPUT);
}

void loop() {
    int buttonState1 = digitalRead(button1);
    if (buttonState1 == HIGH) {
    digitalWrite(ledPin, HIGH);
    Serial.println("1 touched");
    }else{
      digitalWrite(ledPin, LOW);
    }
    int buttonState2 = digitalRead(button2);
    if (buttonState2 == HIGH) {
      digitalWrite(ledPin, HIGH);
      Serial.println("2 pressed");
    }else{
      digitalWrite(ledPin, LOW);
    }
    int buttonState3 = digitalRead(button3);
    if (buttonState3 == HIGH) {
      digitalWrite(ledPin, HIGH);
      Serial.println("3 smashed");
    }else{
      digitalWrite(ledPin, LOW);
    }
    int buttonState4 = digitalRead(button4);
    if (buttonState4 == HIGH) {
      digitalWrite(ledPin, HIGH);
      Serial.println("4 score");
    }else{
      digitalWrite(ledPin, LOW);
    }
    delay(50);
} 
 
*/

Thanks for any assistance in this learning curve.

Have a great day.

Welcome to the forum

In general, it is a matter of timing. Save the value of millis() when the button becomes pressed then test whether it is still pressed at frequent intervals.

If the button is released within the period that the contacts might bounce then forget about the initial press and start again. To detect a long press it is a matter of testing the input state for a longer period

You ask about press for on and press again for off. See the StateChangeDetection example in the IDE

state change and millis like a standard push button would function the same.

I will play around with that

Thanks

examples for a single button debounce does not seem to help much on a 4 button sensor pad :frowning:
I will have to do more digging, and learning. I am out of compilations for the day. Will pick this up another time. Everyone have a great day.

thanks

I foresee arrays in your near future

Either that or 4 lots of repeated code

I am aware of giant cumbersome code. so the change state works with the touch sensor,

const int buttonPin = 2;  // the pin that the pushbutton is attached to
const int ledPin = 13;    // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;  // counter for the number of button presses
int buttonState = 0;        // current state of the button
int lastButtonState = 0;    // previous state of the button

void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}


void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes: ");
      Serial.println(buttonPushCounter);
    } else {
      // if the current state is LOW then the button went from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;


  // turns on the LED every four button pushes by checking the modulo of the
  // button push counter. the modulo function gives you the remainder of the
  // division of two numbers:
  if (buttonPushCounter % 4 == 0) {
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }
}

soooooo. to write the remaining 3 buttons on the touch sensor I can copy paste this code for each button , or use some button[5] (pins, 2,3,4,5). kinda fancy stuff.
Yah very new sorry

Arrays are the way to go to avoid repeating code 4 times. Use a for loop to test each of the buttons in turn using the for loop variable as the index to the arrays

If you want to take it further then an array of structs is even neater because you can have all of the button data in a single array of mixed data types

now that I am digesting word salads, where does one start to understand the structure and implementation of arrays, I am just mastering digitalWrite.. haha

Yes, salad is always available on demand :grinning:

Start with a separate array of the appropriate type for each value and forget structs for now

Something like this

const int buttonPins[] = { A3, A2, A1, A0 };
const int ledPins[] = { 3, 5, 6, 9 };

int buttonPushCounters[] = { 0, 0, 0, 0 };
int buttonStates[] = { HIGH, HIGH, HIGH, HIGH };
int lastButtonStates[] = { HIGH, HIGH, HIGH, HIGH };

void setup()
{
    for (int p = 0; p < 4; p++)
    {
        pinMode(buttonPins[p], INPUT_PULLUP);
        pinMode(ledPins[p], OUTPUT);
        digitalWrite(ledPins[p], HIGH);
    }
    Serial.begin(115200);
}

void loop()
{
    for (int b = 0; b < 4; b++)
    {
        buttonStates[b] = digitalRead(buttonPins[b]);
        if (buttonStates[b] != lastButtonStates[b])
        {
            if (buttonStates[b] == HIGH)
            {
                buttonPushCounters[b]++;

                Serial.println(buttonPushCounters[b]);
            }
            delay(50);
        }
        lastButtonStates[b] = buttonStates[b];

        if (buttonPushCounters[b] % 4 == 0)
        {
            digitalWrite(ledPins[b], HIGH);
        }
        else
        {
            digitalWrite(ledPins[b], LOW);
        }
    }
}

that was a plate of gnocci, haha so thank you for placing that out in code format for me to follow. Seeing what you have there is code is what i figures I would need to do.

I will post what I have right now for a cleaned up code specific to an application. The code right now is written for just the one button and understand the need to master the death rays, i mean arrays.

/* Scope of project for clearity of learning and reciving help.  

The premis is a desk top gadget powered be Arduino.  The gadget turns on lights, motors and then everything off via the 4 button touch sensor. 
 The sensor has 4 outputs for each touch button. 
 
 -Button 1- one click turns on white lights, click again and turns on red, click again and green light turns on.  All three lights on and the 4th click turns them off.  
 -Button 2- one click turns on fan motor, click again and turns off fan motor.
 -Button 3- one click turns on vibrate motor, click again it turns off. 
 -Button 4- Master turn off all lights and motors button. 
*/

// this constant won't change: Writen for interaction with button 1of4 only.  

const int buttonPin1 = 5;  // the pin that the pushbutton #1 is attached to
const int ledPin = 13;    // the pin that the white LED is attached to
const int ledPin2 = 12;   // the pin that the red LED is attached to
const int ledPin3 = 11;   // the pin that the greenLED is attached to
const int motorPin = 10;   //motor pin is attached to 
const int vibPin = 9;     //vibrate motor pin is attached to

// Variables will change:
int buttonPushCounter1 = 0;  // counter for the number of button presses for one of the 4 touch pad buttons
int buttonState1 = 0;        // current state of the button for one of the 4 touch pad buttons
int lastButtonState1 = 0;    // previous state of the button for one of the 4 touch pad buttons

void setup() {
  // initialize the button 1 pin as a input:
  pinMode(buttonPin1, INPUT);
  
  // initialize the LED and motors as an output:
  pinMode(ledPin, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  pinMode(motorPin, OUTPUT);
  pinMode(vibPin, OUTPUT);
  
  // initialize serial communication:
  Serial.begin(9600);
}


void loop() {
  // read the pushbutton input pin:
  buttonState1 = digitalRead(buttonPin1);

  // compare the buttonState to its previous state
  if (buttonState1 != lastButtonState1) {
    // if the state has changed, increment the counter
    if (buttonState1 == HIGH) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter1++;
      Serial.println("on");
      Serial.print("number of button pushes: ");
      Serial.println(buttonPushCounter1);
    } else {
      // if the current state is LOW then the button went from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState1 = buttonState1;


  // each click of the button will have a desired outcome. after 4 pushes the counter resets.  
  //example: 1 touch for white light, 2 touch for blue, 3 touch red, 4 touch off. digital outs for addition leds not represented in code
  
  if (buttonPushCounter1 == 1) {
    digitalWrite(ledPin, HIGH);
  } 
  if (buttonPushCounter1 == 2) { 
    digitalWrite(ledPin2, HIGH);
  }
  if (buttonPushCounter1 == 3) {
    digitalWrite(ledPin3, HIGH);
  }
  if (buttonPushCounter1 == 4) {
    digitalWrite(ledPin, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
  }
  if (buttonPushCounter1 == 4) buttonPushCounter1 = 0;
}

I will go through the generously supplied example and see what I can learn and self formulate. This salad just might end up like a blender meal, but hey worth trying.

cheers,

When you look at my example bear in mind that you may need to change the logic to match your project. For instance, I used INPUT_PULLUP as the pinMode() and have the buttons wired to take the input pin LOW when pressed and I also have the LEDs wired such that LOW turns them on

The actual program logic is taken directly from the StateChangeDetection example. All I did was to make each variable an array so that there was 4 lots of data instead of 4 lots of code. To help understand what is going on I also used the same variable names but just added an s to each of them as they are all plural

Come back if you are stuck and come back too if/when you want to see the "posh" version with a single array (of structs)

gong through array tutorials now.

May I ask why the led on when LOW, I r a 12v atuomotive guy and like when things turn on when power supplied in my head(HIGH). Are there any advantages of how you wired the code with "on" with LOW signal?

As I am sure that you realise it does not matter to the LED whether its anode or cathode are switched although humans tend to think of HIGH as on and LOW as off.

My test system is on a PCB that I designed and I cannot remember why I made the LEDs active LOW. It may be that routing the PCB traces was easier that way but it is a while ago now

The use of INPUT_PULLUP for the button inputs was deliberate in order to remove the need to add external pullup or pulldown resistors

I pieced together some of the beginning of the code. __ for int b = 0; b <4; b++__ in English, I read constant is "b", if "b" is less than 4 "b" get two fist bumps. No really I am barley literate, so learning new languages I have to go through some hoops of understanding.

So now that we have grouped the variables, have a loop for button state, how do I break that out into action. Button 1 has 4 to clicks to count and react to. Buttons 2 and 3 have 2 clicks to watch for and the last button just needs one click for all "off". This is where I cannot put what I want to see happen into silicon speak.

any suggestions to vids or peeps that elaborate for dummies I can use a a resource is appreciated.

/*

*/

const int ledPins [] = {13, 12, 11};
const int pinCount = 6;
const int buttonPins [] = {2, 3, 4, 5};
const int motorPin = 10;
const int vibPin = 9;

int buttonPushCounters[] = {0, 0, 0, 0 };
int buttonStates[] = {HIGH, HIGH, HIGH, HIGH };
int lastButtonStates[] = {HIGH, HIGH, HIGH, HIGH};

void setup() {
    for (int p = 0; p < 4; p++)
    pinMode(buttonPins[p], INPUT_PULLUP);
    pinMode(ledPins[p], OUTPUT);
    digitalWrite(ledPins[p], HIGH);
}
Serial.begin(9600)


void loop() {
    for (int b = 0; b <4; b++)
    {
      buttonStates[b] = digitalRead(buttonPins[b]);
      if (buttonStates[b] != lastButtonStates[b])
      {
        if (buttonStates[b] == HIGH)
        {
          buttonPushCounters[b]++;
          Serial.println(buttonPushCounters[b]);
        
        }
        delay(50);
      }
      lastButtonStates[b] = buttonStates[b];
    }
}

toodles for now

The simple but clumsy way is to add another array that holds the number of clicks received before action is taken. Then, in the for loop that reads the inputs, after you increment the counter for that button test whether the click limit for that button has been reached and, depending on the button number call a function to take the appropriate action.

There is a smarter way to deal with this (an array of structs again) but I am afraid that your head will explode at the thought of it at the moment. For now I suggest that you try adding the extra array and call a function if the limit has been reached for the button

So yah lots of pieces to put together. I figured lets start in smaller steps. I have a program that works, the real live assembly works, and it is soon to be put into its 3D printed housing. Then sent out open source to the health community that will utilize structured water.

The only difference is a single tactile button, to turn on ambient light, motor, uv light, then fourth click turns it all off.

I took a moment to make it way more cool by adding in the "knight Rider" [not dating myself] led timing and the use of array for pins. Again baby steps.

Now it kinda works (small hurrray!!!) but again kinda.....in the simulation the ambient knight riders turn on with the 1 click and they vibe back and forth (winning), but on second "click" (to turn on motor) nothing happens, 3rd click nothing, 4th click nothing, 5th click the knight rider turns off (clicks 2-4 do not register in monitor). Then we are back in sequence with the UV lights coming on and then last click turns everything off.

So I have a structure issue in the loop. I also // dashed out the digitalWrite ledPin in the last loop block (click #4) to turn everything off as I have issues making that work with the knight rider addition. So I know enough to be dangerous, but don't understand well enough to get this code back to safety.



// this constant won't change:
const int buttonPin = 2;  // the pin that the pushbutton is attached to
const int ledPins [] = { 10, 11, 12};    // the pins that the ambient LEDs is attached to
const int motorPin = 3;   //the pin the motor is attached to 
const int uvPin = 4;   // the pin that the UV LED's attached to
const int timer = 100;  // timer for knight rider ambient light

// Variables will change:
int buttonPushCounter = 0;  // counter for the number of button presses
int buttonState = 0;        // current state of the button
int lastButtonState = 0;    // previous state of the button

void setup() {
// initialize the button pin as a input:
pinMode(buttonPin, INPUT_PULLUP);
// initialize the LED's and motor as an output:
//pinMode(ledPin, OUTPUT);
for (int thisPin = 10; thisPin < 13; thisPin++) {
  pinMode(thisPin, OUTPUT);}

pinMode(motorPin, OUTPUT);
pinMode(uvPin, OUTPUT);
 
  // initialize serial communication:
Serial.begin(9600);
}


void loop() {
// read the pushbutton input pin:
buttonState = digitalRead(buttonPin);


// compare the buttonState to its previous state
if (buttonState != lastButtonState) {
  // if the state has changed, increment the counter
  if (buttonState == HIGH) {
    // if the current state is HIGH then the button went from off to on:
    buttonPushCounter++;
    Serial.println("on");
    Serial.print("number of button pushes: ");
    Serial.println(buttonPushCounter);
  } else {
    // if the current state is LOW then the button went from on to off:
    Serial.println("off");
  }
  // Delay a little bit to avoid bouncing
  delay(50);
}
// save the current state as the last state, for next time through the loop
lastButtonState = buttonState;


// Controlling the Clicks

if (buttonPushCounter == 1) {
  // digitalWrite(ledPin, HIGH);   //one click turns on the ambient LED
    for (int thisPin = 10; thisPin < 13; thisPin ++){
      //turn pin on
      digitalWrite(thisPin, HIGH);
      delay(timer);
      //turn pin off
      digitalWrite(thisPin, LOW);
    }
    for (int thisPin = 12; thisPin >= 10; thisPin --) {
      // turn pin on
      digitalWrite(thisPin, HIGH);
      delay(timer);
      digitalWrite(thisPin, LOW);
    }
    
}
if (buttonPushCounter == 2){
  digitalWrite(motorPin, HIGH);   //two cliks turns on the motor while ambient LED stays on
}
if (buttonPushCounter == 3){
digitalWrite(uvPin, HIGH);  //three clicks turns on the UV LED's while motor and ambient LED stay on
}	


if (buttonPushCounter == 4){
 // digitalWrite(ledPins[3] {10, 11, 12}, LOW);
  digitalWrite(motorPin, LOW);
  digitalWrite(uvPin, LOW);       //four clicks turns all LED's and motor off
}
if(buttonPushCounter == 4)buttonPushCounter = 0;
}


So with this code and this project that has meaning to me, i feel i can learn the logic with a little more intent. The lessons here I think could then carry over to arrays on the 4 button touch sensor as it is really the pins that matter not so much the peripheral device.

I thank you in advance for taking time out of your life to school a stranger.

Happy regards,

Included is the unit that will be powered by this code for context.

I have had a quick look and I suspect that the problem is your use of the delay() function, particularly in the Knight Rider code

Whilst this for loop is running, which is most of the time when buttonPushCounter == 1. then the code does not read the input for nearly a second due to the delay()s so changes in input state will not be noticed by the code

You can have a Knight Rider display but you will need to use a non blocking timing technique to do it. This involves using the loop() function to do the looping and millis() for timing so that the inputs can be read very frequently

This is not actually very complicated but is yet another thing to get your head round

If you want to have a look at the technique then See Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

yes I found myself going down that road. the delay is so easy to use, but the logic is it pauses everything. I will look into how to structure that in.

I have gone through your post on millis, and am trying to incorporate what i am learning. The code does not crash and the clicks now respond the way i want them to, but I dont think i have the "knight rider" effect right. the lights quasi come on, but no swooshing back and forth.
My structure in loops is still a work in progress. I am sure you have seen worse.

 if (buttonPushCounter == 1) {
    // digitalWrite(ledPin, HIGH);   //one click turns on the ambient LED
      currentMillis = millis();
      if (currentMillis - startMillis >= period);
      {
      for (int thisPin = 10; thisPin < 13; thisPin ++){      //turn pins on right to left
        
        digitalWrite(thisPin, !digitalRead(thisPin));
        startMillis = currentMillis;
        }
        
      }
    currentMillis = millis();
    if (currentMillis - startMillis >= period);
      for (int thisPin = 12; thisPin >= 10; thisPin --) {      //turn pins on left to right
        // turn pin on
        digitalWrite(thisPin, !digitalRead(thisPin));
        startMillis = currentMillis;
      }