Projects

Just for fun

Home About PubKey

Yet Another Debouncing Method

You can find several approaches for debouncing mechanical switches on the Internet, some work better, some not so good.

One common approach is to ignore events in an ISR when they come too fast:


void count() {
	static uint32_t lastEvent = 0;
	uint32_t currentEvent = micros();
	if (currentEvent > (lastEvent + configBlock.debounce)) {
		lastEvent = currentEvent;
		cnt++;
	}
}

void setup() {
        pinMode(REED_PIN, INPUT_PULLUP);
        attachInterrupt(REED_PIN, count, FALLING);
}

This works very good when only the tipping of a switch is relevant.

When also the time the button was pressed is relevant and when it is especially necessary to distinguish between a short and a long press this approach doesn't work anymore.
Since I couldn't remember the approaches I read about earlier I've sketched this state machine:

(The double-lined states are action-states which send out the related information.)
At least for me, this approach is working very reliable so far, I'm quite happy with it.


enum tPressedState { psHIGH, psLOW, psACCEPTED_LOW, psLONG_START, psLONG_CONT, psLONG_CONT_SEND, psLONG_END, psSHORT, psINVALID };

typedef struct {
	uint8_t index;
	uint8_t buttonPin;
	tPressedState pressedState;
	tPressedState oldPressedState;
	uint32_t lastStateChange;
} tButton;

tButton buttons[] = {
					  { 1, SWITCH_1, psHIGH, psINVALID, 0 },
					  { 2, SWITCH_2, psHIGH, psINVALID, 0 },
					  { 3, SWITCH_3, psHIGH, psINVALID, 0 },
					  { 0, 0, psINVALID, psINVALID, 0 } // END MARKER
};

static void buttonHandler(tButton *button) {
	uint32_t currentMicros = micros();
	uint8_t buttonState = digitalRead(button->buttonPin);

#ifdef DEBUG
	if (button->oldPressedState != button->pressedState) {
		Serial.print("Index ");
		Serial.print(button->index);
		Serial.print(", state changed from ");
		Serial.print(button->oldPressedState);
		Serial.print(" to ");
		Serial.print(button->pressedState);
		Serial.println();
		button->oldPressedState = button->pressedState;
	}
#endif

	switch (button->pressedState) {
	case psHIGH:
		if (buttonState == LOW) {
			button->pressedState = psLOW;
			button->lastStateChange = currentMicros;
		}
		break;
	case psLOW:
		if (buttonState == HIGH) {
			button->pressedState = psHIGH;
			button->lastStateChange = currentMicros;
		} else {
			if (currentMicros > (button->lastStateChange + configBlock.debounce)) {
				button->pressedState = psACCEPTED_LOW;
				button->lastStateChange = currentMicros;
			}
		}
		break;
	case psACCEPTED_LOW:
		if (buttonState == HIGH) {
			button->pressedState = psSHORT;
			button->lastStateChange = currentMicros;
		}
		if (currentMicros > (button->lastStateChange + (configBlock.longPress * 1000))) {
			button->pressedState = psLONG_START;
			button->lastStateChange = currentMicros;
		}
		break;
	case psSHORT:
		sendMsg(button->index, "PRESS_SHORT");
		button->pressedState = psHIGH;
		button->lastStateChange = currentMicros;
		break;
	case psLONG_START:
		sendMsg(button->index, "PRESS_LONG_START");
		button->pressedState = psLONG_CONT;
		button->lastStateChange = currentMicros;
		break;
	case psLONG_CONT:
		if (buttonState == HIGH) {
			button->pressedState = psLONG_END;
			button->lastStateChange = currentMicros;
		}
		if (currentMicros > (button->lastStateChange + (configBlock.longPressRepeat * 1000))) {
			button->pressedState = psLONG_CONT_SEND;
			button->lastStateChange = currentMicros;
		}
		break;
	case psLONG_CONT_SEND:
		sendMsg(button->index, "PRESS_LONG_CONT");
		button->pressedState = psLONG_CONT;
		button->lastStateChange = currentMicros;
		break;
	case psLONG_END:
		sendMsg(button->index, "PRESS_LONG_END");
		button->pressedState = psHIGH;
		button->lastStateChange = currentMicros;
		break;
	default:
		button->pressedState = psHIGH;
		button->lastStateChange = currentMicros;
	}
}

Find it embedded in the code of a small ESP8266-based switch thing I'm using in my home automation setup (home grown control code (on gitlab), homegear for device integration and openhab as user interface) here on gitlab.