„Sales Glocke“ aka Ethernet Webserver

Sales Glocke

Sales Glocke

Weil ich in letzter Zeit häufiger gefragt wurde, will ich nun auch endlich das Sales-Glocken Projekt vorstellen und beschreiben. Es handelt sich um einen Arduino Ethernet mit PoE Modul, der einen kleinen Spielzeugmotor einschaltet. Der Spielzeugmotor hat einen kleinen flexiblen Schläger (Büroklammer) und haut damit bei jeder Umdrehung gegen die Glocke.

Auf dem Arduino läuft ein kleiner Webserver, der den GET-Request auswertet und daraufhin die LEDs steuert und das Relais schaltet. Da die Schaltung und das Relais nur wenig Strom brauchen, wird der Arduino per Power over Ethernet (PoE) versorgt, wenn verfügbar (dadurch entfällt das zweite Kabel für ein Netzteil).

Die Batterien versorgen den Motor. Er hängt nicht am PoE. Gerade diese Billig-Motoren können beim Anlaufen sehr viel Strom verbrauchen. Damit wir nicht den ganzen Strom vom PoE-Switch ziehen oder andere Komplikationen durch den Stromverbrauch provozieren, läuft der Motor auf Batterien. Der Arduino hängt nur mit dem Relais dazwischen. Dadurch ist es egal ob wir Wechselspannung, Gleichspannung schalten und sind später in der Hardwaregestaltung flexibler.

Die Platine, die ich zeigen werde, ist etwas größer und würde nicht in ein so schlankes Gehäuse passen, aber dadurch wird es hoffentlich etwas übersichtlicher. Die Sales Glocke im Foto hängt bei uns im Büro und klingelt, wenn jemand in unserem Online Marketing Daten Shop etwas kauft.

Die Klingel kann wie folgt aufgerufen werden http://<ip>/comand/parameter1/parameter2.

Nice to know

Man kann das Projekt mit einem Arduino Uno umsetzen, auf den das Ethernet-Modul gesteckt wird oder direkt mit einem Arduino Ethernet. Wichtig: für den Arduino Ethernet benötigt man noch einen USB2Serial Programmer, da der Arduino Ethernet keinen (!) USB Anschluss hat. Bei meinem ersten Arduino Ethernet Projekt musste ich diesen dann noch nachbestellen und ein paar Extra-Tage warten. Also bestellt das Ding gleich mit. z.B. im Set (Amazon-Link). Früher oder später braucht man so einen im Arduino Kosmos.

Zum PoE Modul: Das PoE Modul ist nicht immer beim Arduino Ethernet dabei und muss daher häufig extra bestellt werden. Dieses Zusatzmodul passt sowhl auf den Arduino Ethernet als auch auf das reine Ethernet-Shield. Es wird mit wenigen Lötpunkten befestigt. Je nachdem wo man kauft liegt ein original Arduino Uno + Ethernetshield + POE Modul schnell bei 50€ wenn es original sein soll. Meine Erfahrungen mit den billig Clones aus China sind gemischt (ist ja alles open source). Meistens keine Probleme, selten ist so ein Shield/Board mal direkt fritte, Abstriche i.d.R. nur im „Finish“. Allerdings wird hier das Arduino Projekt selbst nicht mit unterstützt (oder nur indirekt). Also kann man  machen, wenn man sparen will .

Wenn man nicht so viel Wert auf eine kompakte Bauform legt, dann würde ich den Arduino Uno und das getrennte Ethernet-Shield empfehlen. Ihr seid dann für weitere Projekte flexibler.

Als Halter für den Motor habe ich einen 08/15 Winkel aus dem Baumarkt genommen. Da schaut einfach nach etwas, was zu eurem Projekt passt. Mehr als 2-3 Euro kann man dafür eigentlich nicht ausgeben. Dieser ist mit einer Schraube an der Wand befestigt. Am anderen Ende habe ich ein Loch gebohrt, das exakt die Größe des Kragens der Motornarbe hatte. Dadurch hatte der Motor von sich aus bereits einen guten Sitz. Mit etwas Sekundenkleber und Heisskleber saß er dann Bombenfest.

Auf die Narbe kam dann noch die Büroklammer. Eine sogenannte Foldback-Klammer – direkt auf die Narbe geklebt. Vorsicht, beim ersten Testlauf war der Sekundenkleber an einigen Stellen noch flüssig. Mit dem Erfolg, dass seit dem eine kleine Linie feiner, harter Sekundenklebertropfen den Display meines Laptops ziert. Ein weiterer Grund beim Basteln immer eine Brille zu tragen.

Zunächst die Schaltung

Selbstbau-Shield mmit einfachem Wechselrelais und zwei LED

