Debounce function

Hello,
I am missing something in basic code below. You might see it.
It seems something is wrong in the function int debounceIN0(). The variable ton0_sb should detect high to low edge transition on input IN0
and shall be set forever to 0x02 if button is pressed.

Expected output on uart:
ton0_sb = 2
lastFallingEdgeTime0 = like 123456

Real output on uart:
ton0_sb = 0
lastFallingEdgeTime0 = 0

// IO assigment:
// there are two inputs IN0, IN1 where are buttons connected, button active is LOW
// there are two outputs OUT1, OUT2 which are connected to two relays, assuming OUT1 runs motor forward, OUT2 runs motor backward
//
// the aim of logic2()
// + aim of this is to ensure the hardware wiring is correct
// + IN0 press runs motor forward, it tests falling edge happend
// + IN1 press runs motor backward, it tests falling edge happend
//
// target arduino mega attiny core, chip attiny1614, shall work on Uno R3 as well
//
#define IN0 (0)
#define IN1 (1)
#define OUT0 (2)
#define OUT1 (3)

// datatype
enum mem_
{
    BOTH_OFF = 0x00,
    OUT0_ON = 0x01,
    OUT1_ON = 0x02
};

enum ton_sb
{
    OFF = 0x00,
    RUN = 0x02,
    CPL = 0x04
};

void setup_io(void);
int debounceIN0(void);
int debounceIN1(void);
void logic2(void);
void dbg_msg(void);

// Duration for which OUT0 and OUT1 remain HIGH
// const unsigned long onDuration = 1000; // 1 second

// Debounce time in milliseconds
const unsigned long debounceDelay = 20;

// Variables for edge detection, debouncing, and timing
unsigned long lastFallingEdgeTime0 = 0;
unsigned long lastFallingEdgeTime1 = 0;
unsigned long lastDebounceTime0 = 0;
unsigned long lastDebounceTime1 = 0;
int lastButtonState0 = HIGH;  // Start assuming button is not pressed
int lastButtonState1 = HIGH;  // Start assuming button is not pressed
int buttonState0;
int buttonState1;
enum ton_sb ton0_sb; // timer0 status byte
enum ton_sb ton1_sb; // timer1 status byte

void setup()
{
    setup_io();
    Serial.begin(19200);
}

void loop()
{
    // Call debounce functions
    debounceIN0();
    debounceIN1();

    /* logic1(); will be implemented later */
    logic2();
    dbg_msg();
}

void dbg_msg(void)
{
    static uint16_t dbg_time, dbg_last_time;

    // prints every second variable under investigation
    dbg_time = millis();
    if (dbg_time - dbg_last_time < 1000)
    {
    }
    else
    {
        dbg_last_time = dbg_time;
        Serial.print("ton0_sb = ");
        Serial.println(ton0_sb);
        Serial.print("lastFallingEdgeTime0 = ");
        Serial.println(lastFallingEdgeTime0);
    }
}

// this shall set OUT0 output forever HIGH if ton0_sb is set to RUN
void logic2(void)
{
    enum mem_ mem = BOTH_OFF;

    // network:
    // if (buttonState0==LOW) <-- this works if uncommented
    if (ton0_sb==RUN) /* <-- this variable seems that is always 0 */
    {
        mem = OUT0_ON;
    }

    // network:
    // if (buttonState1==LOW) <-- this works if uncommented
    if (ton1_sb==RUN) /* <-- this variable seems that is always 0 */
    {
        mem = OUT1_ON;
    }

    // network: finally write to output
    switch (mem)
    {
    case OUT0_ON:
        digitalWrite(OUT0, HIGH);
        break;

    case OUT1_ON:
        digitalWrite(OUT1, HIGH);
        break;

    default:
        digitalWrite(OUT0, LOW);
        digitalWrite(OUT1, LOW);
    }
}

