Arduino Debounce button logic Controlling Servos

I am trying to use a push button with debounce logic to switch between 2 modes in my program. I have an initial button press that begins the cycle between these 2 modes. "mode 2" works fine with the flex sensors controlling the servo. However, in "mode 1" I am trying to think of a way where the servos will cycle through a series of rotations until the button is pressed to go back to "mode 2". The issue I was having was that when I add a long delay to allow the servo to rotate the button toggle won't be detected during this wait time. What logic can I implement that allows the 5 servos to rotate to a series of preset values and keep cycling through this until the button is pressed to switch the mode. (Even just a basic layout that I can customize later)

Here is the transmitter code:

#include <RF24.h>
#include <RF24_config.h>
#include <nRF24L01.h>
#include <printf.h>


// Initialize Radio Data
RF24 radio(7, 8);  // CE, CSN

const byte address[6] = "00001";

int transmit[5];

// Initialize Flex Sensor Data
const int flexPin1 = A1;
const int flexPin2 = A2;
const int flexPin3 = A3;
const int flexPin4 = A4;
const int flexPin5 = A5;

int value1, value2, value3, value4, value5;
int flex1, flex2, flex3, flex4, flex5;


// Initialize Button Data
const int buttonPin = 9;  // Pin connected to the push button


int buttonState;
int lastButtonState = LOW;
int modeSelect = 1;  // Current mode: 0 for mode 1, 1 for mode 2

bool debounceActive = false;       // Flag to track if button debouncing is active
bool initialButtonChange = false;  // Flag to track the initial button state change

unsigned long lastModeChange = 0;   // Timestamp of the last mode change
unsigned long debounceDelay = 100;  // Debounce delay in milliseconds


void setup() {

  Serial.begin(9600);
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
  pinMode(buttonPin, INPUT);
  Serial.println("Start Test");
}


void loop() {
  int buttonState = digitalRead(buttonPin);  // Read the current state of the button

  if (!initialButtonChange) {
    if (buttonState != lastButtonState) {
      lastButtonState = buttonState;
      if (buttonState == HIGH) {
        initialButtonChange = true;  // Button state changed for the first time, set initialButtonChange to true
      }
    }
    return;  // Skip the rest of the loop until initial button change occurs
  }

  if (buttonState != lastButtonState) {
    lastButtonState = buttonState;
    if (buttonState == HIGH && debounceActive == false) {
      changeMode();               // Button pressed, change the mode
      lastModeChange = millis();  // Record the timestamp of mode change
      debounceActive = true;      // Activate button debouncing
    }
  }

  if ((millis() - lastModeChange) > debounceDelay) {
    debounceActive = false;  // Disable button debouncing after debounceDelay
  }

  if (modeSelect == 0) {
    mode1();  // Execute mode 1 functionality
  } else {
    mode2();  // Execute mode 2 functionality
  }
}


void changeMode() {
  if (modeSelect == 0) {
    modeSelect = 1;  // Toggle mode from 0 to 1
  } else {
    modeSelect = 0;  // Toggle mode from 1 to 0
  }
}

void mode1() {  // Preset Gesture Mode 

  transmit[0] = 60;
  radio.write(&transmit, sizeof(transmit));  
  delay(10);
  Serial.println("test");


}


void mode2() {  // Glove Control Mode

  value1 = analogRead(flexPin1);
  value2 = analogRead(flexPin2);
  value3 = analogRead(flexPin3);
  value4 = analogRead(flexPin4);
  value5 = analogRead(flexPin5);

  flex1 = map(value1, 762, 861, 0, 180); // PINKY **
  flex1 = constrain(flex1, 0, 180); 

  flex2 = map(value2, 754, 836, 0, 180); // RING **
  flex2 = constrain(flex2, 0, 180);

  flex3 = map(value3, 806, 895, 0, 180); // MIDDLE **
  flex3 = constrain(flex3, 0, 180);

  flex4 = map(value4, 784, 876, 0, 180); // INDEX **
  flex4 = constrain(flex4, 0, 180);  

  flex5 = map(value5, 807, 905, 0, 180); // THUMB **
  flex5 = constrain(flex5, 0, 180);  

  transmit[0] = flex1;
  transmit[1] = flex2;
  transmit[2] = flex3;
  transmit[3] = flex4;
  transmit[4] = flex5;

  radio.write(&transmit, sizeof(transmit));  

  Serial.println("PINKY: " + String(transmit[0]));
  Serial.println("RING: " + String(transmit[1]));
  Serial.println("MIDDLE: " + String(transmit[2]));
  Serial.println("INDEX: " + String(transmit[3]));
  Serial.println("THUMB: " + String(transmit[4]));

  delay(10);

}

