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!