// Debounce function for IN0
int debounceIN0()
{
    int reading = digitalRead(IN0);

    if (reading != lastButtonState0)
    {
        lastDebounceTime0 = millis();
    }

    if ((millis() - lastDebounceTime0) > debounceDelay)
    {
        if (reading != buttonState0)
        {
            buttonState0 = reading;
            if (buttonState0 == LOW && lastButtonState0 == HIGH)
            {
                if (ton0_sb != RUN)
                {
                    lastFallingEdgeTime0 = millis();
                    ton0_sb = RUN;
                }
            }
        }
    }

    lastButtonState0 = reading;
    return buttonState0;
}
// Debounce function for IN1
int debounceIN1()
{
    int reading = digitalRead(IN1);

    if (reading != lastButtonState1)
    {
        lastDebounceTime1 = millis();
    }

    if ((millis() - lastDebounceTime1) > debounceDelay)
    {
        if (reading != buttonState1)
        {
            buttonState1 = reading;
            if (buttonState1 == LOW && lastButtonState1 == HIGH)
            {
                if (ton1_sb != RUN)
                {
                    lastFallingEdgeTime1 = millis();
                    ton1_sb = RUN;
                }
            }
        }
    }

    lastButtonState1 = reading;
    return buttonState1;
}

void setup_io(void)
{
    // Initialize IN0 and IN1 as inputs with pull-up resistors
    pinMode(IN0, INPUT_PULLUP);
    pinMode(IN1, INPUT_PULLUP);

    // Initialize OUT0 and OUT1 as outputs
    pinMode(OUT0, OUTPUT);
    pinMode(OUT1, OUTPUT);

    // Ensure outputs start LOW
    digitalWrite(OUT0, LOW);
    digitalWrite(OUT1, LOW);
}
// EOF


how can that final test, if (reading != buttonState0) be true within if ((millis() - lastDebounceTime0) > debounceDelay) if lastDebounceTime0 = millis(); is always set when if (reading != lastButtonState0)

look this over

int debounceIN0 ()
{
    if ((millis() - lastDebounceTime0) < debounceDelay)
        return buttonState0;

    int reading = digitalRead (IN0);

    if (lastButtonState0 != reading)  {
        buttonState0         = reading;
        lastFallingEdgeTime0 = millis ();

        if (buttonState0 == LOW)
            ton0_sb = RUN;
    }

    return buttonState0;
}
1 Like

So, lastFallingEdgeTime0 is never set....

I think it is because lastButtonState0 is set equal to reading at the end of the routine unconditionally, so after the button settles after debounceDelay, the if (buttonState0 == LOW && lastButtonState0 == HIGH) is never true.

I tend to put lastButtonState0 = reading inside of the rate-limiting if, and thinking of it as the last stable button state.

The code looks overly complicated, and I think you could stand to lose a level of reading->buttonState0 -> lastButtonState0.

1 Like

look this over ... simpler

// IO assigment:
// there are two inputs IN0, IN1 where are buttons connected, button active is LOW
// there are two outputs OUT1, OUT2 which are connected to two relays, assuming OUT1 runs motor forward, OUT2 runs motor backward
//
// the aim of logic2 ()
// + aim of this is to ensure the hardware wiring is correct
// + IN0 press runs motor forward, it tests falling edge happend
// + IN1 press runs motor backward, it tests falling edge happend
//
// target arduino mega attiny core, chip attiny1614, shall work on Uno R3 as well

enum { Off = LOW,  On = HIGH };

struct Io  {
    const byte  PinInp;
    const byte  PinOut;
    const char *label;

    byte          outState;
    byte          inpState;
    unsigned long msec;
}
ios [] = {
    { A1, 10, "IO1" },
    { A2, 11, "IO2" },
};
const int Nio = sizeof(ios)/sizeof(Io);


const unsigned long MsecDebounce = 30;
unsigned long msec;

// -----------------------------------------------------------------------------
bool
isPressed (
    int n)
{
    if ((millis() - ios [n].msec) < MsecDebounce)
        return false;

    byte inp = digitalRead (ios [n].PinInp);

    if (ios [n].inpState != inp)  {
        ios [n].inpState  = inp;
        ios [n].msec      = msec;

        if (inp == LOW)
            return true;
    }

    return false;
}

