Skip to content

Instantly share code, notes, and snippets.

@ohsix
Created May 26, 2024 04:23
Show Gist options
  • Save ohsix/9f398e2d95ef63a41a793e7b47940391 to your computer and use it in GitHub Desktop.
Save ohsix/9f398e2d95ef63a41a793e7b47940391 to your computer and use it in GitHub Desktop.
frame based button thing, inspired by the quake 3 input code
#include <Arduino.h>
#include "buttons.h"
struct buttonState buttonState[2];
void buttonsBegin()
{
uint8_t buttons = sizeof(buttonState) / sizeof(buttonState[0]);
for(int i = 0; i < buttons; i++)
{
pinMode(buttonState[i].arduinoPin, INPUT);
buttonState[i].changeTime = millis();
/* can't meaningfully catch the first 'frame', but technically it's not pressed */
buttonState[i].currentState = digitalRead(buttonState[i].arduinoPin);
buttonState[i].changeState = 0;
}
}
void updateButtons()
{
uint8_t buttons = sizeof(buttonState) / sizeof(buttonState[0]);
for(int i = 0; i < buttons; i++)
{
uint8_t newState = digitalRead(buttonState[i].arduinoPin);
if(newState == buttonState[i].changeState)
{
buttonState[i].currentState = newState;
} else {
buttonState[i].changeTime = millis();
buttonState[i].changeState = newState;
}
}
}
/* debouncing would use time in Pressed / Release if needed */
uint8_t buttonPressed(uint8_t idx)
{
if(buttonState[idx].currentState == 0 && buttonState[idx].changeState == 1)
return 1;
return 0;
}
/* time aspect to holding, where down and up only happen in one 'frame' */
uint8_t buttonHeld(uint8_t idx, uint16_t time)
{
if(buttonState[idx].currentState == 1)
{
if(millis() - buttonState[idx].changeTime >= time)
{
/* 'time' ends up being a key repeat rate, need per button data to latch */
buttonState[idx].changeTime = millis();
return 1;
}
}
return 0;
}
uint8_t buttonReleased(uint8_t idx)
{
if(buttonState[idx].currentState == 1 && buttonState[idx].changeState == 0)
return 1;
return 0;
}
#include <stdint.h>
enum { BTN_DO, BTN_NEXT };
/* make this a library, it's just the quake input code thing for 'half presses'
it's also a nice way to debounce inputs without delays/rereads/filtering */
struct buttonState {
uint8_t arduinoPin; /* the thing to digitalRead/pinMode */
int32_t changeTime; /* millis() when state latched */
uint8_t currentState;
uint8_t changeState;
};
extern struct buttonState buttonState[2];
void buttonsBegin();
void updateButtons();
/* debouncing would use time in Pressed / Release if needed */
uint8_t buttonPressed(uint8_t idx);
/* time aspect to holding, where down and up only happen in one 'frame' */
uint8_t buttonHeld(uint8_t idx, uint16_t time = 0);
uint8_t buttonReleased(uint8_t idx);
at the moment buttons are added explicitly, can be nicer as a library
in setup:
buttonState[BTN_DO].arduinoPin = BTN_PIN1;
buttonState[BTN_NEXT].arduinoPin = BTN_PIN2;
buttonsBegin();
in loop:
updateButtons();
if(buttonHeld(BTN_DO, 1000)) /* been held for 1000 milliseconds */
if(buttonPressed(BTN_NEXT)) /* pressed, only triggers once, in the same 'frame' updateButtons just read */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment