Selbstgemacht:

Webserver mit Arduino


Features:

  • Abfrage von Sensoren über das LAN / Internet
  • Schalten von Relais und LEDs aus der Ferne
  • Temperatur abfragen
  • Helligkeit abfragen

Bild 1: Oben ist das Ethernet-Shield und das Steckbrett zu erkennen, darunter die Platine mit dem Atmel-Prozessor. Der Ethernet-Shield wird im Betrieb auf die Prozessorplatine aufgesteckt.

Sensoren abfragen, Leuchtdioden oder Relais ein- bzw. ausschalten sind typische Aufgaben für einen Mikroprozessor. Soll das über das lokale Netzwerk oder gar das Internet funktionieren, ist eine Anbindung des Prozessors an das LAN via Ethernet erforderlich. Besonders einfach lässt sich das mit der quelloffenen Arduino Soft- und Hardware realisieren, wie dieses Beispiel zeigt. Weiterer Vorteil: Kein Löten, auch sind keine Detailkenntnisse der Programmierung erforderlich. Dieser Beitrag soll dazu anregen, das hier gezeigte Programm zu erweitern und den eigenen Wünschen anzupassen.

Arduino (arduino.cc) ist die Bezeichnung für ein abgestimmtes Konzept von Hard- und Software. Dazu gehört die Hardware, das sind beispielsweise Platinen mit Prozessor und eine Reihe aufsteckbaren Zusatzplatinen, die shields genannt werden. Arduino ist aber auch die Bezeichnung für die dazugehörende Software zur Entwicklung von Programmen mit einer vereinfachten Syntax der Sprache "C". Diese Entwicklungsumgebung (IDE) baut auf der IDE namens "Processing" auf. Die damit erstellten Programme nennt man auch Sketches.

Zur Realisierung kommt eine Prozessorplatine mit der Bezeichnung "Duemilanove" bzw. einem dazu kompatiblen Klon ("Freeduino") mit dem Prozessor Atmel ATMEGA168 zum Einsatz. Weil diese Platine den Anschluss an das Ethernet vermissen lässt, stecken wir obenauf ein Original Arduino Ethernet-Shield, also eine Zusatzplatine mit einem Ethernet-Chip, die uns die Verbindung zum lokalen Ethernet-Netzwerk und auch dem Internet ermöglicht. Diese kleine und recht preiswerte Ausstattung werden wir um einige wenige Bauteile erweitern, die wir auf einer Steckplatine ohne Lötarbeit verdrahten. Das Aufmacherfoto zeigt, wie es später aussieht. Damit kommen wir zu den Sensoren für Licht und Temperatur sowie zu den Leuchtdioden.

Bauteile auf dem Steckbrett

Das Schaltbild ist sehr übersichtlich: Die beiden LED werden über einen Vorwiderstand mit Spannung versorgt. Die Kathode von LED1 ist an D6 (digitaler Ausgang 6) der Ethernet-Platine, die andere LED an D2 abgeschlossen. Für 2mA-LED wird ein Vorwiderstand von 1200 Ohm verwendet. Der Port des Prozessors wirkt als Stromsenke: Ist der Prozessor-Port D2 bzw. D6 high, fließt kein Strom, die LED ist aus. Wird ein Port auf Low gelegt, leuchtet die betreffende Leuchtdiode. Liegt also z. B. D2 auf HIGH-Pegel, leuchtet die LED nicht, da Anode und Kathode auf demselben Potenzial liegen (5V). Erst wenn der digitale Ausgang D2 auf LOW (0 Volt) geschaltet wird, kann Strom fließen und die LED wird leuchten. Eine LED wird dazu benutzt, anzuzeigen, ob der Webserver gerade arbeitet (Busy-Funktion), die andere kann aus der Ferne ein- bzw. ausgeschaltet werden.

Bild 2: Wenige Bauteile auf dem lötfreien Steckbrett ermöglichen u. a. die Abfrage der Lichtstärke und Temperatur.

