Dieses Tutorial beschreibt den Aufbau eines Pingcheck-Moduls, mit dem sich die Verfügbarkeit von Geräten im WLAN anzeigen lässt. Es sendet periodisch Pings an Geräte im Netzwerk. Wenn es eine Antwort erhält, leuchtet eine LED.

Wozu das ganze?!
Ganz einfach. Die Lampe leuchtet so lange, wie ein Rechner an ist. Wenn man ins Bett gehen will, und vergisst alle Rechner herunter zu fahren, fällt es dadurch auf.

 

# Bezeichnung ~Preis
1 ESP-01 Modul mit 512KB Speicher 3,00 €
1 Kondensator 0,47 uF (Elko) 0,05 €
1 Kondensator 1000 uF (Elko) 0,45 €
1 Kondensator 0,1 uF (Elko) 0,05 €
1 Transistor BC546 0,05 €
1 Widerstand 100 Ohm 0,02 €
1 Widerstand 100K Ohm 0,02 €
2 Widerstand 10K Ohm 0,02 €
1 Spannungswandler L78L34 0,25 €
1 Helle LED 0,30 €
1 USB-Kabel 0,10 €
  Stiftleisten für ESP "Sockel" 0,10 €
  Gehäuse  
  optional einen 5mm Plexiglas Stab  
    < 5 €

 

Benötigte Werkzeuge:
Bezeichnung
Breadboard und Steckverbinder oder Lötkolben und (Lochraster-)platine
USB-Seriell Wandler (zum Programmieren des ESP), siehe hier.
Netzteil 5V DC oder eine andere "Stromquelle" mit USB (z.B. einen Router)
optional: Heißluftfön
optional: Minibohrer

 

