/*
O que é IRAM_ATTR?
Se o código que queremos executar é uma Rotina de Serviço de 
Interrupção (ISR), geralmente queremos executá-lo o mais rápido 
possível. Se tivéssemos que "esperar" que o ISR carregasse 
no Flash, as coisas podem dar terrivelmente errado.
A função IRAM_ATTR, é utilizada, então, para indicar que tal 
trecho de código ficará na seção do barramento de 
instruções da RAM (maior velocidade), e não na Flash.
Uma peculiaridade: 
deve ser feito o debounce do botão em software dentro da 
interrupção, a fim de as trepidações no botão não serem 
contadas erroneamente como acionamentos. 
Isso não pode ser feito com delays, por isso deve-se fazer 
uso da função millis() para ajudar na comparação do tempo 
do acionamento com o tempo do último acionamento válido. 
Dessa forma, o impacto em termos de tempo de execução dentro 
da função ISR é mínimo.


*/

#define TEMPO_DEBOUNCE 100 //ms

unsigned long timestamp_ultimo_acionamento = 0;

struct Button {
	const uint8_t PIN;
	uint32_t numberKeyPresses;
	bool pressed;
};

Button button1 = {18, 0, false};

void IRAM_ATTR isr() {
	/* Conta acionamentos do botão considerando debounce */
if ( (millis() - timestamp_ultimo_acionamento) >= TEMPO_DEBOUNCE )
{
	button1.numberKeyPresses++;
	button1.pressed = true;
	timestamp_ultimo_acionamento = millis();
}
	
}

void setup() {
	Serial.begin(115200);
	pinMode(button1.PIN, INPUT_PULLUP);
	attachInterrupt(button1.PIN, isr, FALLING);
}

void loop() {
	if (button1.pressed) {
		Serial.printf("Button 1 has been pressed %u times\n", button1.numberKeyPresses);
		button1.pressed = false;
	}
}