Die Lichtmessung erfolgt mit einem lichtempfindlichen Widerstand (LDR). Fällt auf ihn viel Licht, nimmt sein Widerstand ab, daher sinkt die Spannung am analogen Eingang AIN5 des Prozessors. Kleine Werte bedeutet daher viel Licht und umgekehrt. Auf dem Ethernet-Shield und dem Arduino-Prozessorboard ist der Anschluss AIN5 einheitlich mit A5 bezeichnet. Zur Temperaturmessung wird der IC LM335 verwendet. Er liefert eine der Temperatur entsprechende Ausgangsspannung. Über einen Adjust-Pin lässt sich eine Referenzspannung anschließen, was hier simpel über ein Potentiometer oder besser einen Trimmer geschieht. Doch wozu den Adjust-Anschluss beschalten? Exemplarstreuungen der LM335 bewirken einen unterschiedlichen Gleichspannungsanteil. Die Temperatur-Spannungskurve selbst ist weitgehend linear, doch der Gleichspannungsanteil (engl.: offset) wird die gemessenen Temperaturen nach oben oder unten verschieben. Über die Referenzspannung an dem Adjust-Anschluss lässt sich dieser Exemplarfehler korrigieren. Praktisch bedeutet das: Sollte die gemessene Temperaturmessung ein wenig zu hoch oder zu niedrig angezeigt werden, ist der Trimmer so einzustellen, dass sich die korrekte Temperatur ergibt.

Die Messung der vom LM335 abgegebenen Spannung erfolgt am Prozessor am Anschluss AIN0. Er ist sowohl auf dem Ethernet-Shield, als auch auf der Arduino-Platine jeweils mit A0 bezeichnet.

Bild 3: Der Temperatursensor LM335 wird im Transistorgehäuse TO92 geliefert und weist einen Adjust-Pin zur Korrektur der gemessenen Temperatur auf. Die Grafik zeigt die drei Anschlüsse mit Sicht von unten.

Und nun - die Software

Wie wir gesehen haben, ist die Schaltung auf dem Steckbrett wenig anspruchsvoll - wenden wir uns also der Software zu: Was muss sie leisten? Zunächst einmal ist es die Aufgabe des Webservers, bei Anfrage eines Webclients (also eines Internet-Browsers) eine Webseite zu erzeugen und an den Webclient zu senden. In dieser Webseite (HTML-Seite) sollen die erfassten Werte der analogen Eingänge, die Temperatur und der Status der LED ablesbar sein. Darüber hinaus soll es eine Möglichkeit geben, die LED zu schalten. Betrachten wir einige Bereiche des Programms, damit Sie es leichter den eigenen Wünschen anpassen und erweitern können:

Bild 4: So stellt sich der Webserver dem Anwender dar. Um eine LED zu schalten, klickt man auf die Checkbox LED1 ON und anschließend auf "Absenden".

      C-Code:
      #include         // wird benötigt für Ethernet shield
      #include               // wird benötigt für Ethernet.h
      #include         // Bibliothek für Temperatursensor LM335 einbinden
      #include     // ermöglicht  Variablen und Strings im Flash-Speicher

Das Programm importiert zunächst einige Definitionen, die später benötigt werden, etwa Ethernet.h und SPI.h für das Ethernet, lokales Netzwerk (LAN) und Internet und die zweite Header-Datei für die Kommunikation zwischen Prozessorplatine und der Ethernet-Aufsteckplatine. Die Bibliothek LM335A enthält die Software zur Abfrage des Temperatursensors. Die letzte Definition ermöglicht es dem Programm, Texte und Variablen nicht nur im dafür vorgesehenen SRAM, sondern auch im Programmspeicher abzulegen. Das ist nötig, weil die Webseite viel Text enthält (HTML-Code). Dieser würde nicht in das schnelle SRAM passen, denn es ist nur 1 KByte groß. Der Platz für Variable wäre auch zu schade, ihn für simple Texte zu verschwenden.

Die Tatsache, dass der für die Webseite zu erzeugende HTML-Code (zum Teil) nicht im SRAM, sondern im Programmspeicher abgelegt wird, wird mit dem Wort "PROGMEM" angezeigt:</p>

      PROGMEM prog_char hr[]  = {"<hr color=\"darkgreen\">"};

Hier handelt es sich also um die Variable "hr" des Typs Prog_char, einem Zeichenarray im Programmspeicher, dessen Inhalt in HTML ausgedrückt eine dunkelgrüne, horizontale Linie erzeugt. Sie wird später so in den HTTP-Sendepuffer übertragen und versandt:

      strcpy_P(sendbuffer, hr);  client.println(sendbuffer);

