Logic help with button presses

I have a functioning project that uses code I have written and the last gremlin to fixes to do with inadvertent button presses by the operator.
I have a foot switch that when pressed it will start movement of stepper motors, they are all controlled by if statements and they move as coded but if the foot switch is activated a second time while the steppers are moving everything comes to a halt, except for one motor that I just turn on and let run and it will run all day until power is turned off to reset the Arduino.

I'm having trouble adding some statement to the existing if statements that I have to ignore any inadvertent button presses but for the life of me I'm stuck and was hoping a nudge will get me thinking about it correctly.
I tried to be cheeky and used the millis delay on the button to increase the debounce for a second or two and by that time movement would be stopped on the motors and everything would be reset and waiting for another button push. It worked a couple times and I thought I was good enough but then the button push wasn't registering for some reason.
Sorry for the length of the code, if you need me to pair it down let me know.
Any guidance is appreciated.

#include <AccelStepper.h>
#include <SPI.h>
#include <Wire.h>
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "Adafruit_STMPE610.h"

AccelStepper stapler(AccelStepper::DRIVER, 3, 4);
AccelStepper roserotate(AccelStepper::DRIVER, 12, 13);
AccelStepper gantry(AccelStepper::DRIVER, 5, 6);
AccelStepper carriage(AccelStepper::DRIVER, 7, 11);

// Default values for Adafruit shield v2.
#define STMPE_CS 8
#define TFT_DC 9
#define TFT_CS 10

Adafruit_STMPE610 ts = Adafruit_STMPE610(STMPE_CS);
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

// Assign human-readable names to some common 16-bit color values:
#define   BLACK   0x0000
#define   BLUE    0x001F
#define   RED     0xF800
#define   GREEN   0x07E0
#define   CYAN    0x07FF
#define   MAGENTA 0xF81F
#define   YELLOW  0xFFE0
#define   WHITE   0xFFFF

#define TS_MINX 150
#define TS_MINY 130
#define TS_MAXX 3800
#define TS_MAXY 4000

// Rotations 0,2 = portrait  : 0->USB=right,upper : 2->USB=left,lower
// Rotations 1,3 = landscape : 1->USB=left,upper  : 3->USB=right,lower
byte rotation = 2; //(0->3)
TS_Point p;
int x, y;
const byte row = 4;
const byte col = 2;
byte lastHit = 0;
byte currentHit = 6;
Adafruit_GFX_Button btn[row * col];

//VALUES FOR BUTTON
const int button = 2;            // pin button is on
int val = 0;                     // current button state
int old_val = 0;
int buttonstate = 0;
unsigned long previousMillis = 0;

//VALUES FOR TURNING STAPLER AT SELECTED NUMBER OF TURNS(using stapler hall Sensor) plus delay the start
int staplercounter = 0;
int staplercurrentState = 0;
int staplerpreviousState = 0;
int staplerSensor = 47;

unsigned long previousMillis2 = 0;
const long interval = 25;

//VALUE FOR GANTRY MOVE
int gantryin = 46;
int gantryout = 43;
int gantryhome;
int gantryfinish;

//VALUE FOR CARRIAGE MOVE
int carriagebottom = 45;
int carriagetop = 44;
int carriagehome;
int carriagefinish;

//VALUES FOR VARIABLE STEPS
int start = 0;
int steps = 0;
int staples = 0;

void setup() {
  delay(500);         //Allow LCD screen time to power up

  pinMode(button, INPUT);
  pinMode(staplerSensor, INPUT);
  pinMode(gantryout, INPUT);
  pinMode(gantryin, INPUT);
  pinMode(carriagetop, INPUT);
  pinMode(carriagebottom, INPUT);

  Serial.begin(115200);

  tft.begin();
  ts.begin();
  if (!ts.begin()) {
    Serial.println("Couldn't start touchscreen controller");
    while (1);
  }
  tft.setRotation(rotation);
  tft.fillScreen(BLACK);
  btnGrid();

  gantry.setMaxSpeed(1000.0);
  gantry.setAcceleration(1000.0);
  carriage.setMaxSpeed(1000.0);
  carriage.setAcceleration(1000.0);
  stapler.setMaxSpeed(10000.0);
  roserotate.setMaxSpeed(1000.0);

  previousMillis = millis();

  while (digitalRead(carriagetop) == HIGH) {
    carriage.move(1);
    carriage.setSpeed(800);
    carriage.runSpeed();
  }
  while (digitalRead(gantryin) == HIGH) {
    gantry.setSpeed(500);
    gantry.runSpeed();
  }
  delay(1000);
  
  while (digitalRead(gantryout) == HIGH) {
    gantry.setSpeed(-500);
    gantry.runSpeed();
  }
}

