4 Way Click Button with Shift Register. Need help with a possible case statment

Hey Guys,

I’ve been working on this code for a couple days and cant seem to get the results I want. I am so close but not quite there.

The problem specifically is: When the doubleClickEvent() is pushed, the dClickIndex is cycled between

byte dClickPatternA = B0000001;
byte dClickPatternB = B00000000;

Turning the last pin on the shift register on or off.

BUT, when I activate the shortHoldEvent(), the last pin is no longer on because it is now cycling through the shortHoldIndex.

PROPOSED SOLUTIONS:

  • Can there somehow be a case statement that says “When this pin is on, keep it on and turn on/off other pins” ?

  • Can we write a bunch of if else statements that detects whether the index counter is even or odd?

I understand this is a tough question but I would really appreciate the help or any ideas that could help me come up with another solution.

THANKS!

  • Christian
/*
 
 Originally written as " 4-Way Button:  Click, Double-Click, Press+Hold, and Press+Long-Hold Test Sketch" By Jeff Saltzman
 Oct. 13, 2009
 
 Modified by Christian Terjesen
 January, 2015
 
 To keep a physical interface as simple as possible, this sketch demonstrates generating four output events from a single push-button.
 1) Click:  rapid press and release
 2) Double-Click:  two clicks in quick succession
 3) Press and Hold:  holding the button down
 4) Long Press and Hold:  holding the button for a long time 
 */

#define buttonPin 1      // analog input pin to use as a digital input
#define Pin1 13          // digital output pin 1
#define Pin2 2           // digital output pin 2
#define Pin3 5           // digital output pin 3
#define Pin4 6           // digital output pin 4
#define relayPin1 3      // digital output pin for relaypin1
#define relayPin2 4      // digital output pin for relaypin2

int latchPin = 8;
int clockPin = 12;
int dataPin = 11;

/*====================================================*/

boolean Val1 = false;    // state of LED 1
boolean Val2 = false;    // state of LED 2
boolean Val3 = false;    // state of LED 3
boolean Val4 = false;    // state of LED 4

boolean latch = LOW;
boolean dClick = LOW;
boolean shortHold = LOW;
boolean longHold = LOW;

/*====================================================*/

byte dClickPatternA = B0000001;
byte dClickPatternB = B00000000;

byte shortHoldPatternA = B00001110;
byte shortHoldPatternB = B00000000;

byte longHoldPatternA = B10000000;
byte longHoldPatternB = B00000000;

/*====================================================*/

byte dClickPatterns[4] = {
  
  dClickPatternA, 100,
  dClickPatternB, 100,
};

int dClickIndex = 0;
int dClickCount = sizeof(dClickPatterns) / 2;

byte shortHoldPatterns[4] = {
  
  shortHoldPatternA, 100,
  shortHoldPatternB, 100,
};

int shortHoldIndex = 0;
int shortHoldCount = sizeof(shortHoldPatterns) / 2;

byte longHoldPatterns[4] = {
  longHoldPatternA, 100,
  longHoldPatternB, 100,
};

int longHoldIndex = 0;
int longHoldCount = sizeof(longHoldPatterns) / 2;

/*=================================================*/

void setup() {
  // Set button input pin
  pinMode(buttonPin, INPUT);
  digitalWrite(buttonPin, HIGH );
  // Set LED output pins
  pinMode(Pin1, OUTPUT);
  digitalWrite(Pin1, Val1);
  pinMode(Pin2, OUTPUT);
  digitalWrite(Pin2, Val2);
  pinMode(Pin3, OUTPUT);    
  digitalWrite(Pin3, Val3);
  pinMode(Pin4, OUTPUT);    
  digitalWrite(Pin4, Val4);

  pinMode(relayPin1, OUTPUT);    
  digitalWrite(relayPin1, LOW);
  pinMode(relayPin2, OUTPUT);    
  digitalWrite(relayPin2, LOW);

  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);
  
}

/*=================================================*/

void loop() {
  // Get button event and act accordingly
  int b = checkButton();
  if (b == 1) clickEvent();
  if (b == 2) doubleClickEvent();
  if (b == 3) holdEvent();
  if (b == 4) longHoldEvent();
  
}

/*=================================================*/
// Events to trigger