Der Sendepuffer enthält jeweils einen Teil der dynamisch erzeugten HTML-Seite und wird mit client.println() an den Benutzer (Webclient) versandt. Doch das war schon zu weit vorgegriffen, dazu später mehr. Kommen wir auf den Anfang des Programms zurück.

Von MACs und IPs

Jedem Gerät in einem Netzwerk ist eine MAC-Adresse (MAC = Media-Access-Control) zugeordnet. Sie dient zur Identifikation des Gerätes innerhalb des Netzwerkes, ähnlich, wie ein Fingerabdruck ein Individuum identifiziert. Auch das Ethernet-shield macht dabei keine Ausnahme. Von dem Aufkleber, der unten auf der Platine klebt, schreiben wir die MAC-Adresse ab:

      byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x9D, 0xB5 };

Wenn wir eine HTML-Seite aufrufen, benötigen wir eine Internet-Adresse. Ein Beispiel wäre www.amateurfunkbasteln.de oder www.vth.de. Hinter dieser für Menschen gut lesbaren Adresse steckt jedoch eine durch drei Punkte unterteilte Zahlenkombination, welche die reale Internet-Adresse bildet, mit der das Netzwerk etwas anfangen kann. Bei Aufruf von www.vth.de wird die Internet-Adresse 217.29.42.49 aufgerufen. Tippen Sie diese Zahlen-Punkte-Kombination anstatt www.vth.de als Adresse in den Browser ein, gelangen Sie ebenfalls zur Webseite des Verlages. Auch unser Arduino benötigt eine Internetadresse und die ist abhängig von der Adresse des Netzwerkes. Eine beliebte Adresse für ein lokales Netzwerk ist z. B. 192.168.1.1 für den DSL-Router und dann folgen die PC des Netzwerkes, z. B. 192.168.1.2, 192.168.1.3 und so fort. Unserem Arduino könnten wir z.B. folgende Adresse verpassen:

      byte ip[] = { 192, 168, 1, 155 };

Er wird damit in dem Netzwerk 192.168.1.x unter der Adresse 192.168.1.155 erreichbar. Und weiter geht's: Zunächst erzeugen wir ein Server-Objekt, dem wir gleich die Port-Nummer mit auf den Weg geben. Auf diesen Port wird der Server lauschen, ob ihn Anfragen interessierter Benutzer erreichen. Für das World Wide Web (WWW), das auf dem Protokoll HTTP basiert, ist es immer der Port 80. Aber dort steht doch "81"? Richtig, aber nur deshalb, will es "Micha's 2. Webserver" ist. Es gibt (in diesem speziellen Netzwerk) schon einen anderen Webserver, der auf Port 80 hört. Sind jedoch zwei HTTP-Server in einem Netzwerk vorhanden, dürfen sie nicht dieselben Ports bedienen. Sie würden also in ihrem Programm den Wert"80" eintragen, wenn Sie nicht bereits einen anderen Webserver betreiben!

      EthernetServer server(81);

In der Funktion Setup() geschieht all das, was zu Beginn des Programms passieren soll:

      void setup()
      {
        pinMode(LedBusy,OUTPUT);     // LED-Port auf Ausgabe schalten
        pinMode(Led1,OUTPUT);

        digitalWrite(LedBusy,HIGH);  // LED aus
        digitalWrite(Led1,HIGH);     // LED aus

        Ethernet.begin(mac, ip);     // Ethernet aktivieren
        server.begin();
      }

Der Befehl pinMode legt fest, ob ein digitaler Ein-Ausgang des Prozessors als Eingang (INPUT) oder als Ausgang (OUTPUT) fungieren soll. Die Prozessor-Pins, an denen die beiden LED angeschlossen sind, werden also als Ausgänge geschaltet. Mit digitalWrite(LED.HIGH) schalten wir die beiden LED aus. Nun wird das Ethernet aktiviert - was dort im Detail passiert ist recht komplex und muss uns als Arduino-Programmierer glücklicherweise nicht en Detail interessieren. Auch der Webserver wird initialisiert und ist nun fertig zur Benutzung.

Hinein geht es in die große Programmschleife Loop(), die immer wieder durchlaufen wird, solange an der Platine Spannung anliegt.

         void loop()
         {
          EthernetClient client = server.available();
          if (client)   // wenn ein Client anfragt...
          {
          …
          }
         …
         }

HTTP-Request auswerten