void loop() {
  /////VALUES FOR LCD SCREEN
  /////////////////////////////////////////////////////////////////////////////////
  if (!ts.bufferEmpty()) {
    p = ts.getPoint();
    switch (rotation) {
      case 0:
        x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
        y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());
        break;
      case 1:
        // p.x, p.y reversed //
        x = map(p.y, TS_MINY, TS_MAXY, 0, tft.width());
        y = map(p.x, TS_MAXX, TS_MINX, 0, tft.height());
        break;
      case 2:
        x = map(p.x, TS_MAXX, TS_MINX, 0, tft.width());
        y = map(p.y, TS_MAXY, TS_MINY, 0, tft.height());
        break;
      case 3:
        // p.x, p.y reversed //
        x = map(p.y, TS_MAXY, TS_MINY, 0, tft.width());
        y = map(p.x, TS_MINX, TS_MAXX, 0, tft.height());
        break;

    }

    while (ts.touched()) {
      for (uint8_t b = 0; b < row * col; b++) {
        if (btn[b].contains(x, y)) {
          btn[b].press(true);
          btn[b].drawButton(true);
          currentHit = b;
        } else if (btn[b].contains(x, y) == false) {
          btn[b].press(false);
          if (b == lastHit) {
            btn[b].drawButton(false);
          }
        } else {
          return;
        }
      }
      lastHit = currentHit;
    }
  }
  if (btn[0].contains(x, y)) {
    start = 1;
    steps = 10;
    staples = 4;
  }
  else if (btn[1].contains(x, y)) {
    start = 1;
    steps = 8;
    staples = 5;
  }
  else if (btn[2].contains(x, y)) {
    start = 1;
    steps = 8;
    staples = 6;

  } else if (btn[3].contains(x, y)) {
    start = 1;
    steps = 3;
    staples = 8;
  }
  else if (btn[4].contains(x, y)) {
    start = 1;
    steps = 9;
    staples = 11;
  }
  else if (btn[5].contains(x, y)) {
    start = 1;
    steps = 7;
    staples = 13;
  }
  else if (btn[6].contains(x, y)) {
    start = 2;
    loadwire();
  }
  else if (btn[7].contains(x, y)) {
    start = 1;
    staples = 1;
  }

  /////////////////////////////////////////////////////////////////////////////////
  starter();
  counter();

  gantryhome = digitalRead(gantryin);
  gantryfinish = digitalRead(gantryout);
  carriagehome = digitalRead(carriagebottom);
  carriagefinish = digitalRead(carriagetop);
  staplercurrentState = digitalRead(staplerSensor);

  if (buttonstate == HIGH && gantryhome == HIGH && start == 1) {

    gantry.move(1);                                 //move to sensor for stapling
    gantry.setSpeed(900);
  }


  if (buttonstate == HIGH && carriagehome == HIGH && start == 1) {

    carriage.move(-1);                         //move to sensor for stapling
    carriage.setSpeed(900);
  }


  if (buttonstate == HIGH && staplercurrentState == LOW && staplercounter >= 0 && start == 1) {      //In position turn with stapler count
    roserotate.move(-steps);
    roserotate.setSpeed(-800);
  }

  if (buttonstate == HIGH && gantryhome == LOW && start == 1) {         //run the stapler
    stapler.setSpeed(7000);
  }

  ///////////////////////////////////////////////////////////////////////////////

  if (staplercounter >= staples && gantryfinish == HIGH && start == 1) {          //Slide gantry back to start position

    stapler.setSpeed(0);                      //turn stapler motor off

    gantry.moveTo(-1);                         //move gantry back to start position
    gantry.setSpeed(-800);
  }
  if (staplercounter >= staples && carriagefinish == HIGH && start == 1) {          //Slide carriage back to start position

    carriage.move(250);
    carriage.setSpeed(600);
  }

  if (staplercounter >= staples && start == 1 && carriagefinish == HIGH) {
    buttonstate = 0;                   //reset button
    staplercounter = 0;                    //reset counter for next button push
    staplercurrentState = 0;               //reset state for next button push
  }
  stapler.runSpeed();
  gantry.runSpeedToPosition();
  carriage.runSpeedToPosition();
  roserotate.runSpeedToPosition();
}

void starter() {
  val = digitalRead(button);
  if ( (millis() - previousMillis) >= 200) {    //state machine for button & debounce
    if ((val == 0) && (old_val == 1)) {
      buttonstate = 1 - buttonstate;
    }
    previousMillis = millis();
    old_val = val;
    //Serial.println(buttonstate);
  }
}
void counter() {
  unsigned long currentMillis2 = millis();
  if (buttonstate == HIGH) {
    if (currentMillis2 - previousMillis2 >= interval) {
      previousMillis2 = currentMillis2;
      staplercurrentState = digitalRead(staplerSensor);     //used to time all events
    }
    if (staplercurrentState != staplerpreviousState) {    //check the count and add 1
      if (staplercurrentState == 1) {
        staplercounter = staplercounter + 1;
        Serial.println(staplercounter);
      }
    }
  }
  staplerpreviousState = staplercurrentState;
}
void btnGrid()
{
  int left, top;
  int l = 10;
  int t = 30;
  int w = 100;
  int h = 40;
  byte hgap = 30;
  byte vgap = 20;
  byte id = 0;
  char *titleStr[row * col] = {"4", "5", "6", "8", "11", "13", "Load", "1"};
  for (byte j = 0; j < row; j++) {
    for (byte i = 0; i < col; i++) {
      left = l + i * (w + vgap);
      top = t + j * (h + hgap);
      btn[id].initButtonUL( &tft, left, top, w, h, WHITE, RED, GREEN, titleStr[id], 3 );
      if (id == currentHit) {
        // inverted
        btn[id].drawButton(true);
      } else {
        btn[id].drawButton(false);
      }
      id++;
    }
  }
}
void loadwire() {
  if (buttonstate == HIGH) {
    stapler.setSpeed(6000);
  }
  if (staplercounter == 1) {
    stapler.setSpeed(0);
    buttonstate = LOW;
    staplercounter = 0;
    staplercurrentState = 0;
  }
  stapler.runSpeed();
}

Assuming the inputs are mechanical sswitches, how are they wired?  Refer to the graphic for your case.

As you can see, standard practice is to connect a switch input with either a pullup or pulldown, otherwise the input floats and its state cannot be relied upon.