Selbstbau-Shield mit einfachem Wechselrelais und zwei LED

An zwei Pins (2 und 3) hängen LEDs, an einem Pin (7) ein Transistor der ein Relais schaltet. Das Relais ist in den Stromkreis der beiden grünen Schraubklemmen eingeschleift.

Als Basis habe ich eine handelsübliche Europlatine mit Punktraster im 2,54mm Format genommen (Conrad Link) und mit einem Cutter zur gewünschten Größe geritzt und abgebrochen. Die Schaltung war schnell zusammen gebaut. Hier muss man nicht viel nachdenken. Man sollte aber darauf achten, dass die Kabel, die den Strom zwischen Batterien (Doppel-Klemme), Relais und Verbraucheranschluss (3er Block) dick genug für die geplante Last sind. Wir wollen ja nicht, dass da etwas durchschmurgelt, nur weil ihr da Klingeldraht und ’nen fetten Rennmotor verwendet habt.

Da das PoE Modul ziemlich hoch baut und unter meiner Platine noch Kabel verlaufen musste ich Höhe gewinnen. Dadurch saß das Shield aber nicht mehr sicher, da die Beinchen nicht lang genug sind. Ich habe dann die stapelbaren Headerleisten von Sparkfun genommen (EXP-Tech Link) und als „Schicht“ dazwischengeklemmt. Die verwendeten Header sind zwar ein bisschen eine Notlösung, jedoch habe ich damit einen sicheren Halt.

Sandwich mit Extra-Lage Headern

Sandwich mit Extra-Lage Headern

_DSC0156 printed-nachbauen

Die Schaltung selbst hier noch einmal von oben und einmal mit Schaltsymbolen. Beim Relais bei Bedarf etwas Größeres wählen.

Teileliste

  • Stackable Headers von Sparkfun (Link)
  • Lochrasterplatine (Link)
  • 5V Relais zum schalten von 1A  bei 30V/DC oder 120V/AC (Link)
  • Diode 1N4148 (Freilaufdiode für Relais; Link)
  • BC 547 C Transistor (Link) + Vorwiderstand
  • Leuchtdioden Rot, Grün + Vorwiderstände
  • Schraubklemmen im 2,54mm Rastermaß

 Universeller Batterie Anschluss

Verschiedene Beschaltung

Verschiedene Beschaltung

9V-Clips! Ich liebe sie. Dadurch können wir verschiedene Batterie Packs direkt anklemmen. Kabelbinder drum, damit die Batterien nicht runterfallen und gut ist. Für kleine Spielzeugmotoren reichen manchmal 2,5V zum Betrieb. Normale Einweg-AA-Batterien haben etwa 1,45-1,5V wenn Sie frisch sind. Die üblichen Zink-Kohle Batterien bringen etwa je 1200 mAh. Akkus können da mehr. Ihre Spannung liegt zwar mit 1,2V etwas niedriger (was man ggf. beachten sollte) aber dafür haben Sie deutlich mehr Kapazität und können dadurch mehr Power bereitstellen und knicken dann nicht so schnell ein.

Ich empfehle immer gerne die Sanyo Eneloop XX (Amazon-Link). 4 Stück für 13 EUR, dafür bekommt man einen Prima Akku der bei nur 15% Selbstentladung pro Jahr Lagerung liegt. Damit ist man im Grunde sicher, immer ein paar volle Akkus zur Hand zu haben. Diese Akkus haben auch vorzüglich meinen Photoblitz (Nikon SB-800) versorgt und selbst bei kalten Temperaturen vernünftig gearbeitet. Die haben 2450 mAh Kapazität und sind als NiMh relativ stressfrei.

Der Code

Wir brauchen einen kleinen Webserver, dem wir ein paar rudimentäre Komandos schicken können. Bei uns in der Firma wird bei einer Bestellung im Datenshop eine IP in unserem Netzwerk mit ein paar Parametern aufgerufen.

Sinngemäß: http://192.168.2.200/interval_ring/5/500

Der Webserver gibt die Parameter die er verstanden hat als Antwort in Form einer Webseite zurück und führt im Anschluss die gewünschten Befehle aus. Als Basis können wir den Beispiel-Sketch >> Ethernet >> Webserver aus der Arduino IDE verwenden. Er ist bereits eine solide Basis und wir können uns hier einfach reinhängen.

Initialisieren und Verbindung mit dem Netzwerk herstellen

Diesmal nehmen wir eine feste IP. Bei DHCP müssten wir sonst jedesmal unseren Arduino im heimischen Netzwerk suchen. Wir fügen dem Beispielsketch zunächst erstmal nur unsere Pin-Belegung hinzu.

