Problem with managing button/logic states.

So I'm trying to create a MIDI controller for a guitar pedal I have.

On the physical pedal (a Line6 M9) each button serves 2 functions.

Button1 is Record/Overdub
Button2 is Play/Stop

If nothing is happening, hitting button one will go into record mode and create a loop. If you then press button 1 again, it goes into PLAY mode. If you press button 2 instead, it goes into PLAY mode. If you press button 2 while in PLAY mode it goes STOPs. If while PLAYing you press button1, it goes back into OVERDUB.

That's sort of the basic operating principles.

Now, in order to manage those interdependent states, I've created this mess of a code, and it doesn't work. I'm guessing that I create impossible states, and the code can't get out of it.

After wrestling with debouncing stuff, I went with the Button library, and so far that's been working fine. Just don't know how to keep track of what's going on, that would be error proof.

Also, a smaller detail. Is there a benefit (other than tidiness) for nesting IF loops? Like in my code, the first 3 bits contain "((button1.uniquePress())". Would it be better to have an IF loop that starts with that, then uses ELSES to go into the next criteria?

Here's my code.

#include <Button.h>

#define LED 13

Button button1 = Button(8,PULLDOWN);
Button button2 = Button(9,PULLDOWN);
Button button3 = Button(10,PULLDOWN);

int state1 = 0;               // state of notrecording(0),record(1),overdub(2)
int state2 = 0;               // state of stop(0),play(1)

void setup() {
  pinMode(13,OUTPUT);
  Serial.begin(31250);
}

void loop(){
  
  // ======= RECORD NEW LOOP
if ((button1.uniquePress()) && (state1 == 0) && (state2 == 0)) {
  state1 = 1;
  Serial.print(0xb0,BYTE);
  Serial.print(50,BYTE);
  Serial.print(127,BYTE);
  digitalWrite(LED,HIGH);
  delay(5);
  digitalWrite(LED,LOW);
}
  
  // ======= OVERDUB (FROM RECORD STATE)
if ((button1.uniquePress()) && (state1 == 1) && (state2 == 0)) {
  state1 = 2;
  state2 = 1;
  Serial.print(0xb0,BYTE);
  Serial.print(50,BYTE);
  Serial.print(0,BYTE);
  digitalWrite(LED,HIGH);
  delay(5);
  digitalWrite(LED,LOW);
}

// ======= OVERDUB (TO PLAY STATE)
if ((button1.uniquePress()) && (state1 == 2) && (state2 == 1)) {
  state1 = 0;
  Serial.print(0xb0,BYTE);
  Serial.print(50,BYTE);
  Serial.print(0,BYTE);
  digitalWrite(LED,HIGH);
  delay(5);
  digitalWrite(LED,LOW);
}

// ======= OVERDUB (FROM PLAY STATE)
if ((button1.isPressed()) && (state1 == 0) && (state2 == 1)) {
  state1 = 2;
  Serial.print(0xb0,BYTE);
  Serial.print(50,BYTE);
  Serial.print(0,BYTE);
  digitalWrite(LED,HIGH);
  delay(5);
  digitalWrite(LED,LOW);
}

// ======= PLAY
if ((button2.uniquePress()) && (state2 == 0)) {
  state2 = 1;
  Serial.print(0xb0,BYTE);
  Serial.print(28,BYTE);
  Serial.print(127,BYTE);
  digitalWrite(LED,HIGH);
  delay(5);
  digitalWrite(LED,LOW);
}

// ======= STOP
if ((button2.uniquePress()) && (state2 == 1)) {
  state2 = 0;
  Serial.print(0xb0,BYTE);
  Serial.print(28,BYTE);
  Serial.print(0,BYTE);
  digitalWrite(LED,HIGH);
  delay(5);
  digitalWrite(LED,LOW);
}

  // ======= PLAY ONCE
  if(button3.uniquePress()){
  Serial.print(0xb0,BYTE);
  Serial.print(80,BYTE);
  Serial.print(127,BYTE);
  digitalWrite(LED,HIGH);
  delay(5);
  digitalWrite(LED,LOW);
  }
}

I thought about using the built in library "stateChanged" but since the button logic is intertwined, I didn't know if that would work right off the bat.

Like if I press button1, and go into RECORD mode, then press button2, to enter PLAY mode, then button2 again to go into STOP mode, when I press button1 again, I would need to go to RECORD mode again, not OVERDUB mode, which is what would be the next state for the button