@dougp I use the internal pullup, the foot switch works fine and doesn't have any problems with floating.
I can see why everything does what it does as far as the stepper motors are concerned, not all of the conditions are being met so they stop moving.
I just can't figure what condition to add so any other button push is ignored after the first press. After conditions are met I reset the button to low so I can start the process again.

Are you happy that the math on (integer) buttonstate is always giving you the desired outcome when there are two button presses?, e.g you dont see 0 -1 = -1? I would be tempted to make this a boolean throughout, then re-assign with bottonstate != buttonstate;

Maybe also have a play with using pin 2 as a hardware interrupt. The button press will always get picked up (which isn't what you always want), but with some logic you can then use the interrupt (the footswitch press ) in combination with a timer to decide what to do in loop.
So it would be something like this

const int IRQPIN = 2;
volatile bool flag = false;

void pcf_irq() {
  flag = true;
}


void setup() {
  pinMode(IRQPIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(IRQPIN), pcf_irq, FALLING);// RISING/CHANGE, are also options
}

void loop() {
  uint32_t now = millis();
  if (flag) {    // the button has been pressed
    // do a check here that the elapsed time is enough, and write to another bool.
    // if enough time elapsed , do all your stuff here, when complete reset the flag
    flag = false;  
  }
  // waste time here otherwise
  delay(10);  // optional, but maybe add a reminder to the operator to wake up and work harder
}

Nice wording for button debouncing.

'ang on a minute, I might have got me logic back to front, or insiode out. I've even confused myself the more I think about it. I think the hardware interrupt should always set the flag to true if the switch is pressed, and your time checking routines should always be able to tell if enough time has elapsed, so I think the "do something" within should always continue on its merry way unless it has been told to start all over again. But then again.. ....

I'm having difficulty understanding exactly what response you desire from each button press.
Is the function of the button solely to start the process, or do you intend that a second press will stop it?
Debouncing aside I'd not be happy that was sufficiently robust, I'd go for this
image

The code you posted doesn't reflect that.

void setup() {
  delay(500);         //Allow LCD screen time to power up

  pinMode(button, INPUT);
  pinMode(staplerSensor, INPUT);
  pinMode(gantryout, INPUT);
  pinMode(gantryin, INPUT);
  pinMode(carriagetop, INPUT);
  pinMode(carriagebottom, INPUT);

  Serial.begin(115200);

@dougp, you are correct. It's been a while since I've messed with this, the internal pullup was still having troubles with EMI so I installed an external 10K pullup and the problems went away.

@Dingo61 I'm unsure of what you are getting too, I just want to check if the button state has been pressed(HIGH) then run code. The button press always gets picked up which is why I'm trying to figure out how to code a logic statement to ignore any button press after the first one.

@johnerrington I'm happy with the first press to start the process, it works exactly the way I want. The problem occurs if the foot switch is pressed a second time, I need any other press to be ignored so the code can finish what it started and at the end of all movement I reset everything back to zero with the following code and then a press of the button will start the process again.

  if (staplercounter >= staples && start == 1 && carriagefinish == HIGH) {
    buttonstate = 0;                   //reset button
    staplercounter = 0;                    //reset counter for next button push
    staplercurrentState = 0;               //reset state for next button push
  }

1: If its a "press to go" you DONT need to debounce. And this debounce code is wrong. If you want to ignore a transient you need different code.

2: using "val" for an important value makes it difficult to search your code to see where it is getting changed.
3: buttonstate = 1 - buttonstate; turns the switch from a "press to go " to a "press to change" which is not what you want.

as far as I can see this is the only place "buttonstate" is getting changed except for

 if (staplercounter >= staples && start == 1 && carriagefinish == HIGH) {
    buttonstate = 0;                   //reset button
    staplercounter = 0;                    //reset counter for next button push
    staplercurrentState = 0;               //reset state for next button push
  }

if that is the "official" END OF SEQUENCE then it would be helpful to give it its own function
(as you did for "starter" to make it clear that is where the process stops.

1 Like

The code is what I learned from a book by Massimo Banzi on making a button behave when turning an LED on so I just used it here. What I hear you saying is I don't need the "Void starter" code at all and I should just digitalRead(button) and if High start the motors.
Is that correct?
Since the button just starts things I shouldn't need to change it to low with my END OF SEQUENCE code.
Will that eliminate my need to ignore any presses after the first one or will I need to establish some kind of counting code for button presses?

Hi @ribbonman, Similar to what @johnerrington mentioned about "button state", I think it is far safer to use a boolean for buttonstate, as then it can only ever be "true" or "false", ( rather than any valid +ve or -ve integer), which despite all good intentions might not be the value you want it to be. Also, just good practice to try to use boolen logic where you can, rather than comparing integers. Also appologies, my bad, I meant to put another assingment in the interrupt routine to set a "timeTheButtonWasLastPressed" variable which you can manipulate and compare with elsewhere. The advantage of interrupts is they are immediate and the code resumes where it left off. The disadvantage is the code inside must be really short. BTW, I'm not familiar with all the libraries you are using, but just check there are no pin conflicts, for example, the good old servo library (On boards other than the Mega) use of the library disables analogWrite() (PWM) functionality on pins 9 and 10, whether or not there is a Servo on those pins. Other libraries also reserve pins or rewrite waht they do. Just check through each of the library documentation to conform there are no pin "conflicts" or "reserved pins" in the libraries used.

I think what you are after is a sort of long interval "button debounce", on the footswitch, which is similar, (but possibly different) to the behavoiur you want ?, Something like below ?

const int buttonPin = 2;
const int ledPin = 13;
bool buttonPressed = false;// use a boolean for a state flag

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);// change to suit
}

void loop() {
  if (digitalRead(buttonPin) == LOW && !buttonPressed) {
    buttonPressed = true;
    digitalWrite(ledPin, HIGH);
    Serial.println("Button pressed");
    delay(5000);// dont actually use a delay here, this bit is just an analogue for the time it takes for your staple gun/ flux capacitor to complete its job, but you would embed/call your functions from here
    digitalWrite(ledPin, LOW);
  }
  else if (digitalRead(buttonPin) == HIGH) {
    buttonPressed = false;
  }
}

maybe just load the code and try smacking the footswitch (disbale other bits for now) to see if it does what you want. ?

I reworked my debounce code to look like the debounce example from the Arduino IDE and added your button press code so I code add logic to my motor movement to ignore extra button presses but it seems to have made all my hall sensor behave erratically. My counting sensor for the stapler motor seems to be off by 1 count each time and my carriagehome sensor doesn't seem to want to go low, the motor runs to it and stops but doesn't seem to read it so the stapler motor will start movement. I did have to change my button HIGH"s to LOW's on my logic statements to control movement because the debounce changed it.
Not sure where I went off.

The libraries are for the LCD screen and all pins are accounted for.

#include <AccelStepper.h>
#include <SPI.h>
#include <Wire.h>
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "Adafruit_STMPE610.h"

AccelStepper stapler(AccelStepper::DRIVER, 3, 4);
AccelStepper roserotate(AccelStepper::DRIVER, 12, 13);
AccelStepper gantry(AccelStepper::DRIVER, 5, 6);
AccelStepper carriage(AccelStepper::DRIVER, 7, 11);

// Default values for Adafruit shield v2.
#define STMPE_CS 8
#define TFT_DC 9
#define TFT_CS 10

Adafruit_STMPE610 ts = Adafruit_STMPE610(STMPE_CS);
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

// Assign human-readable names to some common 16-bit color values:
#define   BLACK   0x0000
#define   BLUE    0x001F
#define   RED     0xF800
#define   GREEN   0x07E0
#define   CYAN    0x07FF
#define   MAGENTA 0xF81F
#define   YELLOW  0xFFE0
#define   WHITE   0xFFFF

#define TS_MINX 150
#define TS_MINY 130
#define TS_MAXX 3800
#define TS_MAXY 4000

// Rotations 0,2 = portrait  : 0->USB=right,upper : 2->USB=left,lower
// Rotations 1,3 = landscape : 1->USB=left,upper  : 3->USB=right,lower
byte rotation = 2; //(0->3)
TS_Point p;
int x, y;
const byte row = 4;
const byte col = 2;
byte lastHit = 0;
byte currentHit = 6;
Adafruit_GFX_Button btn[row * col];

//VALUES FOR BUTTON
const int button = 2;            // pin button is on
bool buttonState;             // the current reading from the input pin
bool lastButtonState = LOW;   // the previous reading from the input pin
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;
bool buttonPressed = false;
int presses = 0;

//VALUES FOR TURNING STAPLER AT SELECTED NUMBER OF TURNS(using stapler hall Sensor) plus delay the start
int staplercounter = 0;
int staplercurrentState = 0;
int staplerpreviousState = 0;
int staplerSensor = 47;

unsigned long previousMillis2 = 0;
const long interval = 25;

//VALUE FOR GANTRY MOVE
int gantryin = 46;
int gantryout = 43;
int gantryhome;
int gantryfinish;

//VALUE FOR CARRIAGE MOVE
int carriagebottom = 45;
int carriagetop = 44;
int carriagehome;
int carriagefinish;

//VALUES FOR VARIABLE STEPS
int start = 0;
int steps = 0;
int staples = 0;

void setup() {
  delay(500);         //Allow LCD screen time to power up

  pinMode(button, INPUT);
  pinMode(staplerSensor, INPUT);
  pinMode(gantryout, INPUT);
  pinMode(gantryin, INPUT);
  pinMode(carriagetop, INPUT);
  pinMode(carriagebottom, INPUT);

  Serial.begin(115200);

  tft.begin();
  ts.begin();
  if (!ts.begin()) {
    Serial.println("Couldn't start touchscreen controller");
    while (1);
  }
  tft.setRotation(rotation);
  tft.fillScreen(BLACK);
  btnGrid();

  gantry.setMaxSpeed(1000.0);
  gantry.setAcceleration(1000.0);
  carriage.setMaxSpeed(1000.0);
  carriage.setAcceleration(1000.0);
  stapler.setMaxSpeed(10000.0);
  roserotate.setMaxSpeed(1000.0);


  while (digitalRead(carriagetop) == HIGH) {
    carriage.move(1);
    carriage.setSpeed(800);
    carriage.runSpeed();
  }
  while (digitalRead(gantryin) == HIGH) {
    gantry.setSpeed(500);
    gantry.runSpeed();
  }
  delay(1000);

  while (digitalRead(gantryout) == HIGH) {
    gantry.setSpeed(-500);
    gantry.runSpeed();
  }
}

void loop() {
  /////VALUES FOR LCD SCREEN
  /////////////////////////////////////////////////////////////////////////////////
  if (!ts.bufferEmpty()) {
    p = ts.getPoint();
    switch (rotation) {
      case 0:
        x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
        y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());
        break;
      case 1:
        // p.x, p.y reversed //
        x = map(p.y, TS_MINY, TS_MAXY, 0, tft.width());
        y = map(p.x, TS_MAXX, TS_MINX, 0, tft.height());
        break;
      case 2:
        x = map(p.x, TS_MAXX, TS_MINX, 0, tft.width());
        y = map(p.y, TS_MAXY, TS_MINY, 0, tft.height());
        break;
      case 3:
        // p.x, p.y reversed //
        x = map(p.y, TS_MAXY, TS_MINY, 0, tft.width());
        y = map(p.x, TS_MINX, TS_MAXX, 0, tft.height());
        break;

    }

    while (ts.touched()) {
      for (uint8_t b = 0; b < row * col; b++) {
        if (btn[b].contains(x, y)) {
          btn[b].press(true);
          btn[b].drawButton(true);
          currentHit = b;
        } else if (btn[b].contains(x, y) == false) {
          btn[b].press(false);
          if (b == lastHit) {
            btn[b].drawButton(false);
          }
        } else {
          return;
        }
      }
      lastHit = currentHit;
    }
  }
  if (btn[0].contains(x, y)) {
    start = 1;
    steps = 10;
    staples = 5;
  }
  else if (btn[1].contains(x, y)) {
    start = 1;
    steps = 8;
    staples = 6;
  }
  else if (btn[2].contains(x, y)) {
    start = 1;
    steps = 8;
    staples = 7;

  } else if (btn[3].contains(x, y)) {
    start = 1;
    steps = 3;
    staples = 9;
  }
  else if (btn[4].contains(x, y)) {
    start = 1;
    steps = 9;
    staples = 12;
  }
  else if (btn[5].contains(x, y)) {
    start = 1;
    steps = 7;
    staples = 14;
  }
  else if (btn[6].contains(x, y)) {
    start = 2;
    loadwire();
  }
  else if (btn[7].contains(x, y)) {
    start = 1;
    staples = 1;
  }

  /////////////////////////////////////////////////////////////////////////////////
  starter();
  counter();

  gantryhome = digitalRead(gantryin);
  gantryfinish = digitalRead(gantryout);
  carriagehome = digitalRead(carriagebottom);
  carriagefinish = digitalRead(carriagetop);
  staplercurrentState = digitalRead(staplerSensor);

  if (buttonState == LOW && presses <= 1 && gantryhome == HIGH && start == 1) {

    gantry.move(1);                                 //move to sensor for stapling
    gantry.setSpeed(900);
  }


  if (buttonState == LOW && presses <= 1 && carriagehome == HIGH && start == 1) {

    carriage.move(-1);                         //move to sensor for stapling
    carriage.setSpeed(900);
  }


  if (buttonState == LOW && presses <= 1 && staplercurrentState == LOW && staplercounter >= 0 && start == 1) {      //In position turn with stapler count
    roserotate.move(-steps);
    roserotate.setSpeed(-800);
  }

  if (buttonState == LOW && presses <= 1 && gantryhome == LOW && carriagehome == LOW && start == 1) {         //run the stapler
    stapler.setSpeed(7000);
  }

  ///////////////////////////////////////////////////////////////////////////////

  if (staplercounter >= staples && gantryfinish == HIGH && start == 1) {          //Slide gantry back to start position

    stapler.setSpeed(0);                      //turn stapler motor off

    gantry.moveTo(-1);                         //move gantry back to start position
    gantry.setSpeed(-800);
  }
  if (staplercounter >= staples && carriagefinish == HIGH && start == 1) {          //Slide carriage back to start position

    carriage.move(250);
    carriage.setSpeed(600);
  }

  if (staplercounter >= staples && start == 1 && carriagefinish == HIGH) {
    buttonState = 0;                         //reset button
    presses = 0;                             // reset press counter
    staplercounter = 0;                    //reset counter for next button push
    staplercurrentState = 0;               //reset state for next button push
  }
  stapler.runSpeed();
  gantry.runSpeedToPosition();
  carriage.runSpeedToPosition();
  roserotate.runSpeedToPosition();
}

