NodeMCU – Implementacja przycisku resetowania
Po lekturze ostatnich atykułów o NodeMCU powinniśmy wiedzieć już jak bezboleśnie skonfigurować i uruchomić ESP8266. Jednak jak na pewno zauważyliście na wszelkiej maści routerach itp. można znaleźć malutki przycisk służący do przywracania urządzenia do stanu fabrycznego. Aby dopełnić moją serię poradników w dzisiejszym artykule dowiemy się jak zaimplementować taki przycisk :)
Implementacja klasy
To w zasadzie pierwszy artykuł o NodeMCU, w któym będziemy potrzebowali fizycznego komponentu – mianowicie przycisku o takeigo np. Zaprogramujemy go na 2 tryby. Krótkie wciśnięcie spowoduje restart urządzenia, a długie przytrzymanie (min. 4 sekundy) poskutkuje wymazaniem wszystkich danych. W tym celu zaimplementujemy sobie prostą klasę zawierającą dwa listenery: shortClickHandler oraz longClickHandler.
#ifndef BUTTONDRIVER_H
#define BUTTONDRIVER_H
class ButtonDriver {
private:
static const int LONG_PRESS_TIME = 4000;
int pin;
unsigned long buttonPressStartTime;
bool lastButtonState;
void (*shortClickHandler)() = NULL;
void (*longClickHandler)() = NULL;
public:
ButtonDriver(int pin);
void poll();
void setShortClickHandler(void (*handler)());
void setLongClickHandler(void (*handler)());
};
#endif /* BUTTONDRIVER_H */
Jak widać mamy w tym przypadku więcej pól niż metod. Przeanalizujmy je więc, bo dwa z nich mogą być szczególnie niepokojące dla kogoś kto nie pisał dużo w C++.
Poza wiele mówiącymi buttonPressStartTime oraz lastButtonState i pin mamy dwa tajemnicze zapisy w formie typ (*f)(). Taki zapis mówi kompilatorowi, że w zmiennej o nazwie f przechowujemy adres do jakiejś funkcji bez parametrów, zwracającej typ typ, którą możemy wykonać odwołując się do tej zmiennej. Dzięki temu możemy zaimplementować tzw. listener, któy wykona się w odpowiednim momencie.
void ButtonDriver::setShortClickHandler(void (*handler)()) {
shortClickHandler = handler;
}
void ButtonDriver::setLongClickHandler(void (*handler)()) {
longClickHandler = handler;
}
W taki sposób ustawiamy jaka funkcja ma się wykonać przy odpowiednim wydarzeniu. Wystarczy jako parametr do którejś z tych metod przekazać nazwę funkcji, która zwraca void’a i nie przyjmuje parametrów.
Zobaczmy co dzieje się przy tworzeniu obiektu:
ButtonDriver::ButtonDriver(int pin) {
this->pin = pin;
pinMode(pin, INPUT_PULLUP);
lastButtonState = digitalRead(pin);
}
Zapamiętujemy w obiekcie do jakiego pinu podłączyliśmy przycisk, ustawiamy ten pin jako wejście z podciągnięćiem do VCC, i sprawdzamy jego obecny stan.
Na koniec tej prostej klasy pozostało nam już tylko zaimplementowanie metody poll():
void ButtonDriver::poll() {
bool currentButtonState = digitalRead(pin);
unsigned long buttonPressedTime = millis() - buttonPressStartTime;
if(currentButtonState == lastButtonState) {
return;
}
if(currentButtonState) {
if(buttonPressedTime < LONG_PRESS_TIME) {
if(shortClickHandler != NULL) {
shortClickHandler();
}
} else {
if(longClickHandler != NULL) {
longClickHandler();
}
}
} else {
buttonPressStartTime = millis();
}
lastButtonState = currentButtonState;
}
To tutaj kryje się cała prosta logika tego mechanizmu. Chciałbym jednak zwrócić szczególną uwagę na wywoływanie shortClickHandler i longClickHandler. Przed każdym wywołąniem bezwzględnie należy sprawdzić, czy te wskaźniki nie są NULLami! Jeżeli tego nie zrobimy mogą stać się rzeczy niestworzone ;)
Wykorzystanie w programie
Oczywiście pierwsze co musimy zrobić to załączyć naszą bibliotekę:
#include "ButtonDriver.h"
Do każdego przycisku, który chcemy podłączyć należy zainicjować odpowiedni obiekt w taki sposób:
#define BTN_RESET D6 ButtonDriver resetBtn(BTN_RESET);
Aby podłączyć jakieś funkcje do krótkiego i długiego wciśnięcia należy je najpierw napisać. U mnie wyglądają tak:
void reboot() {
WiFi.mode(WIFI_STA);
WiFi.disconnect();
ESP.reset();
}
void reset() {
configManager.reset();
reboot();
}
Teraz w funkcji setup() możemy podpiąć je do naszego obiektu w taki sposób:
resetBtn.setShortClickHandler(reboot); resetBtn.setLongClickHandler(reset);
Ostatnim krokiem jest wywołanie metody poll() wszędzie tam, gdzie ma działać nasz przycisk. Należy pamiętać że ta funkcja musi wykonywać się cały czas, bez żadnych dłuższych przerw.
resetBtn.poll();
Podsumowanie
To już wszystko, jeżeli chodzi o zapis konfiguracji w pamięci ESP. W kolejnym artykule dowiemy się w jaki sposób napisać prostą aplikację na system Android, która poprzez zaimplementowaną wcześniej w urządzeniu funkcjonalność UDP w przyjazny sposób (bez Packet Sendera) pozwoli je skonfigurować. Będzie to już ostatnia rtykuł z tej serii, ale na pewno nie ostatni o ESP8266 :)

Super! Tyczy się to całego wątku ESP Dowiedziałem się sporo rzeczy, przydatnych rzeczy i za to wielkie dzięki!