void setup() {
pinMode(ledRed,OUTPUT);
pinMode(ledGreen,OUTPUT);
pinMode(relay,OUTPUT);
digitalWrite(relay,LOW);
// Open serial communications and wait for port to open:
Serial.begin(9600);
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
}

Main Loop

Der Main-Loop ist weitestgehend der Gleiche wie in dem Beispiel aus der IDE. Wir verwenden wieder unseren Recording-Modus um an den interessanten Teil der Anfrage zu gelangen. In unserem Fall steuern wir das Relais (unsere Glocke) über Get-Parameter.

Wenn wir die Sales-Glocke z.B. per Browser aufrufen, erhält der Arduino eine ähnliche Zeichenkette wie die folgende:

GET /interval_ring/5/600 HTTP/1.1
Host: 192.168.2.199
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:36.0) Gecko/20100101 Firefox/36.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive

Nachdem der Browser (oder welche Client auch immer) eine leere Zeile gesendet hat, wissen wir, dass die Anfrage beendet ist, und können mit der Antwort beginnen. DA wir lediglich die Get-Parameter auswerten, interessiert uns lediglich die erste Zeile der Antwort. Den Rest sollten wir aber aus dem Speicher auslesen, damit wir nicht zuviele offene Verbindungen haben. Man könnte noch mit Ende der ersten Zeile einfach alles weitere verwerfen (z.B. mit client.flush()) aber in diesem Fall möchte ich die Anfrage als Debugging auch auf der Konsole sehen.

Um die Anfrage zu verarbeiten, wird wie folgt vorgegangen:

  • Prüfen ob ein Client verbunden ist ->Wenn ja:
    • Erste Zeile der Anfrage zur weiteren Bearbeitung mitschneiden
    • solange die Ethernetverbindung auslesen, bis leere Zeile empfangen und Anfrage beendet wurde
    • Erste Zeile mit Get-Inhalten auswerten
    • Antwort an den Client senden
    • Verbindung zum Client beenden
    • Aktion durchführen
void loop() {
// listen for incoming clients
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
bufferPos = 0;
recordingDone = false;
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// We now have what we need, we can now analyse the request
ParseReceivedRequest();
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println();
client.println("<html>");
client.println("What ever you want the content of this url to be");
client.println("</html>");
break;
}
if (c == '\n') {
// you're starting a new line
// when we meet the first newline character, we stop recording
recordingDone = true;
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
// We start with active recording and record until told to stop
// BufferString then contains first line of request
if (recordingDone == false) {
bufferString[bufferPos] = c;
bufferPos++;
} 

}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disconnected");

//Connection is closed, we can now do some action and won't block the client
interpretAction();

}
}

Damit hätten wir schonmal das ganze Steuern der Verbindung erledigt und können uns nun interessanteren Dingen zuwenden.

Get-Anfrage Auswerten

Was wir auswerten müssen, ist eine Zeichenkette, die in etwa wie folgt aussieht: GET /interval_ring/5/600 HTTP/1.1

Für unser Beispiel gehe ich davon aus das der Aufruf immer wie folgt aussieht: /komando/parameter1/parameter2/

Wie beim Auswerten der API im Projekt SEO Wassersäule schauen wir nach dem vorkommen von bestimmten Trennzeichen, zerlegen den String an diesen Stellen und speichern die einzelnen Werte in dedizierten Variablen. Den Code habe ich aus einem Blog-Artikel, aber ich finde leider die Quelle nicht mehr.

Wir rufen diese Funktion auf, wenn die Anfrage bendet ist und bevor wir die HTML Antwort senden (damit wir die Antwort in das HTML packen können). Sie befüllt uns die globalen Variablen cmd, param1 und param2.

void ParseReceivedRequest()
{

//Received buffer contains "GET /cmd/param1/param2 HTTP/1.1".  Break it up.
char* slash1;
char* slash2;
char* slash3;
char* space2;

slash1 = strstr(bufferString, "/") + 1; // Look for first slash
slash2 = strstr(slash1, "/") + 1; // second slash
slash3 = strstr(slash2, "/") + 1; // third slash
space2 = strstr(slash2, " ") + 1; // space after second slash (in case there is no third slash)
if (slash3 > space2) slash3=slash2;

// strncpy does not automatically add terminating zero, but strncat does! So start with blank string and concatenate.
cmd[0] = 0;
param1[0] = 0;
param2[0] = 0;
strncat(cmd, slash1, slash2-slash1-1);
strncat(param1, slash2, slash3-slash2-1);
strncat(param2, slash3, space2-slash3-1);

}