void starter() {

  int reading = digitalRead(button);
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;
    }
    if (digitalRead(button) == LOW && !buttonPressed) {
      buttonPressed = true;
      presses++;
      Serial.print("Button pressed! Count: ");
      Serial.println(presses);
    }
    else if (digitalRead(button) == HIGH) {
      buttonPressed = false;
    }
  }
  lastButtonState = reading;
}

void counter() {
  unsigned long currentMillis2 = millis();
  if (buttonState == HIGH) {
    if (currentMillis2 - previousMillis2 >= interval) {
      previousMillis2 = currentMillis2;
      staplercurrentState = digitalRead(staplerSensor);     //used to time all events
    }
    if (staplercurrentState != staplerpreviousState) {    //check the count and add 1
      if (staplercurrentState == 1) {
        staplercounter = staplercounter + 1;
        Serial.println(staplercounter);
      }
    }
  }
  staplerpreviousState = staplercurrentState;
}
void btnGrid()
{
  int left, top;
  int l = 10;
  int t = 30;
  int w = 100;
  int h = 40;
  byte hgap = 30;
  byte vgap = 20;
  byte id = 0;
  char *titleStr[row * col] = {"4", "5", "6", "8", "11", "13", "Load", "1"};
  for (byte j = 0; j < row; j++) {
    for (byte i = 0; i < col; i++) {
      left = l + i * (w + vgap);
      top = t + j * (h + hgap);
      btn[id].initButtonUL( &tft, left, top, w, h, WHITE, RED, GREEN, titleStr[id], 3 );
      if (id == currentHit) {
        // inverted
        btn[id].drawButton(true);
      } else {
        btn[id].drawButton(false);
      }
      id++;
    }
  }
}
void loadwire() {
  if (buttonState == LOW) {
    stapler.setSpeed(6000);
  }
  if (staplercounter == 1) {
    stapler.setSpeed(0);
    buttonState = LOW;
    staplercounter = 0;
    staplercurrentState = 0;
    presses = 0;
  }
  stapler.runSpeed();
}