OK, so I've messed with it some more, and tried a more simplified approach. Using only 1 button, and changing between two states.

I also went from 2 variables to determine button states, to 1 variable. In this example, only two states are used, 0 and 1. The fuller bit I'm working with uses 0,1,2,3.

This isn't working right, and I don't know why.

The button behavior is erratic. Sometimes it works right away, other times I have to press it several times for it to do anything. The LED only lights up when something happens (so the problem isn't on the receiving side of the unit being controlled).

From this code, I would expect to have the state be at 0 at startup. When I press the button, it sends one message, and changes the state to 1. Then when I press the button the next time, a different message is sent and the state changed back to 0.

Now I know this can be done with the "wasPressed" command, but with the nested button logic, I need to be able to have them intertwined if that makes sense.

Here is updated/simplified code:

#include <Button.h>

#define LED 13

Button button2 = Button(9,PULLDOWN);

int state = 0;               // state of stop(0),play(1),record(2),overdub(3)

void setup() {
  pinMode(13,OUTPUT);
  Serial.begin(31250);
}

void loop(){

// ======= PLAY
if ((button2.uniquePress()) && (state == 0)) {
  state = 1;
  Serial.print(0xb0,BYTE);
  Serial.print(28,BYTE);
  Serial.print(127,BYTE);
  digitalWrite(LED,HIGH);
  digitalWrite(LED,LOW);
  }

// ======= STOP
if ((button2.uniquePress()) && (state == 1)) {
  state = 0;
  Serial.print(0xb0,BYTE);
  Serial.print(28,BYTE);
  Serial.print(0,BYTE);
  digitalWrite(LED,HIGH);
  digitalWrite(LED,LOW);
  }
}

I thought about using the built in library "stateChanged" but since the button logic is intertwined, I didn't know if that would work right off the bat.

The stateChanged method of the button library (just to straighten out the terminology) returns true if the button state has changed (pressed to released or released to pressed). It has nothing to do with a state machine, which is what you are trying to created.

You need to test once whether uniquePress returns true, not multiple times:

if(button.uniquePress())
{
   // the button was pressed
   // define new state based on existing state
}

Not sure I follow the last part of your post. I tried to narrow it down in the last bit of code I posted, so I check for button press and current state, then alter the state and run code.

To me it looks like the code should just alternate between the two bits, but it doesn't.

The first time you call button.uniquePress(), it returns true or false, depending on whether or not the button was pressed since the last call.

The next time you call button.uniquePress(), it will return false, unless you are very, very, very, very, very, very fast at pushing the button.

So, you should only call it once. If it returns true, a state change is needed. The new state depends on the old state and which button was pressed, although the last code you posted only concerns itself with one button.

Ok, that's starting to make sense.

I've got this going now, and there's progress. The light flickers each time I press the button, so all presses are being read.

My nested code isn't working right though (first time I try to do that), as I seem to only be sending the first bit of the code (if state == 0).

#include <Button.h>

#define LED 13

Button button2 = Button(9,PULLDOWN);

int state = 0;               // state of stop(0),play(1),record(2),overdub(3)

void setup() {
  pinMode(13,OUTPUT);
  Serial.begin(31250);
}

void loop(){

if (button2.uniquePress()){
// ======= PLAY
  if(state == 0){
  state = 1;
  Serial.print(0xb0,BYTE);
  Serial.print(28,BYTE);
  Serial.print(127,BYTE);
  digitalWrite(LED,HIGH);
  digitalWrite(LED,LOW);
  }
// ======= STOP
  if(state == 1){
  state = 0;
  Serial.print(0xb0,BYTE);
  Serial.print(28,BYTE);
  Serial.print(0,BYTE);
  digitalWrite(LED,HIGH);
  digitalWrite(LED,LOW);
  }
}
}

I added an "else if" in between the two bits and now it seems to work.

Ok, now to try to expand this out to the full functionality again.

So code structure wise, I only want to have one check per button, with each state check/change being nested within that with a series of "else if's"?

So code structure wise, I only want to have one check per button, with each state check/change being nested within that with a series of "else if's"?

Yes.

Man, thanks a lot for this. I've been banging my head with this for weeks now (on and off).

I've gotten it kind of working now. Some states aren't shifting, but it's probably bits in the code I'm messing up. At least now I've got a working framework.