Płytka rozwojowa NodeMCU jest platformą open source do tworzenia urządzeń wbudowanych opartych na Wi-Fi. Jest ona oparta na module Wi-Fi ESP8266 i uruchamia firmware NodeMCU oparty na Lua. NodeMCU narodził się z chęci przezwyciężenia wszystkich wąskich gardeł związanych z pierwszymi wersjami modułu ESP8266, który nie był kompatybilny z breadboardami, trudny do zasilania i jeszcze trudniejszy do zaprogramowania. Jego łatwość użycia i niska cena, szybko przyciągnęły go do serca twórców i dziś jest to jedna z najpopularniejszych płytek.
Pod koniec tego projektu-kursu dowiesz się, jak używać NodeMCU jako serwera WWW oraz jak kontrolować piny GPIO płytki NodeMCU/ESP8266 z poziomu strony internetowej.
ESP8266 - schemat
Schemat tego projektu jest dość prosty. Jak wspomniano wcześniej, będziemy przełączać diody LED, aby zademonstrować, co można osiągnąć za pomocą serwera WWW NodeMCU. Podczas gdy diody LED są tutaj używane, możesz zdecydować się na użycie bardziej użytecznych komponentów, takich jak przekaźnik, który może być użyty do sterowania urządzeniami w Twoim domu.
Podłącz komponenty tak jak pokazano na poniższym schemacie.
Dodatnie nóżki zielonej i czerwonej diody LED są podłączone do pinów cyfrowych 1 i 2 NodeMCU (odpowiednio), podczas gdy ich ujemne nóżki są podłączone do masy przez rezystor 220 omów, aby ograniczyć wartość prądu płynącego przez diody LED.
Mając gotowy schemat, możemy teraz przejść do kodu projektu.
ESP8266 - kod
Jednym z najprostszych sposobów zaprogramowania NodeMCU jest Arduino IDE. Wymaga to jednak skonfigurowania Arduino IDE poprzez zainstalowanie pliku obsługi płytki dla NodeMCU. Jeśli używasz Arduino IDE do zaprogramowania NodeMCU po raz pierwszy, musisz to zrobić zanim przejdziesz do dalszej części instrukcji. Skorzystaj z tego szczegółowego przewodnika, aby dowiedzieć się, jak skonfigurować Arduino do programowania płytek opartych na ESP8266.
Mając to już za sobą, możemy teraz przejść do kodu. Główną siłą napędową tego projektu jest biblioteka ESP8266WiFi. Biblioteka ta zawiera przydatne funkcje do implementacji działań i projektów opartych o Wi-Fi na NodeMCU. Zawiera ona wszystko czego potrzebujemy do stworzenia punktu dostępowego Wi-Fi lub połączenia się z istniejącym punktem dostępowym, a także stworzenia serwera i klienta, które są ważne dla tego projektu. Biblioteki są dołączone do plików płytki NodeMCU dla Arduino, więc nie ma potrzeby instalowania ich po zainstalowaniu plików płytki.
Jak wspomniałem wyżej, naszym celem jest stworzenie serwera WWW, poprzez który będzie można sterować GPIO jednostki NodeMCU. Serwer WWW powinien być dostępny przez przeglądarkę na dowolnym urządzeniu w tej samej sieci co NodeMCU.
Kod tego projektu jest zmodyfikowaną wersją kodu Rui Santosa (podziękowania dla niego). Jest on trochę skomplikowany i może być trudny do zrozumienia dla osób bez znajomości HTML, ale postaram się go jak najlepiej rozłożyć na czynniki pierwsze.
Na początek, jak zwykle, dołączamy bibliotekę, która będzie używana w kodzie, w tym przypadku jest to biblioteka ESP8266WiFi.
#include <ESP8266WiFi.h>
Następnie dodajemy dane uwierzytelniające punktu dostępowego Wi-Fi, do którego NodeMCU będzie podłączony. Upewnij się, że nazwa użytkownika i hasło znajdują się pomiędzy cudzysłowami. Określamy również port, przez który system będzie się komunikował oraz tworzymy zmienną do przechowywania żądań.
const char* ssid = "WIFI_SSID";
const char* password = "WIFI_PASSWORD";
WiFiServer server(80);// Set port to 80
String header; // This storees the HTTP request
Następnie deklarujemy piny Nodemcu, do których będą podłączone czerwona i zielona dioda LED oraz tworzymy zmienne, które będą przechowywały stan diod LED.
int greenled = D1;
int redled = D2;
String greenstate = "off";// state of green LED
String redstate = "off";// state of red LED
Mając to już za sobą, przechodzimy do funkcji void setup().
Zaczynamy od zainicjalizowania monitora szeregowego (ponieważ będzie on później używany do debugowania) i ustawienia pinModes pinów, do których podłączone są diody LED jako wyjściowe. Następnie ustawiamy piny "LOW", aby zapewnić, że układ startuje w stanie neutralnym.
void setup() {
Serial.begin(115200);
// Set the pinmode of the pins to which the LEDs are connected and turn them low to prevent flunctuations
pinMode(greenled, OUTPUT);
pinMode(redled, OUTPUT);
digitalWrite(greenled, LOW);
digitalWrite(redled, LOW);
Następnie łączymy się z punktem dostępowym używając danych uwierzytelniających jako argumentów do funkcji WiFi.begin() i używamy funkcji WiFi.status() do sprawdzenia czy połączenie się powiodło.
WiFi.begin(ssid, password);
Serial.print("Connecting to ");
Serial.println(ssid);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Jeżeli połączenie się powiedzie, na monitorze szeregowym zostanie wyświetlony tekst informujący o tym fakcie, a także adres IP serwera sieciowego. Ten adres IP staje się adresem internetowym serwera i to właśnie on będzie wpisywany w każdej przeglądarce internetowej w tej samej sieci, aby uzyskać dostęp do serwera.
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());// this will display the Ip address of the Pi which should be entered into your browser
Po wykonaniu tych czynności uruchamiamy serwer za pomocą funkcji server.begin() i przechodzimy do funkcji void loop( ).
server.begin();
Funkcja void loop() jest miejscem, w którym wykonywana jest większość pracy. Zaczynamy od użycia funkcji server.available() do nasłuchiwania połączeń przychodzących od klientów (przeglądarek internetowych). Kiedy klient jest dostępny i połączony, odczytujemy żądanie klienta i wysyłamy nagłówek jako odpowiedź.
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
Następnie sprawdzamy, czy żądanie klienta wskazuje na naciśnięcie przycisku na stronie internetowej, aby włączyć/wyłączyć któryś z pinów, zaczynając od zielonej diody. Jeśli żądanie wskazuje na "on", pin jest ustawiany w stan wysoki, a zmienna stanu jest odpowiednio aktualizowana i vice versa.
// turns the GPIOs on and off
if (header.indexOf("GET /green/on") >= 0) {
Serial.println("green on");
greenstate = "on";
digitalWrite(greenled, HIGH);
} else if (header.indexOf("GET /green/off") >= 0) {
Serial.println("green off");
greenstate = "off";
digitalWrite(greenled, LOW);
} else if (header.indexOf("GET /red/on") >= 0) {
Serial.println("red on");
redstate = "on";
digitalWrite(redled, HIGH);
} else if (header.indexOf("GET /red/off") >= 0) {
Serial.println("red off");
redstate = "off";
digitalWrite(redled, LOW);
}
Następnie tworzymy stronę internetową, która będzie wyświetlana i aktualizowana przez NodeMCU w miarę interakcji z użytkownikiem. Kluczową funkcją do tego jest funkcja Client.println(), która służy do wysyłania skryptów HTML linia po linii do klienta (przeglądarki).
Zaczynamy od użycia "doctype", aby wskazać, że następne kilka tekstów, które mają zostać wydrukowane, to linie HTML.
client.println("<!DOCTYPE html><html>");
Następnie dodajemy poniższe linie, aby strona była responsywna niezależnie od używanej przeglądarki
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
Wrzucamy również kilka fragmentów CSS do klienta, aby strona była przyjazna dla użytkownika. Możesz to edytować, aby dodać swój własny kolor, styl czcionki itp.
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button2 {background-color: #77878A;}</style></head>");
Następnie nagłówek strony jest wysyłany wraz z przyciskami, które są ustawione na wyświetlanie włączone lub wyłączone w zależności od aktualnego stanu diody LED. Będą one wyświetlane wyłączone, jeśli aktualny stan to ON i na odwrót.
client.println("<body><h1>ESP8266 Web Server</h1>");
// Display current state, and ON/OFF buttons for GPIO 5
client.println("<p>green - State " + greenstate + "</p>");
// If the green LED is off, it displays the ON button
if (greenstate == "off") {
client.println("<p><a href=\"/green/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/green/off\"><button class=\"button button2\">OFF</button></a></p>");
}
// Display current state, and ON/OFF buttons for GPIO 4
client.println("<p>red - State " + redstate + "</p>");
// If the red LED is off, it displays the ON button
if (redstate == "off") {
client.println("<p><a href=\"/red/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/red/off\"><button class=\"button button2\">OFF</button></a></p>");
}
client.println("</body></html>");
Następnie zamykamy połączenie i pętla przechodzi od nowa.
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
Kompletny kod projektu znajduje się poniżej, a także jest dostępny do pobrania w sekcji download na końcu tutoriala.
#include <ESP8266WiFi.h>
// Add wifi access point credentiaals
const char* ssid = "WIFI_SSID";
const char* password = "WIFI_PASSWORD";
WiFiServer server(80);// Set port to 80
String header; // This storees the HTTP request
// Declare the pins to which the LEDs are connected
int greenled = D1;
int redled = D2;
String greenstate = "off";// state of green LED
String redstate = "off";// state of red LED
void setup() {
Serial.begin(115200);
// Set the pinmode of the pins to which the LEDs are connected and turn them low to prevent flunctuations
pinMode(greenled, OUTPUT);
pinMode(redled, OUTPUT);
digitalWrite(greenled, LOW);
digitalWrite(redled, LOW);
//connect to access point
WiFi.begin(ssid, password);
Serial.print("Connecting to ");
Serial.println(ssid);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());// this will display the Ip address of the Pi which should be entered into your browser
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
// turns the GPIOs on and off
if (header.indexOf("GET /green/on") >= 0) {
Serial.println("green on");
greenstate = "on";
digitalWrite(greenled, HIGH);
} else if (header.indexOf("GET /green/off") >= 0) {
Serial.println("green off");
greenstate = "off";
digitalWrite(greenled, LOW);
} else if (header.indexOf("GET /red/on") >= 0) {
Serial.println("red on");
redstate = "on";
digitalWrite(redled, HIGH);
} else if (header.indexOf("GET /red/off") >= 0) {
Serial.println("red off");
redstate = "off";
digitalWrite(redled, LOW);
}
// Display the HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS to style the on/off buttons
// Feel free to change the background-color and font-size attributes to fit your preferences
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button2 {background-color: #77878A;}</style></head>");
// Web Page Heading
client.println("<body><h1>ESP8266 Web Server</h1>");
// Display current state, and ON/OFF buttons for GPIO 5
client.println("<p>green - State " + greenstate + "</p>");
// If the green LED is off, it displays the ON button
if (greenstate == "off") {
client.println("<p><a href=\"/green/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/green/off\"><button class=\"button button2\">OFF</button></a></p>");
}
// Display current state, and ON/OFF buttons for GPIO 4
client.println("<p>red - State " + redstate + "</p>");
// If the red LED is off, it displays the ON button
if (redstate == "off") {
client.println("<p><a href=\"/red/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/red/off\"><button class=\"button button2\">OFF</button></a></p>");
}
client.println("</body></html>");
// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}
ESP8266 - demo
Wgraj kod do swojego NodeMCU. Upewnij się, że wszystko jest podłączone zgodnie z opisem w sekcji schematy. Po załadowaniu kodu, otwórz monitor szeregowy, powinieneś zobaczyć adres IP swojego serwera WWW, jak pokazano poniżej.
Skopiuj adres IP i wklej go w przeglądarce internetowej na urządzeniu podłączonym do tej samej sieci co NodeMCU. Powinieneś zobaczyć stronę internetową i mieć możliwość przełączania diod LED poprzez klikanie na przyciski.
Jak wspomniano powyżej, ten tutorial może służyć jako budulec dla złożonych serwerów internetowych i rozwiązań IoT. Co zbudujesz?