W poprzednim tekście (Domowy NAS: wyłączanie i włączanie… z Arduino?!) zaprzęgłem Arduino do budzenia serwera NAS. Arduino, wyposażone w rozszerzenie Ethernetowe, wysyłało pakiet WOL. Czuwająca karta sieciowa serwera odbierała go – i uruchamiała NAS.
Niestety UNO z rozszerzeniem Ethernet zajmuje trochę miejsca. Oprócz zasilania potrzebuje jeszcze kabla sieciowego. Czy można łatwiej? Tak, na przykład za pomocą WemosD1 (czyli takiego Arduino z EPS8266 na pokładzie). Teraz wystarczy już tylko zasilanie – WemosD1 ma na pokładzie kartę WiFi i może się podłączyć do Waszej domowej sieci bezprzewodowej. Wystarczy już wysłać WOL…
Najpierw MAC
Gdy zabieram się za nowy moduł ESP8266, pierwszym problemem jest znalezienie jego adresu MAC. Mój domowy router filtruje MAC. Jeżeli chcę dodać jakieś nowe urządzenie do sieci domowej, muszę najpierw jego MAC dodać do listy MAC’ów znanych routerowi.
Do odczytania MAC modułu esp możecie użyć taki prosty programik (dodajcie bibliotekę esp8266wifi, pobrano z arduino.cc):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include <ESP8266WiFi.h> /* * Get MAC address of esp8266 * https://www.arduino.cc/en/Reference/WiFiMACAddress */ void printMAC() { byte _mac[6]; WiFi.macAddress(_mac); Serial.print("MAC: "); Serial.print(_mac[0], HEX); Serial.print(":"); Serial.print(_mac[1], HEX); Serial.print(":"); Serial.print(_mac[2], HEX); Serial.print(":"); Serial.print(_mac[3], HEX); Serial.print(":"); Serial.print(_mac[4], HEX); Serial.print(":"); Serial.println(_mac[5], HEX); } void setup() { Serial.begin(115200); Serial.println("-------START-------"); printMAC(); } void loop() { } |
Załadujcie ten program. Otwórzcie konsolę portu szeregowego (CTRL-Shift-M). W rezultacie otrzymacie kilka krzaczków i napis:
1 |
MAC: 5C:CF:7F:1D:71:47 |
…tego MACa dodajcie do listy dozwolonych MAC’ów na Waszym routerze.
Serwer web
Mój wemos włączy się do domowej sieci bezprzewodowej:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <ESP8266WebServer.h> //... const char* ssid = "wasz_ssid"; const char* password = "wasze_haslo"; IPAddress ip(192, 168, 1, 230); IPAddress gateway(192, 168, 1, 254); IPAddress subnet(255, 255, 255, 0); //... void setup(){ //... byte _led = LOW; Serial.begin(115200); Serial.println("-------START-------"); pinMode(LED_BUILTIN, OUTPUT); //configure static IP WiFi.config(ip, gateway, subnet); WiFi.begin(ssid, password); Serial.println("Connecting to WiFi..."); while (WiFi.status() != WL_CONNECTED) { delay(100); //flash led digitalWrite(LED_BUILTIN, _led); (_led == LOW? _led = HIGH: _led = LOW ); } Serial.println("...connected"); //... } |
Funkcja WiFi.config pozwala mi na użycie statycznego IP (zamiast dynamicznego z DHCP). W ten sposób będę wiedział, że sp8266 jest na adresie 192.168.1.230.
1 2 |
const char* ssid = "wasz_ssid"; const char* password = "wasze_haslo"; |
Zmieńcie na nazwę (SSID) oraz hasło do Waszej domowej sieci WiFi.
Będę potrzebował też serwera web (na podstawie bloga runtimeprojects.com):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#include <ESP8266mDNS.h> #include <ESP8266WebServer.h> //... ESP8266WebServer server(80); MDNSResponder mdns; //... void setup(){ //... mdns.begin("esp8266"); Serial.println("My IP: " + WiFi.localIP().toString()); server.on("/", []() { Serial.println("Request for standard page"); //... }); server.on("/up", []() { Serial.println("Request for UP page"); sendWOL(mac_nas); //... }); server.on("/check", []() { Serial.println("Request for check page"); //... }); server.onNotFound([]() { server.send(404, "text/html", "Command Not Found"); }); server.begin(); } void loop(){ server.handleClient(); } |
Zauważcie, że mój serwerek web będzie obsługiwał cztery strony:
- / – główna,
- /up – wysyłająca pakiet WOL
- /check – sprawdzająca stan serwera.
Ping
Do wykrywania serwera skorzystałem z gotowej biblioteki: Biblioteka Ping.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <ESP8266Ping.h> //... IPAddress ip_nas(192, 168, 1, 250); //... /* */ bool pingNAS(IPAddress nasAddress) { IPAddress ip (nasAddress); // The remote ip to ping bool ret = Ping.ping(ip); if ( ret ) { Serial.println("Ping: Host found."); return true; } else { Serial.println("Ping: Host NOT found"); } return false; } //... |
WOL
Moja procedura WOL (na podstawie biblioteki WOL):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <WiFiUdp.h> //... IPAddress broadcast(192, 168, 1, 255); WiFiUDP udp; void sendWOL(byte nasMAC[]) { char _i, _j; if ( udp.beginPacketMulticast(broadcast, 9, WiFi.localIP()) == 1 ) { //Write preamble for (_i = 0; _i < 6; _i++) udp.write(0xFF); for (_i = 0; _i < 16; _i++) for (_j = 0; _j < 6; _j++) udp.write(nasMAC[_j]); if ( udp.endPacket() ) { Serial.println("WOL SENT"); } else { Serial.println("Could not send WOL"); } } else { Serial.println("Could not open UDP packet"); } } //... void setup(){ //... if ( connectUDP(8889) ) { Serial.println("...connected"); } else { Serial.println("COULD NOT CONNECT TO UDP 8888"); } //... server.on("/up", []() { Serial.println("Request for UP page"); sendWOL(mac_nas); server.send(200, "text/html", getUpPage()); }); //... } |
Zwróćcie uwagę:
- Zamiast „udp.begin” użyłem „udp.beginPacketMulticast”,
- Adres broadcastowy „255.255.255.255” nie zadziałał; wyczytałem, że musi zgadzać się z maską – stąd:
12IPAddress subnet(255, 255, 255, 0);IPAddress broadcast(192, 168, 1, 255);
…reszta już poszła łatwo:)
Pełny kod
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
//Server code based on: //http://runtimeprojects.com/2016/10/esp8266-tutorial-part-3-running-a-simple-web-server/ #include <ESP8266mDNS.h> #include <ESP8266WebServer.h> #include <WiFiUdp.h> #include <ESP8266Ping.h> const char* ssid = "wasz_ssid"; const char* password = "wasze_haslo"; byte mac_nas[] = { 0x00, 0x19, 0xb9, 0x1c, 0x10, 0x16 }; IPAddress ip(192, 168, 1, 230); IPAddress ip_nas(192, 168, 1, 250); IPAddress gateway(192, 168, 1, 254); IPAddress subnet(255, 255, 255, 0); IPAddress broadcast(192, 168, 1, 255); ESP8266WebServer server(80); MDNSResponder mdns; WiFiUDP udp; String pageBeg = "<!DOCTYPE HTML><html><head><style>h1{font-family: \"Times New Roman\", Times, serif;}</style></head><body>"; String pageEnd = "</body></html>"; /* Welcome page - start the server? */ String getWelcomePage() { bool _serverFound = pingNAS(ip_nas); String _ret = pageBeg; server.sendHeader("Refresh", "10;url=http://" + WiFi.localIP().toString() ); if ( _serverFound ) { _ret += "<center/><h1>ON LINE</h1></center>"; _ret += "<center/><button disabled>URUCHOM</button></center>"; } else { _ret += "<center/><h1>OFF LINE</h1></center>"; _ret += "<center/><button onclick=\"location.href=\'http://"; _ret += WiFi.localIP().toString(); _ret += "/up\';\">URUCHOM</button></center>"; } return _ret + pageEnd; } /* Start the server page (after sending WOL) */ String getUpPage() { String _ret = pageBeg; server.sendHeader("Refresh", "10;url=http://" + WiFi.localIP().toString() + "/check" ); _ret += "<center/><h1>Budzenie serwera w toku...</h1></center>"; _ret += "<center/><h1>OFF LINE...</h1></center>"; return _ret + pageEnd; } /* Check server state page (wait to finish the boot) */ String getCheckPage() { String _ret = pageBeg; bool _serverFound = pingNAS(ip_nas); if ( _serverFound ) { server.sendHeader("Refresh", "10;url=http://" + WiFi.localIP().toString() ); _ret += "<center/><h1>Budzenie serwera w toku...</h1></center>"; _ret += "<center/><h1>ON LINE</h1></center>"; } else { server.sendHeader("Refresh", "10" ); _ret += "<center/><h1>Budzenie serwera w toku...</h1></center>"; _ret += "<center/><h1>OFF LINE...</h1></center>"; } return _ret + pageEnd; } /* Ping a machine in network (ESP8266Ping lib) */ bool pingNAS(IPAddress nasAddress) { IPAddress ip (nasAddress); // The remote ip to ping bool ret = Ping.ping(ip); if ( ret ) { Serial.println("Ping: Host found."); return true; } else { Serial.println("Ping: Host NOT found"); } return false; } /* http://www.esp8266.com/viewtopic.php?f=32&t=9337 http://www.esp8266.com/viewtopic.php?f=29&t=2222 Connect to UDP – returns true if successful or false if not */ boolean connectUDP(int port) { boolean state = false; int _res = udp.begin(port); if ( _res == 1) { return true; } Serial.print("Failed to connect to:"); Serial.print(port); Serial.print(", error: "); Serial.println(_res); return false; } /* Send WOL - based on https://github.com/koen-github/WakeOnLan-ESP8266 */ void sendWOL(byte nasMAC[]) { char _i, _j; if ( udp.beginPacketMulticast(broadcast, 9, WiFi.localIP()) == 1 ) { //Write preamble for (_i = 0; _i < 6; _i++) udp.write(0xFF); for (_i = 0; _i < 16; _i++) for (_j = 0; _j < 6; _j++) udp.write(nasMAC[_j]); if ( udp.endPacket() ) { Serial.println("WOL SENT"); } else { Serial.println("Could not send WOL"); } } else { Serial.println("Could not open UDP packet"); } } /* Setup */ void setup() { byte _led = LOW; Serial.begin(115200); Serial.println("-------START-------"); pinMode(LED_BUILTIN, OUTPUT); //configure static IP WiFi.config(ip, gateway, subnet); WiFi.begin(ssid, password); Serial.println("Connecting to WiFi..."); while (WiFi.status() != WL_CONNECTED) { delay(100); //flash led digitalWrite(LED_BUILTIN, _led); (_led == LOW? _led = HIGH: _led = LOW ); } Serial.println("...connected"); Serial.println("Connecting to UDP..."); if ( connectUDP(8889) ) { Serial.println("...connected"); } else { Serial.println("COULD NOT CONNECT TO UDP 8888"); } //led on = connected digitalWrite(LED_BUILTIN, true); mdns.begin("esp8266"); Serial.println("My IP: " + WiFi.localIP().toString()); server.on("/", []() { Serial.println("Request for standard page"); server.send(200, "text/html", getWelcomePage()); }); server.on("/up", []() { Serial.println("Request for UP page"); sendWOL(mac_nas); server.send(200, "text/html", getUpPage()); }); server.on("/check", []() { Serial.println("Request for check page"); server.send(200, "text/html", getCheckPage()); }); server.onNotFound([]() { server.send(404, "text/html", "Command Not Found"); }); server.begin(); } /* Main loop */ void loop(void) { server.handleClient(); } |
Powinien zadziałać, jeżeli:
- stałe ssid i password: zmienicie na nazwę (SSID) oraz hasło do Waszej domowej sieci WiFi,
- byte mac_nas[] = {}: zmienicie na adres MAC maszyny, która ma być obudzona (np. server NAS),
- IPAddress ip(192, 168, 1, 230): zmienicie na adres IP, który będzie przypisany esp8266 (nadajcie unikalny adres),
- IPAddress ip_nas(192, 168, 1, 250): zmienicie na adres IP serwera NAS,
- IPAddress gateway(192, 168, 1, 254): zmienicie na adres IP bramy w Waszej sieci domowej,
- IPAddress subnet(255, 255, 255, 0): zmienicie na maskę Waszej podsieci domowej,
- IPAddress broadcast(192, 168, 1, 255): zmienicie na ades rozgłoszeniowy Waszej sieci domowej.
Mam podobna konfiguracje z tym, że serwer DHCP przydziela określony adres IP. Mama jedna problem bo ro ok. 2 dniach serwer nie jest widoczny w sieci. Coś prawdopodobnie rozłącza połączanie po WiFi.