Here is thee receiver code:

  #include <RF24.h>
  #include <RF24_config.h>
  #include <nRF24L01.h>
  #include <printf.h>
  #include <Servo.h>
  #include <string.h>

  RF24 radio(7, 8); // CE, CSN
  const byte address[6] = "00001";  

  const int servoPin1 = 3;
  const int servoPin2 = 5;
  const int servoPin3 = 6;
  const int servoPin4 = 9;
  const int servoPin5 = 10;

  Servo myServo1;
  Servo myServo2;
  Servo myServo3;
  Servo myServo4;
  Servo myServo5;

  //define variable to receive value
  int data[5];

  void setup() {
    Serial.begin(9600);
    Serial.println("test");
    radio.begin();
    radio.openReadingPipe(0, address);
    radio.setPALevel(RF24_PA_MIN);
    radio.startListening();
    myServo1.attach(servoPin1);
    myServo2.attach(servoPin2);
    myServo3.attach(servoPin3);
    myServo4.attach(servoPin4);
    myServo5.attach(servoPin5);
  }

  void loop() {

    if (radio.available()) 
    {
    while (radio.available()){

      radio.read(&data, sizeof(data));

      Serial.write((byte*)&data, sizeof(data)); // Send motor data to Arduino Uno via Serial Communication

      myServo1.write(data[0]);
      myServo2.write(data[1]);
      myServo3.write(data[2]);
      myServo4.write(data[3]);
      myServo5.write(data[4]);

      Serial.println("MOTOR PINKY: " + String(data[0]));
      Serial.println("MOTOR RING: " + String(data[1]));
      Serial.println("MOTOR MIDDLE: " + String(data[2]));
      Serial.println("MOTOR INDEX: " + String(data[3]));      
      Serial.println("MOTOR THUMB: " + String(data[4]));
      delay(10);
    }

    }      
  }

If you need any more information or if I am completely overlooking something with this question please let me know.

Thanks!

Hello cbauma05

Check and try this small button manager for you project:

//https://forum.arduino.cc/t/arduino-debounce-button-logic-controlling-servos/1154479
//https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
#define ProjectName "Small Button Manager"
// make names
enum Mode {One, Two};
// make variables
const char* modes[] {"ModeOne", "ModeTwo"};
uint8_t mode = Two;
// make structures
struct MODESELECTOR
{
  uint8_t buttonPin;
  uint8_t stateOld;
  uint8_t mode;
  uint32_t debounceMillis;
  uint32_t intervalMillis;
}
modeSelector {A0, LOW, mode, 0, 20};
// make support
void heartBeat(int LedPin, uint32_t currentMillis)
{
  static bool setUp = false;
  if (!setUp) pinMode (LedPin, OUTPUT), setUp = !setUp;
  digitalWrite(LedPin, (currentMillis / 500) % 2);
}
// make application
void setup()
{
  Serial.begin(115200);
  Serial.println(ProjectName);
  pinMode(modeSelector.buttonPin, INPUT_PULLUP);
  Serial.print("start mode selected "), Serial.println(modes[modeSelector.mode]);
}
void loop()
{
  uint32_t currentMillis = millis();
  heartBeat(LED_BUILTIN, currentMillis);
  if (currentMillis - modeSelector.debounceMillis >= modeSelector.intervalMillis)
  {
    modeSelector.debounceMillis = currentMillis;
    uint8_t stateNew = digitalRead(modeSelector.buttonPin) ? LOW : HIGH;
    if (modeSelector.stateOld != stateNew)
    {
      modeSelector.stateOld = stateNew;
      if (stateNew == HIGH)
      {
        modeSelector.mode = modeSelector.mode ? LOW : HIGH;
        Serial.print("mode selected "), Serial.println(modes[modeSelector.mode]);
      }
    }
  }
}

Have a nice day and enjoy coding in C++.

Thank you for the suggestion!

In terms of sending the Servo value data within "mode1", what is the best way to radio.write this information so the servos have time to get to their locations but still allow the button press to be detected and switch back to the other mode? (for example have each of the servos rotate 45 degrees, then back to 0 - one at a time cycling within that modes loop)

And where would I implement each mode's functionality in the code you provided?

Thanks!

The current mode is stored in am variable.

Okay, and did you have an idea for the servo values being transmitted in parallel with the button being pressed? The idea is that the receiver code will stay the same regardless of what mode the transmitter code is in. So in "mode 1" I am trying to send values to the servos to get them cycling through pre-programmed positions until the button is pressed to switch back to flex sensor control in "mode 2".

How do you think I can do this?

You might design and code the button manger into a function call like:

int buttonMan(currentMillis);

This function will return the current selected mode of operation.

Okay, thank you.

In terms of the "mode 2" logic in my original transmitter code, how could we send a series of servo values to sets of predetermined locations? How can we allow the servos to cycle through their motions without using delay() because the button won't be detected while the delay is active? What kind of logic can we use so it waits for the servos to get to their set values before moving on to the next set of values?

Please try the below debouncing,

Change to,

bool buttonState;
bool state = LOW;
bool modeSelect = HIGH;  // Current mode: LOW for mode 1, HIGH for mode 2

And,

Change to,

void loop() {
  bool lastButtonState = buttonState;
  buttonState = digitalRead(buttonPin);
  if (buttonState != lastButtonState) {
    lastModeChange = millis();
  }
  if (buttonState != state) {
    if ((millis() - lastModeChange) > debounceDelay) {
      state = buttonState;
      if (state) modeSelect = !modeSelect;
    }
  }

  if (modeSelect) {
    mode2();  // Execute mode 2 functionality    
  } else {
    mode1();  // Execute mode 1 functionality
  }
}

Your Small Button Manage will not work with bounce/noise longer than your debounceMillis debounce period.

Please take a look at the Built-in examples. It works well with any bounce/noise.

And please take a look at both your Small Button Manage and the Arduino Built-in examples in Wokwi Simulator for your comparison.

Bounce Generator: 100 ms

button debounce
look this over

const int buttonPin = 9;

int buttonState;

void loop () {
    int but = digitalRead (buttonPin);
    if (buttonState != but)  {
        buttonState = but;
        delay (20);             // debounce

        if (LOW == but)
            Serial.println ("pressed");
        else
            Serial.println ("released");
    }
}

void setup () {
    Serial.begin (9600);

    pinMode (buttonPin, INPUT_PULLUP);
    buttonState = digitalRead (buttonState);
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.