328ProMini x2 code issues

Ciao all,

I am trying to add debouncing to my code which works accept for the button presses send multiple chars. when depressed when it should be only one per press instance. I think I have some redundancy here and some just plain messed up code. I suppose this might also be the reason I can com. between Ards, but, not with XBs between. :~

Any help is appreciated.

Remote

const int buttonPin = 7;   // Switch connected to digital pin 3
const int buttonPin1 = 8;   // Switch connected to digital pin 4
const int ledPin = 11;   // Switch connected to digital pin 3
const int ledPin1 = 12;   // Switch connected to digital pin 4

char up = ' pu ';  // ascii motor up
char down = ' pd ';  // ascii motor down


int buttonState = 0; // variable for reading pushbutton1 status
int buttonState1 = 0; // variable for reading pushbutton2 status
int lastButtonState = LOW;   // the previous reading from the input pin
int lastButtonState1 = LOW;   // the previous reading from the input pin
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers
long lastDebounceTime1 = 0;  // the last time the output pin was toggled
long debounceDelay1 = 50;    // the debounce time; increase if the output flickers

char nextChar;


void setup() 

{
   

  Serial.begin(9600);   // Initialize the Serial port:

    
  pinMode(buttonPin, INPUT);    // sets digital pin as input to read switch
  pinMode(buttonPin1, INPUT);    // sets digital pin as input to read switch
  pinMode(ledPin, OUTPUT);
  pinMode(ledPin1, OUTPUT);
    
}

void loop() 

{
       buttonState = digitalRead(buttonPin);   // read the state of the pushbutton value:
       buttonState1 = digitalRead(buttonPin1);   // read the state of the pushbutton value:
       {
       int reading = digitalRead(buttonPin);  // check to see if you just pressed the button 
  if (reading != lastButtonState) 
  {
    lastDebounceTime = millis();  // reset the debouncing timer  
       }
       if ((millis() - lastDebounceTime) > debounceDelay) 
       {
    buttonState = reading;    
       }
  if (buttonState == HIGH)   // check if the button is pressed
       {
         Serial.print("1u");
         digitalWrite(ledPin, buttonState);
         delay(100);
         digitalWrite(ledPin, buttonState);
       } 
     else if (buttonState1 == HIGH)   // check if the button is pressed
       {  
         
         Serial.print("1d");
         digitalWrite(ledPin1, buttonState1);
         delay(100);
         digitalWrite(ledPin1, buttonState1);
       }
  if (Serial.available())  
       {
       nextChar = Serial.read();  // read one character
       if (nextChar == up)  // check for 'Up'
       {
         digitalWrite(ledPin, buttonState);
         delay(1000);
         digitalWrite(ledPin, buttonState);
       } 
       else if (nextChar == down)  // check for 'Dn'
       {  
         digitalWrite(ledPin1, buttonState1);
         delay(1000);
         digitalWrite(ledPin1, buttonState1);
       }
       
    }
  }
}

Base

#include <Stepper.h>

#define motorSteps 200     // change this depending on the number of 
                           // steps per revolution of your motor
#define motorPin1 9
#define motorPin2 10
#define motorPin3 11
#define motorPin4 12
const int buttonPin = 7;   // Switch connected to digital pin 3
const int buttonPin1 = 8;   // Switch connected to digital pin 4
const int ledPin = 5;   // Switch connected to digital pin 3
const int ledPin1 = 6;   // Switch connected to digital pin 4

int inByte = 0;         // incoming serial byte

char up = '1u';  // ascii motor up
char down = '1d';  // ascii motor down

int myStepperState;  // variables will change:
int buttonState = 0; // variable for reading pushbutton1 status
int buttonState1 = 0; // variable for reading pushbutton2 status
int lastButtonState = LOW;   // the previous reading from the input pin
int lastButtonState1 = LOW;   // the previous reading from the input pin
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers
long lastDebounceTime1 = 0;  // the last time the output pin was toggled
long debounceDelay1 = 50;    // the debounce time; increase if the output flickers

