Arduino Ethernet Shield 2
Mit dem Ethernet Shield kann man einen Arduino UNO mit einem Computernetzwerk verbinden. Dazu dient der normale RJ45 Netzwerk Anschluss. Das Ethernet Shield wird von dem angesteckten Arduino UNO mit Strom versorgt.
Die Hauptbaugruppen auf dem Shield ist ein W5500 Ethernet Controller der über die Bibliothek
Ethernet.h angesteuert wird und ein micro-SD Kartenslot der sich über die Bibliothek
SD.h steuern lässt. Beide Komponenten kommunizieren über die SPI Schnittstelle mit dem Arduino.
Auf dem aufgesteckten Ethernet Shield sind alle Anschlüsse des Arduino UNO vorhanden. Achtsam muss man jedoch die SPI Pins verwenden. Diese werden bereits sowohl vom WS5500 als auch von der SD Karte verwendet (SPI Pins 11-13, CS W5500 Pin10, CS SD-Karte Pin4)
Auf dem Shield sind weitere 6 farbige Anschlüsse. Diese sind zum direkten einstecken von TinkerKit Kabeln (=Roboterbausätze). Die Steckeranschlüsse sind jeweils mit unterschiedlichen Arduino Pins direkt, ohne weitere Elektronik, verbunden. Die weißen Stecker sind mit A2 und A3 verbunden (mit jeweils GND und VCC). Die orangenen Stecker mit D5 und D6 (mit jeweils GND und VCC). Die grünen Stecker haben 4 Pole und sind an die I2C Schnittstelle angeschlossen.
Programmcode
Bei den folgenden Programmen wird das Ethernet Shield als Webserver verwendet. Ein Client (Browser auf dem Computer) macht bei dem Webserver (Arduino mit Ethernet Shield) einen 'Request'. Nach empfangen des 'Request' kann der Arduino verschieden Informationen auswerten oder bereitstellen. Im Anschluss wird ein 'Response' an den Client zurückgesendet. Die nachfolgenden Programme bauen aufeinander auf.
Eine Client Anfrage sieht folgendermaßen aus.
Eine auf dem Arduino gespeicherte Webseite anzeigen
Folgendes Programm enthält die Grundstruktur für die Verwendung des Arduinos als Webserver. Zunächst werden die notwendigen Bibliotheken eingebunden, sowie die MAC Adresse unseres Webservers (dem Arduino Ethernet Shield) und die IP Adresse festgelegt.
Im setup() wird die Netzwerkverbindung hergestellt, der Webserver gestartet und der serielle Monitor
eingerichtet. Zur Kontrolle einer bestehenden Netzwerkverbindung wird die IP Adresse des eingerichteten
Arduino-Webservers ausgegeben.
In der loop() Schleife wird ständig abgefragt, ob eine Client Anfrage eingegangen ist. Ist dies
irgendwann der Fall wird der weitere Programmcode ausgeführt.
Zunächst wird das Ende der Anfrage (Client Request) abgewartet und dabei zur Kontrolle jedes Zeichen auf dem
seriellen Monitor ausgegeben. Das Ende des Requests wird durch die leere Zeile am Ende erkannt. Sobald die Anfrage
beendet ist, wird dem Client vom Arduino-Webserver geantwortet. Die leere Zeile wird mit Hilfe der Variable
leereZeile detektiert. Bei jedem Zeilenumbruch (\n) wird leerezeile = true. Wenn gleich
danach wieder ein Zeilenumbruch (\n) empfangen wird (nur unterbrochen durch das 'return' Zeichen), ist dies die
gesuchte Zeile ohne Inhalt.
Programm
#include "SPI.h"
#include "Ethernet.h"
// MAC Adresse des Shields angeben (steht auf der Unterseite des Shields)
byte MAC[] = { 0xA8, 0x61, 0x0A, 0xAE, 0xA8, 0x1C };
// IP Adresse angeben. Mein PC hat die IP Adresse 192.168.55.1 mit der Subnetzmaske 255.255.255.0
IPAddress IP(192, 168, 55, 2);
// Es werden zwei Objekte Erstellt. Einen SERVER und einen CLIENT.
EthernetServer SERVER(80); // Port 80 ist fur unverschlüsselte http Seiten
EthernetClient CLIENT;
void setup() {
Serial.begin(115200);
// Netzwerkverbindung wird hergestellt. Ohne die IP Adresse wird diese vom Server per DHCP zugewiesen
Ethernet.begin(MAC, IP);
// Anzeige der IP Adresse zu Kontrolle auf dem seriellen Monitor
Serial.print("IP Adresse des Webservers: ");
delay(3000);
Serial.println(Ethernet.localIP());
// Starten des Webservers
SERVER.begin();
}
void loop() {
CLIENT = SERVER.available(); // Abfragen ob neue Clientanfragen eingegangen sind
if (CLIENT) { // Ein CLIENT hat sich gemeldet
Serial.println("Neue Client Anfrage erkannt....\r\n");
bool neueZeile = true; // Hilfsvariable um herauszufinden ob die Clientanfrage beendet ist
while (CLIENT.connected()) { // Solange eine Verbindung zum CLIENT besteht
if (CLIENT.available()) { // CLIENT Daten zum Lesen vorhanden
char c = CLIENT.read();
Serial.write(c);
if (c == '\n' && neueZeile == true) {
// Senden des 'HTTP response headers'
CLIENT.println("HTTP/1.1 200 OK");
CLIENT.println("Content-Type: text/html");
CLIENT.println("Connection: close");
CLIENT.println(); // Notwendige Leerzeile zur Kennzeichnung 'Header Ende'
// Senden der Webseite in HTML Code
CLIENT.println("<!DOCTYPE html>");
CLIENT.println("<html>");
CLIENT.println("<body>");
CLIENT.println("<h1>Empfangene Webseite");
CLIENT.println("<p>Hello Computerbrowser I'm your Arduino Webserver!");
CLIENT.println("</body>");
CLIENT.println("</html>");
break;
}
// Leere Zeile als Indikator für Request-Ende ('\r\n' = return + new Line)
if (c == '\n') {
neueZeile = true;
} else if (c != '\r') {
neueZeile = false;
}
}
}
delay(1);
CLIENT.stop();
Serial.println(".... Verbindung mit dem Client wurde beendet.");
}
}
Mehrere auf der SD-Karte gespeicherte Webseiten anzeigen
Wichtig ist, dass die SD Karte für ein FAT32 Dateisystem für DOS Dateien (8.3 = Name max 8 Zeichen + Dateiendung 3 Zeichen) formatiert ist. Auf der SD Karte sollte eine Datei mit dem Namen "index.htm" als Startseite gespeichert sein.
Bei vorhergehenden Programm wurde auf jede Client-Anfrage die immer gleiche Webseite zurückgesendet. In diesem Programm wird jetzt die empfangenen "GET / ..." Zeile der Anfrage ausgewertet. In dieser Zeile steht die Zieladresse der Anfrage. Im Beispiel die seite3.htm
Dazu speichert die globale Variable clientRequest den ersten Teil der Client Anfrage (unseres
Browsers) mit der übertragenen Zieladresse bis zum Zeilenende das durch die "unsichtbaren" Zeichen "\r\n" (return
+ new line) erkannt wird. Am Ende des kompletten Requests wird die Variable clientRequest ausgewertet
und eine passender "Response" zurückgesendet.
Programm
#include "SPI.h"
#include "Ethernet.h"
#include "SD.h"
// MAC Adresse des Shields angeben (steht auf der Unterseite des Shields oder irgendeinen andere)
byte MAC[] = { 0xA8, 0x61, 0x0A, 0xAE, 0xA8, 0x1C };
// IP Adresse angeben. Der PC hat die IP Adresse 192.168.55.1 mit der Subnetzmaske 255.255.255.0
IPAddress IP(192, 168, 55, 3);
// Es werden zwei Objekte Erstellt. Einen SERVER und einen CLIENT.
EthernetServer SERVER(80); // Port 80 ist fur unverschlüsselte http Seiten
EthernetClient CLIENT;
String clientRequest; // Speichert die HTTP Anfrage vom CLIENT zum Auswerten
void setup() {
Serial.begin(115200);
// die beiden CS Pins für die SPI Verbindung als Ausgang taufen
pinMode(4, OUTPUT); digitalWrite(4, HIGH);
pinMode(10, OUTPUT); digitalWrite(10, HIGH);
// die Verbindung zur SD Karte wird aufgebaut
Serial.println("Initialisierung SD-Karte ...");
if (!SD.begin(4)) { // Pin 4 is der CS Pin für die SPI Verbindung
Serial.println("Karte nicht lesbar!");
} else {
Serial.println("OK, SD-Karte ist einsatzbereit!");
}
// Netzwerkverbindung wird hergestellt. Ohne die IP Adresse wird diese vom Server per DHCP zugewiesen
Ethernet.begin(MAC, IP);
// Starten des Webservers
SERVER.begin();
}
void loop() {
CLIENT = SERVER.available(); // Abfragen ob neue Clientanfragen eingegangen sind
if (CLIENT) { // Ein CLIENT hat sich gemeldet
bool neueZeile = true; // Hilfsvariable um herauszufinden ob die Clientanfrage beendet ist
while (CLIENT.connected()) { // Solange eine Verbindung zum CLIENT besteht
if (CLIENT.available()) { // CLIENT Daten zum Lesen vorhanden
char c = CLIENT.read();
// Speichert nur die erste Zeile des Strings mit der übertragenen Zieladresse (GET / .....)
if (!clientRequest.endsWith("\r\n")) {
clientRequest = clientRequest + c;
}
Serial.write(c); // Alle empfangenen Zeichen des Requests werden zur Kontrolle ausgegeben
if (c == '\n' && neueZeile == true) {
auswertungRequest();
clientRequest = ""; // Die String Variable für die Auswertung der Anfrage wird wieder gelöscht
break;
}
// Leere Zeile als Indikator für Request-Ende ('\r\n' = return + new Line)
if (c == '\n') {
neueZeile = true;
} else if (c != '\r') {
neueZeile = false;
}
}
}
delay(1);
CLIENT.stop();
}
}
void auswertungRequest() {
// Dateinamen aus 'clientRequest' heraustrennen (Bsp. GET /bild.jpg HTTP/1.1)
String zielDatei = clientRequest.substring(5, clientRequest.length() - 11);
// Wird kein Zeichen übertragen wird die Datei 'index.htm' angefordert
if (zielDatei == "") {
zielDatei = "index.htm";
}
Serial.print("Ziel: ");
Serial.print(zielDatei);
// Überprüfen ob die Zieldatei auf der SD Karte ist
if (!SD.exists(zielDatei)) {
Serial.println(" >>> ERROR - Datei nicht auf der SD Karte!");
// Fehler als HTTP "Response Header" senden
CLIENT.println("HTTP/1.1 404 Not Found");
CLIENT.println("Content-Type: text/html");
CLIENT.println();
CLIENT.println("<h2>Datei nicht gefunden</h2>");
} else {
// Senden des 'HTTP response headers' abhängig des Dateityps
if (zielDatei.endsWith(".htm")) {
CLIENT.println("HTTP/1.1 200 OK");
CLIENT.println("Content-Type: text/html");
CLIENT.println();
} else if (zielDatei.endsWith(".jpg")) {
CLIENT.println("HTTP/1.1 200 OK");
CLIENT.println("Content-Type: image/jepg");
CLIENT.println();
} else if (zielDatei.endsWith(".png")) {
CLIENT.println("HTTP/1.1 200 OK");
CLIENT.println("Content-Type: image/png");
CLIENT.println();
}
}
// Senden des eigentlichen Response Inhalts
File webseite = SD.open(zielDatei); // Laden der Datei in eine Variable vom Typ 'File'
if (webseite) {
Serial.println(" >>>> Seite wird übertragen");
while (webseite.available()) {
CLIENT.write(webseite.read()); // Senden der gesamten Datei an den Client
}
webseite.close(); // Datei muss wieder geschlossen werden
}
}
LED über einen Webbrowser ein- und ausschalten
Eine LED am Pin A2 wird über die Webseite 192.168.55.2 (=Client Anfrage) ein und ausgeschaltet. Die erste Zeile
der Clientanfrage mit der Zieladresse "GET... " wird dazu wie im vorhergegangenen Beispiel gespeichert und in der
Funktion auswertungRequest() ausgewertet und die LED mit einem entsprechenden
digitalWrite() Befehl geschaltet und ein angepasster HTML Code eingefügt.
Programm
#include "SPI.h"
#include "Ethernet.h"
// MAC Adresse des Shields angeben (steht auf der Unterseite des Shields. Es geht aber auch irgendeinen andere)
byte MAC[] = { 0xA8, 0x61, 0x0A, 0xAE, 0xA8, 0x1C };
// IP Adresse angeben. Der PC hat die IP Adresse 192.168.55.1 mit der Subnetzmaske 255.255.255.0
IPAddress IP(192, 168, 55, 2);
// Es werden zwei Objekte Erstellt. Einen SERVER und einen CLIENT.
EthernetServer SERVER(80); // Port 80 ist fur unverschlüsselte http Seiten
EthernetClient CLIENT;
String clientRequest; // Speichert die HTTP Anfrage vom CLIENT zum Auswerten
bool ledStatus = 0; // Led aus
void setup() {
Serial.begin(115200);
// Netzwerkverbindung wird hergestellt. Ohne die IP Adresse wird diese vom Server per DHCP zugewiesen
Ethernet.begin(MAC, IP);
// Anzeige der IP Adresse im SM
Serial.print("IP Adresse: ");
delay(3000);
Serial.println(Ethernet.localIP());
// Starten des Webservers
SERVER.begin();
pinMode(A2, OUTPUT); // Anschluss der LED
}
void loop() {
CLIENT = SERVER.available(); // listen for incoming CLIENTs on Port 80
if (CLIENT) { // Ein CLIENT hat sich gemeldet
Serial.println("Client Anfrage erkannt....\r\n");
bool leereZeile = true; // Hilfsvariable um herauszufinden ob die Clientanfrage beendet ist
while (CLIENT.connected()) { // Solange eine Verbindung zum CLIENT besteht
if (CLIENT.available()) { // CLIENT Daten zum Lesen vorhanden
char c = CLIENT.read();
// Speichert nur die erste Zeile des Strings mit der übertragenen Zieladresse (GET / .....)
if (!clientRequest.endsWith("\r\n")){
clientRequest = clientRequest + c;
}
Serial.write(c);
if (c == '\n' && leereZeile == true) {
// send a standard HTTP response header
CLIENT.println("HTTP/1.1 200 OK");
CLIENT.println("Content-Type: text/html");
CLIENT.println("Connection: close");
CLIENT.println();
// send web page
CLIENT.println("<!DOCTYPE html>");
CLIENT.println("<html>");
CLIENT.println("<head>");
CLIENT.println("<title>Arduino LED Steuerung");
CLIENT.println("</head>");
CLIENT.println("<body>");
CLIENT.println("<h1>LED");
CLIENT.println("<p>Klicken um LED an- oder auszuschalten");
auswertungRequest();
CLIENT.println("</body>");
CLIENT.println("</html>");
clientRequest = ""; // Die String Variable für die Auswertung der Anfrage wird wieder gelöscht
break;
}
// Leere Zeile als Indikator für Request-Ende ('\r\n' = return + new Line)
if (c == '\n') {
leereZeile = true;
} else if (c != '\r') {
leereZeile = false;
}
}
}
delay(1);
CLIENT.stop();
Serial.println(".... Verbindung mit dem Client beendet.");
}
}
void auswertungRequest(){
if (clientRequest.indexOf("?LED=an") > -1){
ledStatus = 1;
} else if (clientRequest.indexOf("?LED=aus") > -1){
ledStatus = 0;
}
if (ledStatus == 0) {
CLIENT.println("<a href='?LED=an'> LED anschalten <a>");
digitalWrite(A2, LOW);
} else {
CLIENT.println("<a href='?LED=aus'> LED ausschalten <a>");
digitalWrite(A2, HIGH);
}
}
Schalterposition an einen Browser übertragen und automatisch aktualisieren
Die Initialisierung und der setup() Bereich ist im wesentlichen gleich zum vorhergehenden Beispiel.
Eine 'meta' Anweisung die im HTML Code der an den Browser als Response gesendet wurde veranlasst den Client (den
Browser) sich im Sekundenrhythmus zu aktualisieren. Oder besser, nach jeweils einer Sekunde einen Client request
an den Arduino-Webserver zu senden. Bei dieser Anfrage muss die Zieladresse nicht ausgewertet werden. Es wird nur
gewartet bis der Request beendet ist (durch Leerzeile am Ende) und anschließend der Schalterstatus abgefragt und
als Response gesendet.
Die periodische Abfrage kann auch über eine Java Script Anweisung vom Browser erfolgen. Dabei ist es möglich nur einen Bereich zu ändern. Das "Flackern" der Webseite beim Neuladen wird so unterbunden.
Programm
#include "SPI.h"
#include "Ethernet.h"
// MAC Adresse des Shields angeben (steht auf der Unterseite des Shields. Es geht aber auch irgendeinen andere)
byte MAC[] = { 0xA8, 0x61, 0x0A, 0xAE, 0xA8, 0x1C };
// IP Adresse angeben. Der PC hat die IP Adresse 192.168.55.1 mit der Subnetzmaske 255.255.255.0
IPAddress IP(192, 168, 55, 2);
// Es werden zwei Objekte Erstellt. Einen SERVER und einen CLIENT.
EthernetServer SERVER(80); // Port 80 ist fur unverschlüsselte http Seiten
EthernetClient CLIENT;
void setup() {
Serial.begin(115200);
// Netzwerkverbindung wird hergestellt. Ohne die IP Adresse wird diese vom Server per DHCP zugewiesen
Ethernet.begin(MAC, IP);
// Anzeige der IP Adresse im SM
Serial.print("IP Adresse: ");
delay(3000);
Serial.println(Ethernet.localIP());
// Starten des Webservers
SERVER.begin();
pinMode(A2, INPUT_PULLUP); // Schalter auf GND an Pin A2
}
void loop() {
CLIENT = SERVER.available(); // listen for incoming CLIENTs on Port 80
if (CLIENT) { // Ein CLIENT hat sich gemeldet
Serial.println("Client Anfrage erkannt....\r\n");
bool leereZeile = true; // Hilfsvariable um herauszufinden ob die Clientanfrage beendet ist
while (CLIENT.connected()) { // Solange eine Verbindung zum CLIENT besteht
if (CLIENT.available()) { // CLIENT Daten zum Lesen vorhanden
char c = CLIENT.read();
Serial.write(c);
if (c == '\n' && leereZeile == true) {
// send a standard HTTP response header
CLIENT.println("HTTP/1.1 200 OK");
CLIENT.println("Content-Type: text/html");
CLIENT.println("Connection: close");
CLIENT.println();
// send web page
CLIENT.println("<!DOCTYPE html>");
CLIENT.println("<html>");
CLIENT.println("<head>");
// Diese Zeile veranlasst den Browser die Seite jede Sekunde neu zu laden
CLIENT.println("<meta http-equiv=\"refresh\" content=\"1\">");
CLIENT.println("</head>");
CLIENT.println("<body>");
CLIENT.println("<h1>Schalterposition");
schalterStatus();
CLIENT.println("</body>");
CLIENT.println("</html>");
break;
}
// Am Ende einer Anfrage ist eine leere Zeile
if (c == '\n') {
leereZeile = true;
} else if (c != '\r') {
leereZeile = false;
}
}
}
delay(1);
CLIENT.stop();
Serial.println(".... Verbindung mit dem Client beendet.");
}
}
void schalterStatus(){
if (digitalRead(A2) == false) {
CLIENT.println("<p>ON</p>");
}
else {
CLIENT.println("<p>OFF</p>");
}
}