Serwer WWW jest zasadniczo urządzeniem, które przechowuje, przetwarza i dostarcza strony internetowe do klientów internetowych, które mogą obejmować od przeglądarek na naszych laptopach do aplikacji mobilnych na naszych smartfonach. Komunikacja między klientem a serwerem odbywa się za pomocą specjalnego protokołu zwanego Hypertext Transfer Protocol (HTTP). Klient używa adresu URL do złożenia żądania o konkretną stronę, a serwer odpowiada treścią tej strony lub komunikatem o błędzie, jeśli strona nie jest dostępna.
W naszym przypadku, zamiast odpowiadać konkretną stroną internetową, adres URL będzie wykorzystywany do sterowania stanem diod LED podłączonych do ESP, a dokonane zmiany będą aktualizowane na stronie internetowej. Na przykład, jeśli adres URL taki jak "http://192.168.1.1/ledon" zostanie wpisany do przeglądarki, serwer WWW zrozumie, że nadszedł czas, aby włączyć diodę LED, a następnie zaktualizuje stan diody LED do "ON" na stronie internetowej. To samo stanie się, gdy dioda będzie musiała się wyłączyć.
Po zakończeniu tego projektu- kursu będziesz wiedział, jak skonfigurować serwer WWW na ESP32 i połączyć się z nim za pomocą klienta (strony internetowej), aby sterować jego pinami GPIO.
Płytka DOIT Devkit V1 ESP32 została użyta w tym projekcie ze względu na jej oczywistą popularność wśród konstruktorów. Możesz użyć dowolnej innej płytki opartej na ESP32 do tego projektu.
Schemat
Schemat dla tego projektu jest dość prosty. Jak wspomnieliśmy wcześniej, będziemy przełączać diody LED, aby zademonstrować co można osiągnąć używając serwera WWW NodeMCU i chociaż 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 dać Ci możliwość zdalnego sterowania urządzeniami w Twoim domu. Podłącz komponenty tak jak pokazano poniżej.
Dodatnie nóżki zielonej i czerwonej diody LED są podłączone do pinów GPIO 26 i 27 na ESP32 (odpowiednio), podczas gdy ich ujemne nóżki są podłączone do masy przez rezystor 220 omów, aby ograniczyć natężenie prądu płynącego przez diody LED.
Mając gotowy schemat, możemy teraz przejść do kodu projektu.
Kod
Do stworzenia szkicu dzisiejszego projektu użyjemy Arduino IDE ze względu na jego wszechstronność, ogromne wsparcie i popularność. Aby móc to zrobić, należy zainstalować pliki wspierające płytkę ESP32 w Arduino IDE. Algorytm działania polega na podłączeniu ESP32 do punktu dostępowego i wygenerowaniu adresu IP, za pomocą którego urządzenia podłączone do ESP mogą uzyskać dostęp do serwera WWW. Klient (strona internetowa działająca na podłączonych urządzeniach), w połączeniu z serwerem, serwuje stronę internetową, na której znajdują się przyciski do sterowania diodami LED, a za przyciskami umieszczona jest funkcja obsługująca akcje, które występują po ich kliknięciu.
Aby ograniczyć długość kodu, który musimy napisać, skorzystamy z biblioteki ESP32 WiFi Library. Biblioteka ta zawiera funkcje, które ułatwiają skonfigurowanie ESP32 jako serwera WWW. Biblioteka ta, wraz z kilkoma innymi, jest spakowana razem z plikami płytki ESP32 i jest automatycznie instalowana po zainstalowaniu plików płytki ESP32 w Arduino IDE.
Zaczynamy jak zwykle, od dołączenia biblioteki wymaganej dla projektu, która w tym przypadku jest po prostu biblioteką WiFi.h.
#include <WiFi.h>
Następnie dodajemy dane uwierzytelniające punktu dostępowego WiFi, do którego będzie podłączone ESP32. 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ń.
// Replace with your network credentials
const char* ssid = "Enter_SSID_here";
const char* password = "Enter_Password_here";
// Set web server port number to 80
WiFiServer server(80);
// Variable to store the HTTP request
String header;
Następnie deklarujemy piny ESP32, do których podłączone są czerwona i zielona dioda LED oraz tworzymy zmienne przechowujące stan diod LED.
int greenled = 26;
int redled = 27;
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 (będzie on później używany do debugowania) i ustawiamy pinModes pinów, do których podłączone są diody LED jako wyjścia. 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 funkcji WiFi.begin() oraz 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 dla serwera i jest tym, co musimy wpisać w dowolnej 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. 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 w celu włączenia/wyłączenia któregokolwiek z pinów, zaczynając od zielonej diody LED. Jeśli żądanie wskazuje "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 kilka następnych 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 tak, aby wyświetlać ON lub OFF w zależności od aktualnego stanu diod LED. Przycisk będzie wyświetlał OFF, jeśli aktualny stan jest ON i odwrotnie.
client.println("<body><h1>ESP32 Web Server</h1>");
// Display current state, and ON/OFF buttons for green LED
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 Red LED
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 jest dostępny poniżej oraz w dziale download tego poradnika.
#include <WiFi.h>
// Replace with your network credentials
const char* ssid = "Enter_SSID_here";
const char* password = "Enter_Password_here";
// Set web server port number to 80
WiFiServer server(80);
// Variable to store the HTTP request String header;
String header;
// Declare the pins to which the LEDs are connected
int greenled = 26;
int redled = 27;
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 26
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 27
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("");
}
}
Demo
Podłącz DOIT ESP32 Devkit do komputera, upewnij się, że jest on wybrany jako typ płytki w Arduino IDE, następnie wklej kod w IDE, zweryfikuj i wyślij. Po zakończeniu wysyłania kodu otwórz monitor szeregowy. Powinieneś zobaczyć wyświetlony adres IP, jak pokazano na poniższym obrazku.
Podłącz urządzenie (telefon komórkowy lub komputer) do tej samej sieci co ESP32, a następnie otwórz przeglądarkę internetową na tym urządzeniu i wpisz adres IP z monitora szeregowego w pasku adresu. Powinna otworzyć się strona internetowa, którą utworzyliśmy na serwerze WWW, jak pokazano na poniższym obrazku. Klikając na przyciski można przełączać diody LED.
Jak wspomniano wcześniej, w miejsce diod LED można podłączyć przekaźniki lub inny rodzaj aktuatora, aby uczynić projekt bardziej użytecznym.