Sercem dzisiejszego projektu jest płytka z Wi-Fi, której nie trzeba przedstawiać; oparta na ESP8266 płytka deweloperska NodeMCU. Jest to platforma open source do tworzenia systemów wbudowanych opartych na Wi-Fi i jest oparta na popularnym module Wi-Fi ESP8266, z firmware NodeMCU opartym na Lua. NodeMCU narodził się z chęci przezwyciężenia ograniczeń związanych z pierwszymi wersjami modułu ESP8266, który nie był kompatybilny z breadboardami, był trudny do zasilania i jeszcze trudniejszy do programowania. Płytka NodeMCU jest łatwa w użyciu, tania, co szybko przyciągnęło ją do serc konstruktorów i dziś jest jedną z najpopularniejszych płytek.
W tym projekcie dodamy 2-kanałowy moduł przekaźnika do płytki ESP8266. Istota projektu polega na sterowaniu GPIO NodeMCU z poziomu strony internetowej na dowolnym urządzeniu podłączonym do tej samej sieci co płytka. Stan GPIO steruje cewkami przekaźników, co powoduje, że przekaźnik przełącza się pomiędzy stanem normalnie otwartym (NO) i normalnie zamkniętym (NC) w zależności od stanu GPIO, tym samym efektywnie włączając lub wyłączając podłączone urządzenie.
Jeśli nie masz dostępu do modułu przekaźnikowego, możesz użyć 2× jednokanałowych modułów przekaźnikowych lub pojedynczych przekaźników z obsługującymi je układami tranzystorowymi.
Schemat
Schemat dla tego projektu jest dość prosty. Podłącz elementy tak jak pokazano na poniższym schemacie.
Aby ułatwić śledzenie połączeń, poniżej znajduje się mapa pinów połączeń pomiędzy NodeMCU a modułem przekaźnika.
Jeśli używasz zwykłych przekaźników bez układu podtrzymującego moduł, podłącz przekaźniki do NodeMCU jak pokazano poniżej. Upewnij się, że cewki przekaźnika mają napięcie znamionowe 5 V lub zmień zasilanie 5 V, aby dopasować je do znamionowego napięcia cewki Twojego przekaźnika.
Mając gotowy schemat, możemy przejść do pisania kodu dla projektu.
Kod
Jednym z najłatwiejszych sposobów programowania NodeMCU jest użycie 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 tego projektu. Postępuj zgodnie ze szczegółowym przewodnikiem "Getting Started with the NodeMCU", aby dowiedzieć się, jak skonfigurować Arduino IDE do programowania płytek opartych na ESP8266.
Kod dla tego projektu jest zmodyfikowaną wersją kodu z artykułu "NodeMCU ESP8266 WebServer". Kod bazuje na bibliotece ESP8266Wi-Fi.h, która pozwala na łatwe wykorzystanie funkcjonalności Wi-Fi płytki. Zawiera ona wszystko co jest nam potrzebne do stworzenia lub dołączenia do punktu dostępowego Wi-Fi, a także stworzenia serwera i klienta, które są istotne dla tego projektu. Biblioteka jest dołączona do plików płytki NodeMCU dla Arduino, więc nie ma potrzeby instalowania jej po zainstalowaniu plików płytki.
Kod do tego projektu pozwoli nam na zdalne sterowanie urządzeniami podłączonymi do GPIO (poprzez przekaźniki) płytki NodeMCU.
Na początek dołączamy bibliotekę, którą wykorzystamy w projekcie, czyli w tym przypadku jest to biblioteka ESP8266Wi-Fi.h.
#include <ESP8266WiFi.h>
Następnie dodajemy dane uwierzytelniające punktu dostępowego Wi-Fi, do którego będzie podłączone NodeMCU. 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ł i tworzymy zmienną do przechowywania żądań.
// Add wifi access point credentiaals
const char* ssid = "xxxxx";
const char* password = "xxxx";
WiFiServer server(80);// Set port to 80
Następnie deklarujemy piny Nodemcu, do których podłączone są piny przekaźników i tworzymy zmienne przechowujące stan każdego przekaźnika.
// Declare the pins to which the appliances are connected via relays
int app1 = D1; //appliance 1
int app2 = D2; //appliance 2`
//you can add more more appliances below.
String app1state = "off";// state of appliance1
String app2state = "off";// state of appliance2
Następna jest funkcja 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ą przekaźniki jako wyjściowe. Następnie ustawiamy piny "LOW", aby zapewnić, że system startuje w stanie OFF.
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(app1, OUTPUT);
pinMode(app2, OUTPUT);
digitalWrite(app1, LOW);
digitalWrite(app2, LOW);
Następnie łączymy się z punktem dostępowym używając poświadczeń podanych jako argumenty do funkcji Wi-Fi.begin() i używamy funkcji Wi-Fi.status() do sprawdzenia czy połączenie się powiodło. System będzie próbował tak długo, aż połączenie się powiedzie.
//connect to access point
WiFi.begin(ssid, password);
Serial.print("Connecting to ");
Serial.println(ssid);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Jeżeli połączenie jest udane, na monitorze szeregowym jest drukowany tekst, aby to wskazać, wraz z adresem IP NodeMCU. Ten adres IP staje się adresem internetowym serwera i powinien być wpisany w dowolnej przeglądarce internetowej w tej samej sieci co serwer, abyśmy mogli uzyskać do niego dostęp.
// 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 browse
}
Następnie uruchamiamy serwer za pomocą funkcji server.begin().
server.begin();
}
Następnie piszemy funkcję void loop(). To właśnie tutaj 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). Gdy klient jest dostępny i połączony, odczytujemy jego żądanie i wysyłamy nagłówek jako odpowiedź.
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();
Następnie żądanie klienta jest sprawdzane, czy wskazuje ono na naciśnięcie przycisku na stronie internetowej. Jeżeli tak, to stan GPIO jest zmieniany odpowiednio do żądania. Jeśli żądanie wskazuje na "ON", pin jest ustawiany na HIGH, a zmienna stanu jest odpowiednio aktualizowana.
// turns the GPIOs on and off
if (header.indexOf("GET /app1/on") >= 0) {
Serial.println("App 1 on");
app1state = "on";
digitalWrite(app1, HIGH);
} else if (header.indexOf("GET /app1/off") >= 0) {
Serial.println("App 1 off");
app1state = "off";
digitalWrite(app1, LOW);
} else if (header.indexOf("GET /app2/on") >= 0) {
Serial.println("App 2 on");
app2state = "on";
digitalWrite(app2, HIGH);
} else if (header.indexOf("GET /app2/off") >= 0) {
Serial.println("App 2 off");
app2state = "off";
digitalWrite(app2, 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.
Zaczynamy od wskazania, że kilka następnych tekstów, które mają zostać wydrukowane, to linie HTML, zgodnie z deklaracją doctype.
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\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
Następnie, niektóre kawałki CSS są wysyłane do klienta, aby nadać stronie internetowej przyjazny wygląd. 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 wysyłany jest nagłówek strony, po którym następują przyciski, a przyciski są ustawione tak, aby wyświetlały aktualny stan urządzeń. Będą one pokazywały OFF, jeśli aktualny stan to ON i odwrotnie.
client.println("<body><h1>ESP8266 Web Server</h1>");
// Display current state, and ON/OFF buttons for GPIO 5
client.println("<p>app1 - State " + app1state + "</p>");
// If Appliance 1 is off, it displays the ON button
if (app1state == "off") {
client.println("<p><a href=\"/app1/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/app1/off\"><button class=\"button button2\">OFF</button></a></p>");
}
// Display current state, and ON/OFF buttons for GPIO 4
client.println("<p>app2 - State " + app2state + "</p>");
// If Appliance 2 is off, it displays the ON button
if (app2state == "off") {
client.println("<p><a href=\"/app2/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/app2/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
}
}
}
Następnie zamykamy połączenie i przepływ wraca na górę.
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}
Kompletny kod projektu dostępny jest poniżej, a także załączony w dziale download.
#include <ESP8266WiFi.h>
// Add wifi access point credentiaals
const char* ssid = "xxxx";
const char* password = "xxxxx";
WiFiServer server(80);// Set port to 80
String header; // This storees the HTTP request
// Declare the pins to which the appliances are connected via relays
int app1 = D1; //appliance 1
int app2 = D2; //appliance 2`
//you can add more more appliances below.
String app1state = "off";// state of appliance1
String app2state = "off";// state of appliance2
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(app1, OUTPUT);
pinMode(app2, OUTPUT);
digitalWrite(app1, LOW);
digitalWrite(app2, 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 /app1/on") >= 0) {
Serial.println("App 1 on");
app1state = "on";
digitalWrite(app1, HIGH);
} else if (header.indexOf("GET /app1/off") >= 0) {
Serial.println("App 1 off");
app1state = "off";
digitalWrite(app1, LOW);
} else if (header.indexOf("GET /app2/on") >= 0) {
Serial.println("App 2 on");
app2state = "on";
digitalWrite(app2, HIGH);
} else if (header.indexOf("GET /app2/off") >= 0) {
Serial.println("App 2 off");
app2state = "off";
digitalWrite(app2, 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>app1 - State " + app1state + "</p>");
// If Appliance 1 is off, it displays the ON button
if (app1state == "off") {
client.println("<p><a href=\"/app1/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/app1/off\"><button class=\"button button2\">OFF</button></a></p>");
}
// Display current state, and ON/OFF buttons for GPIO 4
client.println("<p>app2 - State " + app2state + "</p>");
// If Appliance 2 is off, it displays the ON button
if (app2state == "off") {
client.println("<p><a href=\"/app2/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/app2/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("");
}
}
Demo
Wgraj kod do NodeMCU. Upewnij się, że wszystko jest podłączone zgodnie z opisem w sekcji schematy. Po załadowaniu kodu powinieneś zobaczyć adres IP swojego serwera WWW wyświetlony na monitorze szeregowym, jak pokazano poniżej.
Skopiuj adres IP i wklej go w przeglądarce internetowej na dowolnym urządzeniu (Mobile lub PC) podłączonym do tej samej sieci co NodeMCU. Powinieneś zobaczyć stronę internetową i mieć możliwość przełączania podłączonych urządzeń poprzez klikanie przycisków.
Jak wspomniano powyżej, ten pr4ojekt może posłużyć jako budulec dla bardziej złożonych serwerów WWW i rozwiązań IoT. Co zbudujesz?