Wenn client den Wert TRUE hat (oben: if (client)), fragt ein Webclient bei dem Server an. Der Internet-Browser sendet dann folgenden Text an den Webserver:

      GET / HTTP/1.1
      Host: 192.168.2.155:81

Der Server sendet daraufhin seine HTML-Seite, die der Browser auf dem Bildschirm schließlich anzeigt. Klickt der Anwender auf die Checkbox LED1 ON und anschließend auf den Absenden-Button, schickt der Browser folgenden Text an den Server:

      GET /?LED1=ON1 HTTP/1.1
      Host: 192.168.

Oder, wenn er die LED ausschaltet:

      GET /?LED1=OFF1 HTTP/1.1
      Host: 192.168

In jedem Fall sendet der Server den Text der Webseite an den Webclient. Es geschieht aber noch mehr: Der von Webclient empfangene Text - die ersten 40 Zeichen - werden in der Variable readbuffer abgelegt und auf dessen Inhalt untersucht.


    // Nach "N1" suchen - bedeutet ON1 = Led1 = ON
    for(int count = 0; count < 39; count++)
    {
      // ON1 ?
      if (readbuffer[count] == '1' && readbuffer[count-1] == 'N' )
        {
          digitalWrite(Led1,LOW);  // an
        }

      // Nach F1 suchen = OFF1 - bedeutet Led1 = OFF
      if (readbuffer[count] == '1' && readbuffer[count-1] == 'F' )
         {
           digitalWrite(Led1,HIGH);  // aus
         }
      }

In obigem Block wird nach dem Text "N1" als Teil von "?LED1=ON1" gesucht, das bedeutet: Die LED1 soll eingeschaltet werden. Ist im readbuffer der Text "F1" enthalten, wird die LED1 ausgeschaltet. Dabei ist die Abfrage etwas umständlich, da auf jedes Zeichen explizit geprüft wird. Arduino-C verfügt zwar über leistungsstarke Funktionen für die Verarbeitung von Zeichenkennen (Texte), doch bei mir bereitete die String-Klasse Speicherprobleme, wenn Variablen im Programmspeicher gehalten werden. Daher musste auf die Nutzung der String-Klasse verzichtet werden.

Was geschieht im weiteren Verlauf des Programms? Nach Schalten der LED wird Lichtstärke und Temperatur abgefragt, berechnet und sodann die Webseite an den Webclient geschickt. Das Zusammenbasteln der HTML-Zeilen für die Werte der analogen Eingänge sieht in C z.B. so aus:

          client.print("<p>");
          for (int i = 0; i < 6; i++) {
            client.print("Analoger Eingang ");
            client.print(i);
            client.print(" : ");
            client.print(analogRead(i));
            client.println("<br>");          // Ende der Zeile
          }
          client.print("</p>");

Der Webclient empfängt dann folgende HTML-Zeilen:

      <p>
      Analoger Eingang 0 : 603<br>
      Analoger Eingang 1 : 474<br>
      Analoger Eingang 2 : 422<br>
      Analoger Eingang 3 : 394<br>
      Analoger Eingang 4 : 402<br>
      Analoger Eingang 5 : 77<br>
      </p>

Wie zu erkennen ist, werden die Zeilen dynamisch erzeugt. Wie bereits erwähnt, wurden größere Texte aus Platzgründen in den Arbeitsspeicher ausgelagert, um das SRAM zu schonen. Einen Text aus dem Programmspeicher kopieren wir so in den Sendepuffer:

      // Formular ab hier
      strcpy_P(sendbuffer, form1);  client.println(sendbuffer);
      strcpy_P(sendbuffer, form2);  client.println(sendbuffer);
      strcpy_P(sendbuffer, hr);  client.println(sendbuffer);

Obige drei Zeilen erzeugen dann folgenden HTML-Output:

      <input type="radio" name="LED1" value="ON1">LED1 ON<br>
      <input type="radio" name="LED1" value="OFF1">LED1 OFF<br>
      <hr color="darkgreen">

Es handelt sich dabei um den HTML-Code der beiden Checkboxen, mit denen ein Benutzer die LED1 ein- bzw. ausschalten kann.

Damit ist das Programm weitgehend besprochen. Der Sourcecode und die C-Bibliothek für den Temperatursensor LM335A ist auf der Heft-DVD im Verzeichnis "Artikel" abgelegt und sind ausführlich dokumentiert. Betrachten Sie das Programm in seiner Gänze, wird sich die Funktionsweise schnell nachvollziehen lassen. Dann sollten persönliche Ergänzungen oder Änderungen schnell umsetzbar sein.