char nextChar;

// initialize of the Stepper library:
Stepper myStepper(motorSteps, motorPin1,motorPin2,motorPin3,motorPin4); 

void setup() 

{
   myStepper.setSpeed(80);   // set the motor speed at 60 RPMS:
 

  Serial.begin(9600);   // Initialize the Serial port:
    
  pinMode(buttonPin, INPUT);    // sets digital pin as input to read switch
  pinMode(buttonPin1, INPUT);    // sets digital pin as input to read switch
  pinMode(ledPin, OUTPUT);
  pinMode(ledPin1, OUTPUT);
  
}

void loop() 

{
       buttonState = digitalRead(buttonPin);   // read the state of the pushbutton value:
       buttonState1 = digitalRead(buttonPin1);   // read the state of the pushbutton value:
     {
       int reading = digitalRead(buttonPin);
  if (reading != lastButtonState) 
       {
    lastDebounceTime = millis();      
       }
  if (buttonState == HIGH)   // check if the button is pressed
       {
         myStepper.step(300);  // Step forward 300 steps:
         Serial.print(" pu ");
         digitalWrite(ledPin, HIGH);
         delay(100);
         digitalWrite(ledPin, LOW);
       } 
       else if (buttonState1 == HIGH)   // check if the button is pressed
       {  
         myStepper.step(-300);  // Step backward 100 steps:
         Serial.print(" pd ");
         digitalWrite(ledPin1, HIGH);
         delay(100);
         digitalWrite(ledPin1, LOW);
    }
  if (Serial.available())     
    {
     int inByte = Serial.read();  // read just one character
     if (inByte == up)  // check for '1u'
       {
         myStepper.step(300);  // Step forward 300 steps:
         Serial.print(" pu ");
         digitalWrite(ledPin, HIGH);
         delay(1000);
         digitalWrite(ledPin, LOW);
       } 
     else if (inByte == down)  // check for '1d'
       {  
         myStepper.step(-300);  // Step backward 100 steps:
         Serial.print(" pd ");
         digitalWrite(ledPin1, HIGH);
         delay(1000);
         digitalWrite(ledPin1, LOW);
       }
       
    }
  }
}

Anybody have any ideas?

Graynomad, what do you think?

Anybody? Bueller...Bueller.

The correct data-type is "unsigned long" not "long".

Would you prefer a ready-to-use solution or would you prefer to learn how to de-bounce a button?

Thank you. I have been trying to "get it" from a book, but, debouncing is eluding me. Especially with my code already working. Adding it is cumbersome.

So, I think learning the when's, why's and how's is best.

I've used two techniques both of which seem to work well...

  1. Fire on click ... A click is immediately handled and a timer is started. While the timer runs, the digital input is ignored. This method works well if you are only interested in clicks. This method does not work well for handling "holds" or releases.

  2. State machine ... There are three states: up, down, limbo. The digital input is read in setup to determine the initial state (up or down; we'll start in up). When the digital input changes, we enter the limbo state and start a timer. If the digital input returns to the previous value, we return the previous state (up) and stop the timer. If the timer expires, we transition to the new state (down). Going the other direction works the same way. This method works well for especially "noisy" digital inputs (buttons usually aren't). However, this method really requires object-oriented programming.

It's about bedtime. I'll try to post some code for #1 tomorrow. Hopefully someone else will post their code between now and then to get you started.

Morning @Coding Badly,

I think the FoC would do fine in my code.

It simply says State is changed and a small delay executed upon button press, no? This keeps the small button fluctuations created by a human finger (flutter) from being used in the code. Right?

But, State machine is not clear to me as to why it is so different.

And how would I incorporate either into my code? Would I need to rework the whole thing or some or very little?

Thx.

Days later Coding Badly has time to reply...

