Laden Sie also von der Webseite des Arduino-Projekts [4] die für das verwendete Betriebssystem passende Version der Arduino IDE herunter. Zur Installation folgen Sie der Anleitung. Nach dem Start der IDE binden Sie die Bibliothek zur Software-Unterstützung des Controllino ein. Klicken Sie dazu auf Sketch | Bibliothek einbinden | Bibliotheken verwalten. Hier suchen Sie nun nach Controllino. Ein Klick auf Install richtet die Bibliothek ein.
Es erfordert zwei Schritte, die Hardware-Unterstützung für den Controllino einzubinden. Als Erstes tragen Sie unter Datei | Voreinstellungen eine zusätzliche Boardverwalter URL [5] ein (Abbildung 4). Als Zweites navigieren Sie über Werkzeuge | Board | Boardverwalter zum Board-Verwalter, suchen dort nach Controllino und installieren das Board.

Abbildung 4: Um die Hardware-Unterstützung in Arduino zu aktivieren, tragen Sie die Boardverwalter URL ein.
Danach wählen Sie unter Werkzeuge bei Board den Controllino maxi an. Der Port unter Werkzeuge | Port sollte automatisch richtig gesetzt sein. Stellt Ihr System mehrere Ports zur Auswahl, wählen Sie /devt/tyACM0 aus. Jetzt steht die IDE bereit, um Programme für den Controllino zu erstellen.
Das Programm
Das Programm basiert auf dem Webserver-Beispielprogramm aus der Arduino IDE, das Sie unter Datei | Beispiele | Ethernet | WebServer finden. Da die IDE selbst bereits viele Teile des Codes erklärt, gehen wir hier nicht gesondert darauf ein. Um den Webserver über das Netz zu erreichen, ändern Sie die IP-Adresse im Programm (Listing 1) so, dass sie zu Ihrem Netzwerk passt. Wir sehen uns zunächst die einfachen Teile des Programms an und gehen dann immer weiter in die Tiefe.
Das Arduino-Projekt teilt sich in zwei Dateien auf. In der einen (Listing 1) befindet sich der eigentliche Webserver, die zweite (Listing 2) enthält die HTML-Seiten. Sie sollten sich immer angewöhnen, den Quellcode frühzeitig in mehrere Dateien aufzuteilen – das gestaltet ihn übersichtlicher.
Listing 1
#include <SPI.h>
#include <Ethernet.h>
#include <Controllino.h>
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(192, 168, 0, 177);
EthernetServer server(80);
#define DEBUG
void setup() {
Ethernet.begin(mac, ip);
server.begin();
#ifdef DEBUG
Serial.begin(9600);
while (!Serial) {delay(1);}
Serial.println("WebServer up");
#endif
pinMode(CONTROLLINO_A0, INPUT);
pinMode(CONTROLLINO_A1, INPUT);
pinMode(CONTROLLINO_R0, OUTPUT);
}
void loop() {
if((digitalRead(CONTROLLINO_A0)==HIGH) & (digitalRead(CONTROLLINO_A1)==HIGH)){
digitalWrite(CONTROLLINO_R0, HIGH);
}
if(digitalRead(CONTROLLINO_A1)==LOW){
digitalWrite(CONTROLLINO_R0, LOW);
}
EthernetClient client = server.available();
String request="";
if (client) {
#ifdef DEBUG
Serial.println("Connect");
#endif
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
#ifdef DEBUG
Serial.print(c);
#endif
request += c;
if (c == '\n' && currentLineIsBlank) {
if (request.indexOf("GET / ") > -1) {home(client,request);}
if (request.indexOf("GET /ajax/on") > -1) {ajaxOn (client,request);}
if (request.indexOf("GET /ajax/off") > -1) {ajaxOff(client,request);}
if (request.indexOf("GET /ajax/status") > -1) {ajaxStatus(client,request);}
break;
}// if currentLineIsBlank
if (c == '\n') {
currentLineIsBlank = true;
} else if (c != '\r') {
currentLineIsBlank = false;
}
} // if client
} //while
delay(1);
client.stop();
} // if client
} //loop
Listing 2
void header(EthernetClient client,String request,String content) {
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("<head>");
client.println("<meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>");
client.println("<script>");
client.println("function load(element,endPoint) { ");
client.println(" var xhttp = new XMLHttpRequest(); ");
client.println(" xhttp.onreadystatechange = function() {");
client.println(" if (this.readyState == 4 && this.status == 200) {");
client.println(" document.getElementById(element).innerHTML = this.responseText;");
client.println(" }");
client.println(" }");
client.println(" xhttp.open('GET', endPoint, true);");
client.println(" xhttp.send();");
client.println("}");
client.println("function autoRefresh(){");
client.println(" load('data','/ajax/status');");
client.println("}");
client.println("function init(){");
client.println(" if (typeof lock == 'undefined'){setInterval('autoRefresh()', 2000);lock=1;}");
client.println("}");
client.println("</script>");
client.println("</head>");
client.println("<body onload='init()'>");
client.println(content);
client.println("</body>");
client.println("</html>");
}// header
void home(EthernetClient client,String request) {
String content ="";
content+="<style>";
content+=".box1 { border-radius: 5px; border: 2px solid black; width: 80px; }";
content+=".text1 { fill: black; font: bold 16px sans-serif; }";
content+=".text2 { fill: white; font: bold 16px sans-serif; }";
content+="</style>";
content+="<div class='box1'>";
content+="<div id='data' >";
content+=status();
content+="</div>";
content+="<div onclick=load('data','/ajax/off')>";
content+="<svg width=80 height=80 xmlns='http://www.w3.org/2000/svg' version='1.1'><circle cx=40 cy=40 r=30 fill=red stroke=black stroke-width=2 />";
content+="<circle cx=40 cy=40 r=15 fill=red stroke=white stroke-width=4 /></svg>";
content+="</div>";
content+="<div onclick=load('data','/ajax/on')>";
content+="<svg width=80 height=80 xmlns='http://www.w3.org/2000/svg' version='1.1'><circle cx=40 cy=40 r=30 fill=green stroke=black stroke-width=2 />";
content+="<line x1=40 y1=22 x2=40 y2=58 stroke=white stroke-width=4 /></svg>";
content+="</div>";
content+="</div>";
header(client,request,content);
}
void ajaxOn(EthernetClient client,String request) {
digitalWrite(CONTROLLINO_R0, HIGH);
client.println(status());
}
void ajaxOff(EthernetClient client,String request) {
digitalWrite(CONTROLLINO_R0, LOW);
client.println(status());
}
void ajaxStatus(EthernetClient client,String request) {
client.println(status());
}
String status(){
String status="<svg width=80 height=95 xmlns='http://www.w3.org/2000/svg' version='1.1'>";
if (digitalRead(CONTROLLINO_R0)==1){
status += "<circle cx=40 cy=60 r=30 fill=#0000ff stroke=black stroke-width=2 />";
status += "<text x=30 y=65 class=text2>an</text>";
} else {
status += "<circle cx=40 cy=60 r=30 fill=#ffffff stroke=black stroke-width=2 />";
status += "<text x=25 y=65 class=text1>aus</text>";
}
status += "<text x=10 y=20 class=text1>Pumpe</text>";
status+="</svg>";
return status;
}
Tuning-Maßnahmen
Kommen wir nun zum ersten interessanten Punkt, der Debug-Ausgabe. Um die Ausgaben zu (de-)aktivieren, wählte der Autor den Weg über den C-Präprozessor (#define DEBUG). Das bietet den Vorteil, dass sich kein überflüssiger Code im Mikrocontroller befindet, wenn man das Programm ohne Debug-Informationen erzeugt. Im Internet findet man häufig Code, der die Ausgabe über eine Variable und If-Abfragen vor dem Debug-Output steuert. Diese Vorgehensweise bläht den Code unnötig auf. Bedenken Sie, dass der Mikrocontroller nur sehr begrenzte Ressourcen bietet.
Das bringt uns zum nächsten Punkt, den man beachten sollte. Die hier verwendete Ethernet-Bibliothek schickt mit jedem client.println ein einzelnes IP-Paket auf die Reise. Bei einfachen Programmen fällt das kaum auf, bei komplexeren macht sich das hingegen schnell durch lange Wartezeiten bemerkbar.
Achten Sie daher darauf, den Datenverkehr zwischen Webbrowser und Mikrocontroller möglichst gering zu halten. Das erreichen Sie etwa mit dem Einsatz von AJAX, dessen Funktionsweise der Kasten “AJAX” beschreibt. Alternativ könnten Sie eine eigene Funktion schreiben, die bei der Übertragung von Daten die IP-Pakete so weit wie möglich auffüllt. Das wird dann aber wirklich kniffelig.
AJAX
AJAX (Asynchronous Javascript and XML) erlaubt es, Daten zwischen einem Browser und einem Webserver auszutauschen, ohne dabei einen HTTP-Request auszuführen. Das reduziert den Datenverkehr erheblich, sodass Webanwendungen sehr viel schneller reagieren. Die Daten auf einer Webseite lassen sich gezielt aktualisieren, was die Bedienung einer Internet-Seite vereinfacht. Bereits 2005 verwendeten die ersten Anwendungen diese Webtechnik. Sie gehört zum festen Bestandteil aller üblichen Webbrowser. AJAX manipuliert den Objektbaum eines Browsers direkt mit Javascript. Heute kommen oft auf AJAX basierende Frameworks wie JQuery zum Einsatz. Diese sind aber relativ groß und lassen sich nur schlecht mit einem Mikrocontroller verwenden – hier eignet sich das ältere AJAX besser.
Damit eine Webanwendung gut aussieht, arbeitet der Designer oft mit Bildern. Wie Abbildung 5 zeigt, macht auch unsere kleine Anwendung hier keine Ausnahme. Wie bekommt man nun die Ressourcenbeschränkungen eines Mikrocontrollers mit coolen Bildern unter einen Hut? Die meisten Mikrocontroller bringen nur wenige Hundert KByte Hauptspeicher mit – zu wenig Platz für tolle Bilder. Eine Lösung für dieses Problem bietet das SVG-Format (Scalable Vector Graphics). Anders als Bitmap-Bilder beschreibt es eine Grafik durch Vektoren, die nur sehr wenig Platz benötigen. Eine kleine Warnung zu SVG ist aber angebracht: Die Styles von SVG unterscheiden sich leicht von denen, die HTML/CSS verwendet.

Abbildung 5: Die Pumpensteuerung im Browser. Wegen eingeschränkter Platzverhältnisse im Controller übernehmen Vektorgrafiken die Bildgestaltung.
Am Anfang der loop()-Funktion befindet sich der Code, um die Pumpe manuell an- und auszuschalten. Danach folgt der Code, der den Webserver implementiert. Der Server kennt nur eine Webseite (/) und drei Aufrufe für die AJAX-Funktionen.
Sehen wir uns nun die Datei mit dem HTML-Code einmal genauer an. Die Funktion header() erzeugt einen gültigen HTTP-Header und ein HTML-Gerüst. Da innerhalb einer Anwendung üblicherweise alle Seiten einheitlich aussehen, verschieben wir alle konstanten Teile in eine Funktion. Dieser übergeben wir dann nur noch den Content, der sich für jede Seite verändert. Das hat auch den Vorteil, dass wir Speicherplatz im Mikrocontroller sparen.