Das hier vorgestellte Programm läuft zwar auf einem (älteren) Duemilanove (ital.: für "2009") mit dem ATMega168, der 16 KB Programmspeicher aufweist. Doch der kleine Prozessor ist jetzt schon gut gefüllt. Man kann den Duemilanove auch mit dem größeren ATMega328P ausstatten und verfügt dann über den doppelten Programmspeicher (32 KB). Die bessere Alternative ist jedoch der Arduino UNO, er wird bereits mit dem ATMega328P geliefert und der Upload des Programms ist schneller als beim "alten" Duemilanove.

Persönlich arbeite ich gern mit den Arduinos, deren Prozessoren im DIL-Gehäuse im 28-poligen Sockel stecken. Ein Prozessor lässt sich etwa tausend Mal programmieren, bevor er unbrauchbar wird. Dann ziehe ich den defekten ATMega aus dem Sockel und ersetze ihn durch einen neuen mit Bootloader. Bei Platinen mit Prozessoren im SMD-Gehäuse ist das nicht so leicht möglich.

Und nun ins Internet

Bisher haben wir unseren kleinen Webserver mit einer lokalen IP-Adresse ausgestattet und so ist er auch nur im lokalen Netzwerk (LAN) über die Adresse 192.168.1.155 erreichbar. Man startet den eigenen PC, dann den Internet-Browser und tippt in das Adressfeld die IP-Adresse ein. Möchten Sie aus der Ferne, beispielsweise im Urlaub, die Temperatur von Zuhause abfragen, muss das LAN so eingestellt werden, dass ein Zugriff aus dem Internet auf das LAN und damit den Webserver erfolgen kann. Dabei gehen wir davon aus, dass das LAN über einen permanent eingeschalteten DSL-Router mit dem Internet verbunden ist. Leider ergibt sich das Problem, dass sich die IP-Adresse des DSL-Routers im Internet verändern kann - manche Internet-Anbieter weisen den DSL-Routern sogar jeden Tag eine neue IP-Adresse zu. Ohne weiteres ist das private LAN wegen der sich ändernden IP-Adresse also nicht aus dem Internet erreichbar.

Die meisten DSL-Router verfügen jedoch über die Möglichkeit, mit einem dynamischen DNS-Dienst Kontakt aufzunehmen, z. B. mit DynDNS.org oder ähnlichen Dienstanbietern. Eine Liste gibt es bei http://netzadmin.org/ddns-provider.php. Ist man bei einem der DNS-Dienste angemeldet, passiert folgendes: Ändert der Provider die IP-Adresse des DSL-Routers, teilt der DSL-Router diese Veränderung umgehend dem DNS-Dienstanbieter mit und der weiß nun, dass er diese IP-Adresse beispielsweise auf die URL "rufzeichen.dyndns.org" umleiten soll. Damit ist das private LAN immer unter dieser URL erreichbar, unabhängig davon, welche IP-Adresse gerade dem LAN vergeben wurde.

Weitere Informationen zu DynDNS bietet das Wikipedia http://de.wikipedia.org/wiki/DynDNS, Im Internet existieren zahlreiche detaillierte Anleitungen zum Einrichten des dynamischen DNS.

Bild 5: Statt einer LED kann auch zuerst ein 6-Volt- und dann ein Koppelrelais geschaltet werden. Die Abbildung zeigt je ein 6-Volt-Relais, ein 12-Volt-Relais und ein Koppelrelais für 230 Volt Wechselspannung, angesteuert mit 24 Volt Gleichspannung.

Fazit

Arduino-C und die C-Bilbiotheken nehmen dem Programmierer eine ganze Menge Arbeit ab, so dass er sich völlig auf seine Aufgabenstellung konzentrieren kann. Oft sind bereits ähnliche Programme vorhanden, die man nur etwas anpassen muss. Im Fall eines Webservers sind zudem einige Grundkenntnisse des HTTP-Protokolls und der HTML-Beschreibungssprache erforderlich.


Download von Dateien zu diesem Projekt

Schaltbild, Arduino-Sketch (Programm), Hexdatei zum Brennen des Prozessors und anderes stehen zum Download bereit. Und hier geht es zum Download.