Im Unterschied zur Wassersäule behalten wir die Werte aber in einem Character-Array und wandeln sie noch nicht in Zahlen um. In unserem Beispiel werde ich zwei Befehle implementieren. /switch/ und /interval_ring/.

  1. /switch/on/
  2. /switch/on/600
  3. /switch/off/

Zum (1) dauerhaften, einmaligen anschalten des Relais, mit (b) optionaler Angabe der Schaltdauer zum automatischen Ausschalten nach x Millisekunden sowie (3) ein Befehl zum direkten Ausschalten des Relais.

Dazu habe ich noch einen Interval Befehl, diesen rufe ich wie folgt auf:

  1. /interval_ring/5/500

Die erste Zahl gibt an wie oft das Relais geschlossen werden soll und die zweite Zahl gibt an, wie lange das Relais jeweils geschlossen sein soll.

Da also mein zweiter Parameter sowohl Buchstaben („on“) als auch Zahl (z.B. 5) sein könnte, belassen wir es also bei dem Character-Array (für’s Erste).

Die Komandos an Funktionen weiterreichen

Hierfür reich uns eine ganz kleine Funktion. Zu Begin wandeln wir das Character-Array in der Variable cmd in einen String um, damit wir es danach ein wenig leichter haben. Je nachdem, welcher Befehl nun in der Variable enthalten ist, rufen wir eine weitere Funktion auf:

void interpretAction() {

String command = cmd;

if (command == "interval_ring") {
switch_relay_interval();
} else if (command == "switch") {
switch_relay();
}

}

Interval-Schleife

Wenn wir die Funktion switch_relay_interval() aufrufen, gehen wir davon aus, das die beiden Parameter nur Zahlen und keine Buchstaben enthalten. Mit atoi() wandelt wir diese beiden Zeichenketten nun in Integerwerte um, die wir in einer lokalen Variable speichern. Danach gehen wir mit einem for-loop durch unseren Klingel-Prozess.

Wir schalten die rote LED und das Relais ein und die grüne LED aus. Dann warten wir soviele Millisekunden, wie durch Parameter 2 festgelegt. Danach schalten wir die rote LED und das Relais aus und die grüne LED an. Und legen eine Pause von einer halben Sekunde ein. Das wiederholen wir so oft, wie durch Parameter 1 festgelegt.

Die LED nutze ich hier als visuelle Stütze, in welchem Modus sich das Board befindet. Wechseln sich Rot/Grün ab, befinde ich mich im Interval Modus.

void switch_relay_interval() {

int loopCount = atoi(param1);
int duration = atoi(param2);

for (int x = 0; x < loopCount; x++) {
digitalWrite(ledRed,HIGH);
digitalWrite(ledGreen,LOW);
digitalWrite(relay,HIGH);
delay(duration);
digitalWrite(ledRed,LOW);
digitalWrite(ledGreen,HIGH);
digitalWrite(relay,LOW);
delay(500);
}

digitalWrite(ledRed,LOW);
digitalWrite(ledGreen,LOW);

}

Einzelne Schaltvorgänge

Über die Funktion switch_relay() schalten wir das Relais ohne Interval. Der erste Parameter enthält eine Zeichenkette (on/off) und muss dieses Mal in einen String umgewandelt werden. Der Zweite optionale Parameter der eine Schaltdauer mit Abschaltung ermöglichst braucht einen Integer Wert. Wir nutzen hier wieder atoi().

Wenn duration > 0 ist, schalten wir noch die grüne LED hinzu, es leuchten Rot und Grün gleichzeitig und signalisieren einen Schaltvorgang mit Timer. Ein Rotes Dauerleuchten signalisiert ein dauerhaft geschaltetes Relais.

void switch_relay() {

String mode = param1;
int duration = atoi(param2);

if (mode == "on") {
digitalWrite(ledRed,HIGH);
digitalWrite(relay,HIGH);
if (duration > 0) {
digitalWrite(ledGreen,HIGH);
delay(duration);
digitalWrite(relay,LOW);
digitalWrite(ledRed,LOW);
digitalWrite(ledGreen,LOW);
}
} else{
digitalWrite(relay,LOW);
digitalWrite(ledRed,LOW);
digitalWrite(ledGreen,LOW);
}

}

Und das war’s dann auch schon. Wir haben nun einen Webserver im heimischen Netzwerk, dem wir per GET-Parameter Schaltbefehle geben können. Nun können wir diese Adresse aufrufen und damit Events „verkünden“. Sei es ein Motor der auf eine Glocke haut, eine Rundum-Leuchte (Blaulicht) oder ein Spenden-Karussel wie bei Sistrix. Der Rest ist dann eurer Fantasie überlassen.

Hier noch die Datei mit dem ganzen Code. Sales Glocke V1 *.ino Datei für die Arduino IDE

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert