Devi usare una variabile per memorizzare lo stato del pulsante nel precedente ciclo di campionamento:
int prevButtonState;
Poi all'interno di loop, ma sottoposto al controllo di millis() per non campionare lo stato del pulsante troppo spesso (vedi il più volte citato blink without delay), usi una logica tipo questa:
currButtonState = digitalRead(buttonPin);
if (currButtonState != prevButtonState) { // il pulsante ha cambiato stato
if (currButtonState == HIGH) {
// il pulsante è passato da LOW a HIGH
}
else {
// il pulsante è passato da HIGH a LOW
}
prevButtonState = currButtonState; // aggiorna per il prossimo ciclo
}
Cosa scrivere al posto dei commenti lo decidi tu in base a come vuoi che il sistema si comporti. Ad esempio se il pulsante è attivo basso (LOW quando premuto) e vuoi incrementare il contatore al suo rilascio, scriverari contatore++ al posto del commento "da LOW a HIGH".