Utoto: It simply says State is changed and a small delay executed upon button press, no?

Yes. Ideally, the delay should be implemented in the "Blink Without Delay" fashion so your Sketch is not blocked. But, in the simplest case, you just put a delay after detecting the button click.

This keeps the small button fluctuations created by a human finger (flutter) from being used in the code. Right?

Right. (only the fluctuations are usually from "bouncy" switch contacts not from a finger)

But, State machine is not clear to me as to why it is so different.

It ensures both "click" and "release" can be cleanly detected. You would use the State machine if you need to handle both directions.

And how would I incorporate either into my code? Would I need to rework the whole thing or some or very little?

I'll make a series of code examples that hopefully will illustrate how it works. You can decide if you need more help or if you can rework your Sketch on your own.

Fire on click WITH NO DEBOUNCE. This shows the how to detect a digital input state change and execute code for a HIGH to LOW change (using pullup resistor)...

const uint8_t PushbuttonPin = 4;

const uint8_t LEDPin = 3;

int PreviousState;

void setup( void )
{
  // While the following is technically not necessary but it clearly conveys our intention.
  pinMode( PushbuttonPin, INPUT );
  // Because I'm too lazy to dig out a resistor, I'll use the internal pullup resistor.
  digitalWrite( PushbuttonPin, HIGH );
  
  // The LED is solely to turn this into a self contained example.
  pinMode( LEDPin, OUTPUT );
  
  // Read the initial state of the digital input
  PreviousState = digitalRead( PushbuttonPin );
}

void loop( void )
{
  int CurrentState;
  boolean PushbuttonClicked;

  // Read the current state  
  CurrentState = digitalRead( PushbuttonPin );
  
  // Assume the pushbutton was not clicked
  PushbuttonClicked = false;
  
  // Did the digital input change from the last time we read it
  if ( CurrentState != PreviousState )
  {
    // Because we're using a pullup, LOW means the pushbutton is pressed
    if ( CurrentState == LOW )
    {
      // The input state changed and the pushbutton is pressed which can only mean the pushbutton was just clicked
      PushbuttonClicked = true;
    }
    // The current state of the digital input becomes the previous state
    PreviousState = CurrentState;
  }
  
  if ( PushbuttonClicked )
  {
    // Handle the click here.
    digitalWrite( LEDPin, ! digitalRead( LEDPin ) );
  }
}

Fire on click WITH DEBOUNCE. To see the effect, change DebounceMillis to 1000...

static const uint8_t PushbuttonPin = 4;

static const uint8_t LEDPin = 3;

int PreviousState;
unsigned long PreviousMillis;

static const unsigned long DebounceMillis = 50;

void setup( void )
{
  // While the following is technically not necessary but it clearly conveys our intention.
  pinMode( PushbuttonPin, INPUT );
  // Because I'm too lazy to dig out a resistor, I'll use the internal pullup resistor.
  digitalWrite( PushbuttonPin, HIGH );
  
  // The LED is solely to turn this into a self contained example.
  pinMode( LEDPin, OUTPUT );
  
  // Read the initial state of the digital input
  PreviousState = digitalRead( PushbuttonPin );
  
  // Force the time condition to be true for the first button click
  PreviousMillis = 0x80000000;
}

void loop( void )
{
  int CurrentState;
  boolean PushbuttonClicked;
  unsigned long CurrentMillis;

  // Read the current state  
  CurrentState = digitalRead( PushbuttonPin );
  
  // Assume the pushbutton was not clicked
  PushbuttonClicked = false;
  
  // Record the current time
  CurrentMillis = millis();
  
  // Did the digital input change from the last time we read it
  if ( (CurrentState != PreviousState) && (CurrentMillis - PreviousMillis >= DebounceMillis) )
  {
    // Because we're using a pullup, LOW means the pushbutton is pressed
    if ( CurrentState == LOW )
    {
      // The input state changed and the pushbutton is pressed which can only mean the pushbutton was just clicked
      PushbuttonClicked = true;
      
      // Record the time of the click.  Ignore changes until the debounce time has elapsed.
      PreviousMillis = millis();
    }
    // The current state of the digital input becomes the previous state
    PreviousState = CurrentState;
  }
  
  if ( PushbuttonClicked )
  {
    // Handle the click here.
    digitalWrite( LEDPin, ! digitalRead( LEDPin ) );
  }
}