void clickEvent() {
  Val1 = !Val1;

  if (latch) {  // set relay
    digitalWrite (Pin1, Val1);
    digitalWrite (relayPin1, HIGH);
    delay(15);
    digitalWrite (relayPin1, LOW);
  } 
  else {  // reset relay
    digitalWrite (Pin1, Val1);
    digitalWrite (relayPin2, HIGH);
    delay(15);
    digitalWrite (relayPin2, LOW);
  }
  latch = !latch;
  
/*=================================================*/  

}
void doubleClickEvent() {
  Val2 = !Val2;
  
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, dClickPatterns[dClickIndex * 2]);
  digitalWrite(latchPin, HIGH);
  delay(dClickPatterns[(dClickIndex * 2) + 1]);
  dClickIndex++;
  if (dClickIndex >= dClickCount){
    dClickIndex = 0;
  }


/*=================================================*/

}
void holdEvent() {
  Val3 = !Val3;

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, shortHoldPatterns[shortHoldIndex * 2]);
  digitalWrite(latchPin, HIGH);
  delay(shortHoldPatterns[(shortHoldIndex * 2) + 1]);
  shortHoldIndex++;
  if (shortHoldIndex >= shortHoldCount){
    shortHoldIndex = 0;
  }
 
/*=================================================*/

}
void longHoldEvent() {
  Val4 = !Val4;
  
    digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, longHoldPatterns[longHoldIndex * 2]);
  digitalWrite(latchPin, HIGH);
  delay(longHoldPatterns[(longHoldIndex * 2) + 1]);
  longHoldIndex++;
  if (longHoldIndex >= longHoldCount){
    longHoldIndex = 0;
  }

}


//=================================================
//  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 = 150;            // max ms between clicks for a double click event
int holdTime = 1000;        // ms hold period: how long to wait for press+hold event
int longHoldTime = 2500;    // ms long hold period: how long to wait for press+hold 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
boolean ignoreUp = false;   // whether to ignore the button release because the click+hold was triggered
boolean waitForUp = false;        // when held, whether to wait for the up event
boolean holdEventPast = false;    // whether or not the hold event happened already
boolean longHoldEventPast = false;// whether or not the long hold event happened already

/*=================================================*/

int checkButton() {    
  int event = 0;
  buttonVal = digitalRead(buttonPin);
  // Button pressed down
  if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce)
  {
    downTime = millis();
    ignoreUp = false;
    waitForUp = false;
    singleOK = true;
    holdEventPast = false;
    longHoldEventPast = false;
    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)
  {        
    if (not ignoreUp)
    {
      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;
  }
  // Test for hold
  if (buttonVal == LOW && (millis() - downTime) >= holdTime) {
    // Trigger "normal" hold
    if (not holdEventPast)
    {
      event = 3;
      waitForUp = true;
      ignoreUp = true;
      DConUp = false;
      DCwaiting = false;
      //downTime = millis();
      holdEventPast = true;
    }
    // Trigger "long" hold
    if ((millis() - downTime) >= longHoldTime)
    {
      if (not longHoldEventPast)
      {
        event = 4;
        longHoldEventPast = true;
      }
    }
  }
  buttonLast = buttonVal;
  return event;
}

Hi,

byte dClickPatternA = B0000001;
byte dClickPatternB = B00000000;

Are you missing a "0" in dClickPatternA?

If that's not the problem, it would help us understand if you could describe what your circuit is for, and post a schematic. Its not clear at the moment exactly what you are saying is wrong.

You can test for odd values like this:

if (n & 1) { .... }

Paul

I am missing a 0 in the dClickPatternA but I dont think that is the problem.

The goal of this project is to use a single momentary foot switch for guitar effects pedals to achieve different signal manipulation with various tap patterns, such as double click, short hold and long hold. The single click option is reserved for the relay. Eventually the shift register outs will either be connected to multiplexers or relays, but getting the code to work properly is more than half the battle.

I will try and test for even or odd values. Where would I put that portion of the code? In the void loop() ?

It would read something like

if (dClickCounter & 1) {

let shortHoldPatternA = B00001111;

}

The other problem is that if the short hold option is chosen first, and then the double click option is chosen then the dClickPatternA becomes B00001111.

This is why I think that a case statement would work best in this case. Thoughts?