// -----------------------------------------------------------------------------
void loop ()
{
    for (int n = 0; n < Nio; n++)  {
        if (isPressed (n))  {
            Serial.print   (ios [n].label);

            if (Off == ios [n].outState)  {
                ios [n].outState = On;
                Serial.println (" On");
            }
            else  {
                ios [n].outState = Off;
                Serial.println (" Off");
            }

            digitalWrite (ios [n].PinOut, ios [n].outState);


        }
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (19200);

    for (int n = 0; n < Nio; n++)  {
        ios [n].outState = Off;
        digitalWrite (ios [n].PinOut, ios [n].outState); 
        pinMode      (ios [n].PinOut, OUTPUT);

        pinMode      (ios [n].PinInp, INPUT_PULLUP);
        ios [n].inpState = digitalRead (ios [n].PinInp);
    }
}
1 Like

is this an accurate (complete) description of what is needed

  • both outputs should never be on, the motor can't be run forward and backwards at the same time and before turning one output On the other should be turned Off, possibly with a delay in between
  • there's no need to debounce the buttons because there is a separate button for each case

... but i see no description of how to turn the motor Off

1 Like

Hello,
thank you for finding the bug in the debunce function and sharing your solution. I used a an older snippet to debounce the input somehow. It seems it works now. Code is below.

// file: s06_hold_and_run.ino
// IO assigment:
// there are two inputs IN0, IN1 where are buttons connected, button active is LOW
// there are two outputs OUT1, OUT2 which are connected to two relays, assuming OUT1 runs motor forward, OUT2 runs motor backward
//
// the aim of logic1()
// + aim of this is to ensure the hardware wiring is correct
// + IN0 press runs motor forward
// + IN1 press runs motor backward
//
// target arduino mega attiny core, chip attiny1614, shall work on Uno R3 as well
//
#define IN0 (0)
#define IN1 (1)
#define OUT0 (2)
#define OUT1 (3)

// datatype
enum mem_
{
    BOTH_OFF = 0x00,
    OUT0_ON = 0x01,
    OUT1_ON = 0x02
};

void setup_io(void);
void debounce_IN0_IN1(void);
void logic1(void);
void dbg_msg(void);

// variables
uint8_t input_flag;

// code strats here
void setup()
{
    setup_io();
    Serial.begin(19200);
}

void loop()
{
    debounce_IN0_IN1();
    logic1();
    dbg_msg();
}


void logic1(void)
{
    enum mem_ mem;

    // network: hold to run
    if (input_flag & _BV(0))
    {
        mem = OUT0_ON;
    }
    else if (input_flag & _BV(1))
    {
        mem = OUT1_ON;
    }
    else
    {
        mem = BOTH_OFF;
    }

    // network: finally write to output
    switch (mem)
    {
    case OUT0_ON:
        digitalWrite(OUT0, HIGH);
        break;

    case OUT1_ON:
        digitalWrite(OUT1, HIGH);
        break;

    default:
        digitalWrite(OUT0, LOW);
        digitalWrite(OUT1, LOW);
    }
}

/* debounce based on an older C snippet */
/* result is stored in the global variable uint8_t input_flag */
void debounce_IN0_IN1(void)
{
    uint16_t time;
    static uint16_t lastTime;
    static uint8_t cnt_in0;
    static uint8_t cnt_in1;

    time = millis();
    if(time!=lastTime)
    {
        lastTime = time;

        // network: debounce IN0, delay is hardcoded 10ms
        if(digitalRead(IN0)==LOW)
        {
            if(cnt_in0<10)
            {
                cnt_in0++;
            }
            else
            {
                input_flag |= _BV(0);
            }
        }
        else
        {
            if(cnt_in0!=0)
            {
                cnt_in0--;
            }
            else
            {
                input_flag &= ~(_BV(0));
            }
        }

        // network: debounce IN1, delay is hardcoded 10ms
        if(digitalRead(IN1)==LOW)
        {
            if(cnt_in1<10)
            {
                cnt_in1++;
            }
            else
            {
                input_flag |= _BV(1);
            }
        }
        else
        {
            if(cnt_in1!=0)
            {
                cnt_in1--;
            }
            else
            {
                input_flag &= ~(_BV(1));
            }
        }
    }
}

void dbg_msg(void)
{
    uint16_t dbg_time;
    static uint16_t dbg_last_time;

    // prints every second variable under investigation
    dbg_time = millis();
    if (dbg_time - dbg_last_time < 1000)
    {
    }
    else
    {
        dbg_last_time = dbg_time;
        Serial.print("input_flag = ");
        Serial.println(input_flag);
    }
}

void setup_io(void)
{
    // Initialize IN0 and IN1 as inputs with pull-up resistors
    pinMode(IN0, INPUT_PULLUP);
    pinMode(IN1, INPUT_PULLUP);

    // Initialize OUT0 and OUT1 as outputs
    pinMode(OUT0, OUTPUT);
    pinMode(OUT1, OUTPUT);

    // Ensure outputs start LOW
    digitalWrite(OUT0, LOW);
    digitalWrite(OUT1, LOW);
}
// EOF 

My another tial. Now I detect the edge transition on input and based on this run the output for fixed time. It seems it works now. Code is below.

// file: s07_momentary_run.ino
// IO assigment:
// there are two inputs IN0, IN1 where are buttons connected, button active is LOW
// there are two outputs OUT1, OUT2 which are connected to two relays, assuming OUT1 runs motor forward, OUT2 runs motor backward
//
// the aim of logic2()
// + aim of this is to run motor for one second only
// + IN0 press runs motor forward
// + IN1 press runs motor backward
//
// target arduino mega attiny core, chip attiny1614, shall work on Uno R3 as well
//
#define IN0 (0)
#define IN1 (1)
#define OUT0 (2)
#define OUT1 (3)

// datatype
enum mem_
{
    BOTH_OFF = 0x00,
    OUT0_ON = 0x01,
    OUT1_ON = 0x02
};

enum ton_sb_
{
    OFF = 0x00,
    RUN = 0x02,
    CPL = 0x04
};

// datablock
typedef struct
{
    enum ton_sb_ sb;
    uint16_t time;
    uint16_t preset;
} ton_db;

void setup_io(void);
void debounce_IN0_IN1(void);
void logic2(void);
void ton16(ton_db * const db);  // software timer
void dbg_msg(void);

// variables
uint8_t input_flag;
uint8_t input_nail_ip;    // nail impuls
uint8_t input_help_flag;  // delayed copy of input_flag

ton_db ton1;  // making motor timing delay
uint8_t stm;  // state machine

// code starts here
void setup()
{
    setup_io();
    Serial.begin(19200);
}

void loop()
{
    debounce_IN0_IN1();

    // network: generate nail impuls on button push
    input_nail_ip = input_flag & (~input_help_flag);
    input_help_flag = input_flag;

    logic2();
    ton16(&ton1);
    dbg_msg();
}

void logic2(void)
{
    enum mem_ mem = BOTH_OFF;
    uint16_t time = millis();

    switch (stm)
    {
    case 0:
        /* let the button's edge trigger the timer */
        if ((input_nail_ip & _BV(0)) != 0)
        {
            ton1.time = time;
            ton1.preset = 1000;
            ton1.sb = RUN;
            stm = 1;
        }
        else if ((input_nail_ip & _BV(1)) != 0)
        {
            ton1.time = time;
            ton1.preset = 1000;
            ton1.sb = RUN;
            stm = 2;
        }
        break;
    case 1:
        /* output OUT0 active */
        if (ton1.sb == RUN)
        {
            mem = OUT0_ON;
        }
        else if (ton1.sb == CPL)
        {
            ton1.time = time;
            ton1.preset = 2000;
            ton1.sb = RUN;
            stm = 3;
        }
        break;
    case 2:
        /* output OUT1 active */
        if (ton1.sb == RUN)
        {
            mem = OUT1_ON;
        }
        else if (ton1.sb == CPL)
        {
            ton1.time = time;
            ton1.preset = 2000;
            ton1.sb = RUN;
            stm = 3;
        }
        break;
    case 3:
        /* little dead-time to let the motor cool down before next run */
        if (ton1.sb == CPL)
        {
            stm = 4;
        }
        break;

    default:
        ton1.sb = OFF;
        stm = 0;
    }

    // network: finally write to output
    switch (mem)
    {
    case OUT0_ON:
        digitalWrite(OUT0, HIGH);
        break;

    case OUT1_ON:
        digitalWrite(OUT1, HIGH);
        break;

    default:
        digitalWrite(OUT0, LOW);
        digitalWrite(OUT1, LOW);
    }
}

/* debounce based on an older C snippet */
/* result is stored in the global variable uint8_t input_flag */
void debounce_IN0_IN1(void)
{
    uint16_t time;
    static uint16_t lastTime;
    static uint8_t cnt_in0;
    static uint8_t cnt_in1;

    time = millis();
    if (time != lastTime)
    {
        lastTime = time;

        // network: debounce IN0, delay is hardcoded 10ms
        if (digitalRead(IN0) == LOW)
        {
            if (cnt_in0 < 10)
            {
                cnt_in0++;
            }
            else
            {
                input_flag |= _BV(0);
            }
        }
        else
        {
            if (cnt_in0 != 0)
            {
                cnt_in0--;
            }
            else
            {
                input_flag &= ~(_BV(0));
            }
        }

        // network: debounce IN1, delay is hardcoded 10ms
        if (digitalRead(IN1) == LOW)
        {
            if (cnt_in1 < 10)
            {
                cnt_in1++;
            }
            else
            {
                input_flag |= _BV(1);
            }
        }
        else
        {
            if (cnt_in1 != 0)
            {
                cnt_in1--;
            }
            else
            {
                input_flag &= ~(_BV(1));
            }
        }
    }
}

void dbg_msg(void)
{
    uint16_t dbg_time;
    static uint16_t dbg_last_time;

    // prints every 0.5 second variable under investigation
    dbg_time = millis();
    if (dbg_time - dbg_last_time < 500)
    {
    }
    else
    {
        dbg_last_time = dbg_time;
        Serial.print("input_flag = ");
        Serial.print(input_flag);
        Serial.print(", stm = ");
        Serial.print(stm);
        Serial.print(", ton1.sb = ");
        Serial.println(ton1.sb);
    }
}

/* software timer */
void ton16(ton_db * const db)
{
    enum ton_sb_ ton_sb = db->sb;
    uint16_t ton_preset = db->preset;
    uint16_t ton_time = db->time;

    uint16_t time = millis();

    if (ton_sb == RUN)
    {
        if (time - ton_time < ton_preset)
        {
        }
        else
        {
            ton_sb = CPL;
        }
    }

    db->sb = ton_sb;
}

void setup_io(void)
{
    // Initialize IN0 and IN1 as inputs with pull-up resistors
    pinMode(IN0, INPUT_PULLUP);
    pinMode(IN1, INPUT_PULLUP);

    // Initialize OUT0 and OUT1 as outputs
    pinMode(OUT0, OUTPUT);
    pinMode(OUT1, OUTPUT);

    // Ensure outputs start LOW
    digitalWrite(OUT0, LOW);
    digitalWrite(OUT1, LOW);
}
// EOF

Interestingly, someone found that ChatGPT generates debounce code with the same bug.

3 Likes

Regarding to the my #1 post I have to admit I used Grok at x.com to generate the core of the debounce function :grinning:. It seems AI not perfect yet.

Nope, it isn't. AI makes purposefully misleading mistakes.

3 Likes

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