Fire on click with debounce restructure as a class. Simply add more CodingBadlyDebouncer instances for more buttons.

class CodingBadlyDebouncer
{
public:

  void begin( uint8_t pin, bool useInternalPullup );

  void update( void );

  bool wasClicked( void )
  {
    return( _wasClicked );
  }
  
  static const unsigned long DebounceMillis = 50;
  
private:

  uint8_t _pin;
  bool _pullup;
  bool _previousState;
  unsigned long _previousMillis;
  bool _wasClicked;
};

void CodingBadlyDebouncer::begin( uint8_t pin, bool useInternalPullup )
{
  _pin = pin;
  
  // While the following is technically not necessary but it clearly conveys our intention.
  pinMode( pin, INPUT );
  
  // If requested, enabled the internal pullup
  if ( useInternalPullup )
  {
    digitalWrite( pin, HIGH );
    _pullup = true;
  }
  else
  {
    _pullup = false;
  }

  // Read the initial state of the digital input.  The "!= _pullup" condition makes _previousState HIGH if the button is down
  _previousState = digitalRead( pin ) != _pullup;

  // Force the time condition to be true for the first button click
  _previousMillis = 0x80000000;

  // Ensure no misfires
  _wasClicked = false;
}

void CodingBadlyDebouncer::update( void )
{
  int CurrentState;
  unsigned long CurrentMillis;
  
  // Assume the pushbutton was not clicked
  _wasClicked = false;
  
  // Read the current state.  The "!= _pullup" condition makes CurrentState HIGH if the button is down
  CurrentState = digitalRead( _pin ) != _pullup;
  
  // Record the current time
  CurrentMillis = millis();
  
  // Did the digital input change from the last time we read it and the debounce time has elapsed
  if ( (CurrentState != _previousState) && (CurrentMillis - _previousMillis >= DebounceMillis) )
  {
    // The "!= _pullup" condition above makes it so CurrentState is HIGH when the button is pressed
    if ( CurrentState == HIGH )
    {
      // The input state changed and the pushbutton is pressed which can only mean the pushbutton was just clicked
      _wasClicked = true;
      
      // Record the time of the click.  Ignore changes until the debounce time has elapsed.
      _previousMillis = millis();
    }
    // The current state of the digital input becomes the previous state
    _previousState = CurrentState;
  }
}


CodingBadlyDebouncer pushbutton;

static const uint8_t PushbuttonPin = 4;

static const uint8_t LEDPin = 3;

void setup( void )
{
  pushbutton.begin( PushbuttonPin, true );

  // The LED is solely to turn this into a self contained example.
  pinMode( LEDPin, OUTPUT );
}

void loop( void )
{
  pushbutton.update();
  
  if ( pushbutton.wasClicked() )
  {
    // Handle the click here.
    digitalWrite( LEDPin, ! digitalRead( LEDPin ) );
  }
}

I don’t think these lines do what you expect:

char up = ' pu ';  // ascii motor up
char down = ' pd ';  // ascii motor down

The type ‘char’ holds a single character in a single byte. If I recall correctly from my years of C and C++ programming, the construct ’ pu ’ is a four byte constant. When you store a four-byte value in a one-byte variable it gets truncated. I think both will end up being ’ ’ (space). If you want named character strings you probably want something like:

char up[] = " pu ";  // ascii motor up
char down[] = " pd ";  // ascii motor down
 or
#define up " pu "
#define down " pd "