Hmm..., so the debounce sorta works, but there are other problems you didn't have before.
Starting with the debounce, just reading back through the posts, your reply to

@dougp I use the internal pullup, the foot switch works fine and doesn't have any problems with floating.
you have

pinMode(button, INPUT);// pin 2

the following will enforce it, if you don't use a physical pullup resistor ?

pinMode(buttonPin, INPUT_PULLUP);//ensure pin 2 is using internal pullup, (default is pulldown)

So I am assumning you still have the external resitor ?

The snippet I posted was built around the following logic, when your foot isn't on the switch, the default value is 1 (HIGH). When you press the footswitch (on pin 2 ?) , it makes a circuit and the state directly goes to 0 (LOW) and comes back to HIGH when you release the button.? (i.e. its not a latching switch?)
Also, it's a "press to make", not a "press to break" switch? (other wise my HIGH/LOW logic was back to front)

For debugging, sometimes the serial plotter (rather than the monitor) is quite a good way to see the state of int or float variables in one screen, albeit with a bit of scaling, (see Serial Plotter ) which might help with debug.

If the the switch and internal pullup are now working they way you want them to; and the switch has been succesfully debounced, then the remaining problems are sensor and counter related:

Not knowing quite how your rig is set up, I assume your hall sensors pins are low when activated,
1 - counting sensor for the stapler (off by 1), - check the value in the serial monitor to check it is incrementing how you think it should, i.e the counter only increments when the stapler is running? (check incrementing before vs after,
2 - carriagehome (hall?) sensor wont go low, - I assume you mean programatically ? (not electrically or magnetically?)

You have a really intersting project, cant wait to see the video of it in action.

Debounce is NOT what you need. All you need is to check the button and if it is pressed and released start your process.

@johnerrington . Correct, "debounce" is probably the wrong word, that's why I suggested 'sort of', and called it "a long interval debounce"

Admittedly, "debounce" is typically a word for coping with that few milliseconds of uncertainty on the state of a mechanical switch, but exactly the same process can be used to check any desired duration between "possible false contacts". Hence my suggestion to register the initial contact and set a "minimum interval" between registering consequent switch presses.
As you suggest, once the initial contact, (close to make, or close to break?, high or low) has been made , close off the rest of the process so nothing from the "outside" gets "inside". i.e. subsequent switch presses are ignored.

@ribbonman
Without really knowing your physical setup , I'm not quite sure where to go from here, other than suggest:
0 - Always use programmed internal pullups, rather than external pullup resistors, if the logic suits
1 - only ever use Boolean for logical true or false states,
2 - don't try and force integer comparison to do your bidding if you only want to compare "true" or "false".
3- Try, where possible to reform any logic so as to only check only one comparator at a time (i.e.<, >, or = ) rather than two (e.g. >= , <= ). For instance, >= means two things can be true at the same time and the condition is met, remove the ambiguity by making sure only one thing is compared and only one thing can be true at one time. Using =, usually works for int, but much safer to form your logic into < or >. as one of these conditions will alway be true
4 - It really helps to draw a logic table of possible conditions; check if any states are actually redundant, or if any get through your code without being caught.
5 - put everything that you can into functions, and where possible use local variables where possible. Then loop basically becomes a list of functions, in order of execution, with maybe some escape clauses.
6 - hardware control systems like touch LCD panels are fantastic, but sometimes it is worth knocking up something up in "Processing" to do the GUI control/reporting while you are building a project, then you can isolate the GUI control system from Arduino and reduces the complexity of your Arduino code, Processing compiles and executes really fast without having to wait to download everything to the board other than the bare bones hardware control. Once the physical world is working, then add in the LCD commands.
Can't wait to see the staples flying.

1 Like

@johnerrington
I tried this yesterday and the problem was as soon as I stopped holding the button the code would stop and couldn't figure out how to adjust the logic to make it work and since the debounce was already checking for a state change in the button I went with it.
I still had the problem with multiple button presses and I could just add it to the debounce and be done with it.

@Dingo61
I played with it further today and things work as coded but it seems I have to hold the button down for an extra count to get it to move the steppers fully, for some reason 1 stepper stops just short if I lift off the button to quickly.
Very strange behavior.

I finally got everything worked out and thought I would post the solution for me in case someone in the future has the same situation.
I basically added a press count for the button and keyed my logic statements to it by using this code snippet, I did have to put the button presses at 0 to get the logic to work for me but you can do what works for you.

void pressCounter() {
  unsigned long currentTime = millis();
  if (currentTime - previousTime >= pause) {
    if (digitalRead(button) == LOW && !buttonPressed && presses <= 0) {
      buttonPressed = true;
      presses++;
      Serial.print("Button pressed! Count: ");
      Serial.println(presses);

      previousTime = currentTime;
    }
    else if (digitalRead(button) == HIGH) {
      buttonPressed = false;
    }
  }
}

I did some minor clean up on the full code to make it easier to read so I will add it here also so anyone can see the whole code.

#include <AccelStepper.h>
#include <SPI.h>
#include <Wire.h>
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "Adafruit_STMPE610.h"

AccelStepper stapler(AccelStepper::DRIVER, 3, 4);
AccelStepper roserotate(AccelStepper::DRIVER, 12, 13);
AccelStepper gantry(AccelStepper::DRIVER, 5, 6);
AccelStepper carriage(AccelStepper::DRIVER, 7, 11);

// Default values for Adafruit shield v2.
#define STMPE_CS 8
#define TFT_DC 9
#define TFT_CS 10

Adafruit_STMPE610 ts = Adafruit_STMPE610(STMPE_CS);
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

// Assign human-readable names to some common 16-bit color values:
#define   BLACK   0x0000
#define   BLUE    0x001F
#define   RED     0xF800
#define   GREEN   0x07E0
#define   CYAN    0x07FF
#define   MAGENTA 0xF81F
#define   YELLOW  0xFFE0
#define   WHITE   0xFFFF

#define TS_MINX 150
#define TS_MINY 130
#define TS_MAXX 3800
#define TS_MAXY 4000

// Rotations 0,2 = portrait  : 0->USB=right,upper : 2->USB=left,lower
// Rotations 1,3 = landscape : 1->USB=left,upper  : 3->USB=right,lower
byte rotation = 2; //(0->3)
TS_Point p;
int x, y;
const byte row = 4;
const byte col = 2;
byte lastHit = 0;
byte currentHit = 6;
Adafruit_GFX_Button btn[row * col];

//VALUES FOR BUTTON
const int button = 2;            // pin button is on
bool val = 0;                     // current button state
bool old_val = 0;
bool buttonstate = 0;
unsigned long previousMillis = 0;

//VALUES FOR PRESS COUNTER
bool buttonPressed = false;
int presses = 0;
unsigned long previousTime = 0;
const unsigned long pause = 50;

//VALUES FOR TURNING STAPLER AT SELECTED NUMBER OF TURNS(using stapler hall Sensor) plus delay the start
int staplercounter = 0;
int staplercurrentState = 0;
int staplerpreviousState = 0;
int staplerSensor = 47;

unsigned long previousMillis2 = 0;
const long interval = 25;

//VALUE FOR GANTRY MOVE
int gantryin = 46;
int gantryout = 43;
int gantryhome;
int gantryfinish;

//VALUE FOR CARRIAGE MOVE
int carriagebottom = 45;
int carriagetop = 44;
int carriagehome;
int carriagefinish;

//VALUES FOR VARIABLE STEPS
int start = 0;
int steps = 0;
int staples = 0;

void setup() {
  delay(500);         //Allow LCD screen time to power up

  pinMode(button, INPUT);
  pinMode(staplerSensor, INPUT);
  pinMode(gantryout, INPUT);
  pinMode(gantryin, INPUT);
  pinMode(carriagetop, INPUT);
  pinMode(carriagebottom, INPUT);

  Serial.begin(115200);

  tft.begin();
  ts.begin();
  if (!ts.begin()) {
    Serial.println("Couldn't start touchscreen controller");
    while (1);
  }
  tft.setRotation(rotation);
  tft.fillScreen(BLACK);
  btnGrid();

  gantry.setMaxSpeed(1000.0);
  gantry.setAcceleration(1000.0);
  carriage.setMaxSpeed(1000.0);
  carriage.setAcceleration(1000.0);
  stapler.setMaxSpeed(10000.0);
  roserotate.setMaxSpeed(1000.0);

  homing();
}

void loop() {
  
  ScreenInputs();
  starter();
  pressCounter();
  stapleCounter();

  gantryhome = digitalRead(gantryin);
  gantryfinish = digitalRead(gantryout);
  carriagehome = digitalRead(carriagebottom);
  carriagefinish = digitalRead(carriagetop);
  staplercurrentState = digitalRead(staplerSensor);

  if (presses == 1 && gantryhome == HIGH && start == 1) {

    gantry.move(1);                                 //move to sensor for stapling
    gantry.setSpeed(900);
  }


  if (presses == 1 && carriagehome == HIGH && start == 1) {

    carriage.move(-1);                         //move to sensor for stapling
    carriage.setSpeed(900);
  }


  if (presses == 1 && staplercurrentState == LOW && staplercounter >= 0 && start == 1) {      //In position turn with stapler count
    roserotate.move(-steps);
    roserotate.setSpeed(-800);
  }

  if (presses == 1 && gantryhome == LOW && start == 1) {         //run the stapler
    stapler.setSpeed(7000);
  }

  ///////////////////////////////////////////////////////////////////////////////

  if (staplercounter >= staples && gantryfinish == HIGH && start == 1) {          //Slide gantry back to start position

    stapler.setSpeed(0);                      //turn stapler motor off

    gantry.moveTo(-1);                         //move gantry back to start position
    gantry.setSpeed(-800);
  }
  if (staplercounter >= staples && carriagefinish == HIGH && start == 1) {          //Slide carriage back to start position

    carriage.move(250);
    carriage.setSpeed(600);
  }

  EndofSequence();

  stapler.runSpeed();
  gantry.runSpeedToPosition();
  carriage.runSpeedToPosition();
  roserotate.runSpeedToPosition();
}

///////////////////////////functions for controlling movement//////////////////////

void starter() {
  val = digitalRead(button);
  unsigned long currentMillis = millis();
  
  if ( (currentMillis - previousMillis) >= 50) {    //state machine for button & debounce
    if ((val == 0) && (old_val == 1)) {
      buttonstate = 1 - buttonstate;
    }
    previousMillis = currentMillis;
    old_val = val;
    //Serial.println(buttonstate);
  }
}
////////////////////////////////////////////////////////////////////

void pressCounter() {
  unsigned long currentTime = millis();
  if (currentTime - previousTime >= pause) {
    if (digitalRead(button) == LOW && !buttonPressed && presses <= 0) {
      buttonPressed = true;
      presses++;
      Serial.print("Button pressed! Count: ");
      Serial.println(presses);

      previousTime = currentTime;
    }
    else if (digitalRead(button) == HIGH) {
      buttonPressed = false;
    }
  }
}
////////////////////////////////////////////////////////////////////////////////////

void stapleCounter() {
  unsigned long currentMillis2 = millis();
  if (presses == 1) {
    if (currentMillis2 - previousMillis2 >= interval) {
      previousMillis2 = currentMillis2;
      staplercurrentState = digitalRead(staplerSensor);     //used to time all events
    }
    if (staplercurrentState != staplerpreviousState) {    //check the count and add 1
      if (staplercurrentState == 1) {
        staplercounter = staplercounter + 1;
        Serial.println(staplercounter);
      }
    }
  }
  staplerpreviousState = staplercurrentState;
}
///////////////////////////////////////////////////////////////////////

void btnGrid()
{
  int left, top;
  int l = 10;
  int t = 30;
  int w = 100;
  int h = 40;
  byte hgap = 30;
  byte vgap = 20;
  byte id = 0;
  const char *titleStr[row * col] = {"4", "5", "6", "8", "11", "13", "Load", "1"};
  int currentHit = -1; // change this line to set the default button to not selected
  for (byte j = 0; j < row; j++) {
    for (byte i = 0; i < col; i++) {
      left = l + i * (w + vgap);
      top = t + j * (h + hgap);
      btn[id].initButtonUL( &tft, left, top, w, h, WHITE, RED, GREEN, titleStr[id], 3 );
      if (id == currentHit) {
        // inverted
        btn[id].drawButton(true);
      } else {
        btn[id].drawButton(false);
      }
      id++;
    }
  }
}

////////////////////////////////////////////////////////////////////////////////

void loadwire() {
  if (buttonstate == HIGH && presses == 1) {
    stapler.setSpeed(6000);
  }
  if (staplercounter == 1) {
    stapler.setSpeed(0);
    buttonstate = LOW;
    presses = 0;
    staplercounter = 0;
    staplercurrentState = 0;
  }
  stapler.runSpeed();
}
////////////////////////////////////////////////////////////////

void homing() {
  while (digitalRead(carriagetop) == HIGH) {
    carriage.move(1);
    carriage.setSpeed(800);
    carriage.runSpeed();
  }
  while (digitalRead(gantryin) == HIGH) {
    gantry.setSpeed(500);
    gantry.runSpeed();
  }
  delay(1000);

  while (digitalRead(gantryout) == HIGH) {
    gantry.setSpeed(-500);
    gantry.runSpeed();
  }
}
/////////////////////////////////////////////////////////////////////////////////

void EndofSequence() {
  if (staplercounter >= staples && start == 1 && carriagefinish == HIGH) {
    buttonstate = 0;                   //reset button
    presses = 0;                       //reset press counter
    staplercounter = 0;                 //reset counter for next button push
    staplercurrentState = 0;            //reset state for next button push
  }
}
///////////////////////////////////////////////////////////////////////

void ScreenInputs() {
unsigned long lastTouchUpdate = 0;

if (!ts.bufferEmpty()) {
    p = ts.getPoint();
    switch (rotation) {
      case 0:
        x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
        y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());
        break;
      case 1:
        // p.x, p.y reversed //
        x = map(p.y, TS_MINY, TS_MAXY, 0, tft.width());
        y = map(p.x, TS_MAXX, TS_MINX, 0, tft.height());
        break;
      case 2:
        x = map(p.x, TS_MAXX, TS_MINX, 0, tft.width());
        y = map(p.y, TS_MAXY, TS_MINY, 0, tft.height());
        break;
      case 3:
        // p.x, p.y reversed //
        x = map(p.y, TS_MAXY, TS_MINY, 0, tft.width());
        y = map(p.x, TS_MINX, TS_MAXX, 0, tft.height());
        break;

    }

    if (ts.touched() && millis() - lastTouchUpdate >= 50) {
        lastTouchUpdate = millis();
        for (uint8_t b = 0; b < row * col; b++) {
            if (btn[b].contains(x, y)) {
                btn[b].press(true);
                btn[b].drawButton(true);
                currentHit = b;
            } else if (btn[b].contains(x, y) == false) {
                btn[b].press(false);
                if (b == lastHit) {
                    btn[b].drawButton(false);
                }
            } else {
                return;
            }
        }
        lastHit = currentHit;
    }
}

  if (btn[0].contains(x, y)) {
    start = 1;
    steps = 14;
    staples = 4;
  }
  else if (btn[1].contains(x, y)) {
    start = 1;
    steps = 16;
    staples = 5;
  }
  else if (btn[2].contains(x, y)) {
    start = 1;
    steps = 8;
    staples = 6;

  } else if (btn[3].contains(x, y)) {
    start = 1;
    steps = 3;
    staples = 8;
  }
  else if (btn[4].contains(x, y)) {
    start = 1;
    steps = 9;
    staples = 11;
  }
  else if (btn[5].contains(x, y)) {
    start = 1;
    steps = 7;
    staples = 13;
  }
  else if (btn[6].contains(x, y)) {
    start = 2;
    loadwire();
  }
  else if (btn[7].contains(x, y)) {
    start = 1;
    staples = 1;
  }
}