Das fertige Modul sieht so aus:

 

  1. Als erstes müsst ihr euch Gedanken darüber machen, in was für eine Hülle ihr das Modul bauen wollt. Ich habe aus optischen Gründen ein rundes Plastikgehäuse genommen.
  2. Nun programmiert den ESP-01 mit dem folgenden Code:

    /*
      ESP8266 Pingcheck Wifi Module
      Pings three IP Addresses. If one of them responses, the light is on.
      2016 www.frag-duino.de
    */
    
    #include <ESP8266WiFi.h>
    #include <WiFiClient.h>
    #include <ESP8266WebServer.h>
    #include <EEPROM.h>
    extern "C" {
    #include "user_interface.h"
    #include "ping.h"
    }
    
    ESP8266WebServer server(80);
    
    /*
     * To set the new Wifi, enter credentials in the two variables and set RESET_EEPROM to true.
     * During the next start it will save it into the EEPROM. 
     * Then set RESET_EEPROM to false and flash it again.
     * When wifi changes, remember to first change the name over the webinterface.
    */
    
    #define RESET_EEPROM false
    #define WIFI_SSID "bla"
    #define WIFI_PASSWORD "321"
    
    // Settings
    const int PING_INTERVAL = 5000;
    const int PIN_LED = 2;
    const int count_hosts = 3;
    int connection_try = 15;
    char host1[15];
    char host2[15];
    char host3[15];
    boolean host_is_online[] = {false, false, false};
    
    // EEPROM Addresses
    const int ADDRESS_IP1 = 0;
    const int ADDRESS_IP2 = 15;
    const int ADDRESS_IP3 = 30;
    const int ADDRESS_INIT = 105; // - 99
    const int ADDRESS_PASSWORD = 45; // - 75
    const int ADDRESS_SSID = 75; // - 105
    const int EEPROM_SIZE = 106;
    
    // Variables
    IPAddress network_ip;
    char network_ssid[30];
    char network_password[30];
    struct ping_option pingopt;
    unsigned long last_ping = 0;
    unsigned long last_seen_online = 0;
    unsigned long currentMillis = 0;
    int currentHost = 0;
    int state_LED = LOW;
    boolean got_new_hosts;
    boolean save_wifi;
    
    // Save the current config
    void writeIPtoEEPROM() {
      for (int i = 0; i < 15; i++) {
        EEPROM.write(ADDRESS_IP1 + i, host1[i]);
        EEPROM.write(ADDRESS_IP2 + i, host2[i]);
        EEPROM.write(ADDRESS_IP3 + i, host3[i]);
      }
      EEPROM.commit();
    }
    
    void writeSSIDtoEEPROM(String new_ssid) {
      for (int i = 0; i < 30; i++) {
        if (i < new_ssid.length())
          EEPROM.write(ADDRESS_SSID + i, new_ssid[i]);
        else
          EEPROM.write(ADDRESS_SSID + i, '\0');
      }
      EEPROM.commit();
    }
    
    void writePASSWORDtoEEPROM(String new_password) {
      for (int i = 0; i < 30; i++) {
        if (i < new_password.length())
          EEPROM.write(ADDRESS_PASSWORD + i, new_password[i]);
        else
          EEPROM.write(ADDRESS_PASSWORD + i, '\0');
      }
      EEPROM.commit();
    }
    
    // Reply is called, when response arrives
    static void callback_reply (void* arg, void *pdata) {
      struct ping_resp *pingresp = (struct ping_resp *)pdata;
      if (pingresp->resp_time > 0) {
        Serial.println(" Response");
        last_seen_online = currentMillis;
        host_is_online[currentHost] = true;
      }
      else {
        Serial.println(" Timeout");
        host_is_online[currentHost] = false;
      }
    }
    
    // Sends an echo request to an IP-Address
    void ping(uint32 ip)
    {
      pingopt.ip = ip;
      pingopt.count = 1;
      pingopt.coarse_time = 200;
      pingopt.recv_function = &callback_reply;
      // pingopt.sent_function = NULL;
      ping_start(&pingopt);
    }
    
    
    boolean checkIPsyntax(String ip) {
      // Only 0-9 and dots
      for (int i = 0; i <= 33; i++)
        if (ip.indexOf(i) != -1)
          return false;
      if (ip.indexOf(0x2F) != -1)
        return false;
      for (int i = 0x3A; i <= 0x7F; i++)
        if (ip.indexOf(i) != -1)
          return false;
    
      // Count the amount of dots
      int dots = 0;
      for (int i = 0; i < ip.length(); i++)
        if (ip[i] == '.')
          dots++;
      if (dots != 3)
        return false;
    
      // Check the range of the oktets
      int dot_1 = ip.indexOf('.');
      int dot_2 = ip.indexOf('.', dot_1 + 1);
      int dot_3 = ip.indexOf('.', dot_2 + 1);
      int oktet_1 = ip.substring(0, dot_1).toInt();
      int oktet_2 = ip.substring(dot_1 + 1, dot_2).toInt();
      int oktet_3 = ip.substring(dot_2 + 1, dot_3).toInt();
      int oktet_4 = ip.substring(dot_3 + 1).toInt();
      if (oktet_1 >=  0 && oktet_1 <= 255 &&
          oktet_2 >= 0 && oktet_2 <= 255 &&
          oktet_3 >= 0 && oktet_3 <= 255 &&
          oktet_4 >= 0 && oktet_4 <= 255 )
        return true;
      else
        return false;
    }
    
    
    void handleRoot() {
      Serial.print(currentMillis);
      Serial.print(": HTTP-Request with ");
      got_new_hosts = false;
      save_wifi = false;
      String temp_ssid;
      String temp_password;
    
      if (server.args() > 0) {
        Serial.print(server.args());
        Serial.print(" args: ");
        for (int a = 0; a < server.args(); a++) {
          Serial.print("[");
          Serial.print(server.argName(a));
          Serial.print("=");
          Serial.print(server.arg(a));
          Serial.print("]");
    
          if (server.arg(a) == "Restart")  // Restart ESP8266
            system_restart();
          else if (server.arg(a) == "Save-Wifi") {
            Serial.println("OK, Save-Wifi");
            save_wifi = true;
          } else if (server.argName(a) == "ssid") {
            for (int i = 0; i < 30; i++) {
              if (i < server.arg(a).length())
                network_ssid[i] = server.arg(a)[i];
              else
                network_ssid[i] = '\0';
            }
          } else if (server.argName(a) == "password") {
            for (int i = 0; i < 30; i++) {
              if (i < server.arg(a).length())
                network_password[i] = server.arg(a)[i];
              else
                network_password[i] = '\0';
            }
          } else if (server.argName(a) == "ip1" && checkIPsyntax(server.arg(a))) {
            memset(host1, 0, sizeof(host1));
            for (int i = 0; i < server.arg(a).length(); i++)
              host1[i] = server.arg(a)[i];
            got_new_hosts = true;
            Serial.print(" OK1 ");
          } else if (server.argName(a) == "ip2" && checkIPsyntax(server.arg(a))) {
            memset(host2, 0, sizeof(host2));
            for (int i = 0; i < server.arg(a).length(); i++)
              host2[i] = server.arg(a)[i];
            got_new_hosts = true;
            Serial.print(" OK2 ");
          } else if (server.argName(a) == "ip3" && checkIPsyntax(server.arg(a))) {
            memset(host3, 0, sizeof(host3));
            for (int i = 0; i < server.arg(a).length(); i++)
              host3[i] = server.arg(a)[i];
            got_new_hosts = true;
            Serial.print(" OK3 ");
          } else
            Serial.print(" NACK; ");
        }
      } else Serial.print("no args");
      Serial.println(".");
    
      // Save it
      if (got_new_hosts)
        writeIPtoEEPROM();
    
      if (save_wifi) {
        writeSSIDtoEEPROM(network_ssid);
        if (temp_password != "notchanged")
          writePASSWORDtoEEPROM(network_password);
      }
    
      // Print the HTML-Page
      String result = "<html><head><title>PingCheck</title></head>\r\n<body>\r\n";
      result += "PingCheck 2.0 (2015 Marcel Imig <a href=\"http://www.frag-duino.de\">www.frag-duino.de</a>)<br><br>\r\n\r\n";
      result += "<fieldset name=\"wifistatus\"><legend>Wifi status</legend><form action=\"\" method=\"post\">";
    
      result += "<table>\r\n";
    
      result += "<tr><td><b>Network SSID:</b></td>\r\n";
      result += "<td><input type=\"text\" name=\"ssid\" value=\"";
      result += network_ssid;
      result += "\" maxlength=\"30\"></td>\r\n</tr>\r\n";
      result += "<tr><td><b>Network Password:</b></td>\r\n";
      result += "<td><input type=\"password\" name=\"password\" value=\"notchanged\" maxlength=\"30\"></td>\r\n</tr>\r\n";
      result += "</td>\r\n</tr>\r\n</table>\r\n";
    
      result += "<input type=\"Submit\" name=\"send\" value=\"Save-Wifi\" >\r\n";
      result += "<input type=\"Submit\" name=\"send\" value=\"Restart\" ></form></fieldset>\r\n\r\n<br>\r\n<br>\r\n";
    
      result += "<fieldset name=\"hosts\"><legend>Hosts</legend>\r\n<b>Monitoring IPs:</b><br><br>\r\n\r\n";
      result += "<form action=\"\" method=\"post\">";
      result += "IP1: <input type=\"text\" name=\"ip1\" value=\"";
      result += host1;
      result += "\" ";
      if (host_is_online[0])
        result += "style=\"background-color:green\"";
      else
        result += "style=\"background-color:red\"";
      result += " maxlength=\"15\"><br>\r\n";
    
      result += "IP2: <input type=\"text\" name=\"ip2\" value=\"";
      result += host2;
      result += "\" ";
      if (host_is_online[1])
        result += "style=\"background-color:green\"";
      else
        result += "style=\"background-color:red\"";
      result += " maxlength=\"15\"><br>\r\n";
    
      result += "IP3: <input type=\"text\" name=\"ip3\" value=\"";
      result += host3;
      result += "\" ";
      if (host_is_online[2])
        result += "style=\"background-color:green\"";
      else
        result += "style=\"background-color:red\"";
      result += " maxlength=\"15\"><br>\r\n";
      result += "<input type=\"Submit\" name=\"send\" value=\"Save\"> \r\n";
      result += "<input type=\"Submit\" name=\"send\" value=\"Refresh\">\r\n\r\n</form>\r\n";
      result += "</body>\r\n</html>";
      server.send(200, "text / html", result);
    }
    
    void handleDump() {
      Serial.println("EEPROM:");
      for (int i = 0; i < EEPROM_SIZE; i++) {
        Serial.print(i);
        Serial.print(": ");
        Serial.println((char) EEPROM.read(i));
      }
      Serial.println("END");
      server.send(200, "text / html", "DUMPED ON SERIAL OUTPUT");
    }
    
    
    void setup() {
      // Initialize Serial and LEDs
      Serial.begin(115200);
      delay(20);
      Serial.println("\r\n Ready ");
      pinMode(PIN_LED, OUTPUT);
      digitalWrite(PIN_LED, HIGH);
    
      // Read IPs and Wifi-credentials from EEPROM
      EEPROM.begin(EEPROM_SIZE);
    
      if (RESET_EEPROM) { // First start, initialize EEPROM
        strncpy(host1, "192.168.0.1", 11);
        strncpy(host2, "0.0.0.0", 7);
        strncpy(host3, "0.0.0.0", 7);
    
        writeSSIDtoEEPROM(WIFI_SSID);
        writePASSWORDtoEEPROM(WIFI_PASSWORD);
        writeIPtoEEPROM();
    
        Serial.println("EEPROM initialized");
        while (true)
        {
          digitalWrite(PIN_LED, HIGH);
          delay(100);
          digitalWrite(PIN_LED, LOW);
          delay(100);
        }
      } else {
        // Read from EEPROM
        for (int i = 0; i < 15; i++) {
          host1[i] = EEPROM.read(ADDRESS_IP1 + i);
          host2[i] = EEPROM.read(ADDRESS_IP2 + i);
          host3[i] = EEPROM.read(ADDRESS_IP3 + i);
        }
        for (int i = 0; i < 30; i++) // SSID
          network_ssid[i] = EEPROM.read(ADDRESS_SSID + i);
        for (int i = 0; i < 30; i++) // Password
          network_password[i] = EEPROM.read(ADDRESS_PASSWORD + i);
      }
    
      // Print hosts
      Serial.println("Hosts from EEPROM:");
      Serial.print("[1]: ");
      Serial.println(host1);
      Serial.print("[2]: ");
      Serial.println(host2);
      Serial.print("[3]: ");
      Serial.println(host3);
      Serial.print("Wifi from EEPROM: \"");
      Serial.print(network_ssid);
      Serial.print("\" \"");
      Serial.print(network_password);
      Serial.println("\"\r\n");
    
      // Connect to AP
      WiFi.mode(WIFI_STA); // Set to client
      WiFi.begin(network_ssid, network_password);
    
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println(".");
        if (state_LED == HIGH)
          state_LED = LOW;
        else
          state_LED = HIGH;
        digitalWrite(PIN_LED, state_LED);
      }
    
      Serial.print("Connected to ");
      Serial.println(network_ssid);
      Serial.print("IP address: ");
      network_ip = WiFi.localIP();
      Serial.println(network_ip);
    
      // Start the server
      server.on("/", handleRoot);
      server.on("/dump/", handleDump);
      server.begin();
    
      digitalWrite(PIN_LED, HIGH);
      delay(250);
      digitalWrite(PIN_LED, LOW);
    }
    
    void loop() {
      server.handleClient();
      currentMillis = millis(); // Read current runtime
    
      if (WiFi.status() != WL_CONNECTED) {
        Serial.println("Not connected anymore, restarting");
        system_restart();
      }
    
      if (last_ping + PING_INTERVAL < currentMillis) {
        Serial.print(currentMillis);
        const char* host;
    
        currentHost = (currentHost + 1) % count_hosts;
        if (currentHost == 0)
          host = host1;
        else if (currentHost == 1)
          host = host2;
        else
          host = host3;
    
        uint32 address = ipaddr_addr(host);
        if (host[0] == '0' && host[2] == '0' && host[4] == '0' && host[6] == '0')
          Serial.println(": Skipping");
        else {
          Serial.print(": Pinging \"");
          Serial.print(host);
          Serial.print("\" - ");
          ping(address);
          last_ping = currentMillis;
        }
      }
    
      if (last_seen_online + (count_hosts * PING_INTERVAL) > currentMillis)
        state_LED = HIGH;
      else
        state_LED = LOW;
    
      digitalWrite(PIN_LED, state_LED);
    }
    
    

     

  3. Baut nun die folgende Schaltung auf:

  4. Um dem Modul einen stabilen Standfuß zu geben, habe ich dicke Muttern auf der Basisplatte aufgeklebt:

  5. Um das ganze zu befestigen, habe ich es auf einer Metallplatte mit einem Kabelbinder befestigt und diese an einem Schrank festgeschraubt:

  6. Wenn man dann ein Loch in den Deckel bohrt, kann man dort eine Acrylstange durchstecken:

    Um sie zu biegen, benötigt man einen Heißluftfön. Dabei muss man darauf achten, dass man den Strahl nichtzu lange auf dieselbe Stelle richtet. In diesem Fall kommt es  nämlich zu Blasenbildung.

  7. Die Konfiguration erfolgt über das Webinterface. Dort kann man angeben, welche Hosts zu überwachen sind:

     

  8.  Das Modul ist so rubust programmiert, dass es bei Verlust der WLAN-Verbindung neustartet und die Verbindung wiederherstellt.

 

Kommentare  

#1 Marko Ban 2016-10-14 12:31
Perfekt.
Super und danke.

Würde mich über eine kleine Hilfe freuen.
Habe den ESP8266 ESP-12.

Wie kann ich jeder IP einen anderen GPIO("LED") zuordnen?

Gruß,
Marko
#2 Marcel I. 2016-10-23 09:49
Hallo Marko,

klar geht das. Aber nicht mit dem Code oben ;)

Spontan fallen mir zwei Wege ein um das zu lösen:
1. Du hast ja die Variable currentHost. Du kannst ein Array an Pins definieren, welches so groß ist wie die Anzahl der Hosts. Dann lässt du die entsprechende LED aufleuchten.
2. Du analysierst das ECHO_REPLY Paket (Die Antwort vom Ping). Dort müsste die Source IP stehen vom den Host der geantwortet hat. Das ist etwas sicherer, aber komplizierter.