In diesem Kapitel werden wir sehen, wie wir eine funktionierende Anwendung zusammenstellen, die das in den vorangegangenen Kapiteln Erlernte nutzt. Dieses Kapitel soll dir zeigen, wie jedes einzelne Beispiel als Baustein für ein komplexes Projekt verwendet werden kann.
Hier kommt der Möchtegern-Designer in mir zum Vorschein. Wir werden die 21.-Jahrhundert-Version einer klassischen Lampe meines bevorzugten italienischen Designers Joe Colombo anfertigen. Das Objekt, das wir bauen werden, ist von einer Lampe namens Aton aus dem Jahr 1964 inspiriert.
– Massimo
Wie du in Abb. 6–1 siehst, ist die Lampe eine einfache Kugel, die auf einem Fuß mit einem großen Loch sitzt, das verhindert, dass die Kugel auf deinen Tisch herunterrollt. Dieses Design erlaubt dir, die Lampe in verschiedene Richtungen auszurichten.
In Bezug auf die Funktionalität wollen wir ein Gerät bauen, das sich mit dem Internet verbindet, die aktuelle Artikelliste des Make-Blogs (http://blog.makezine.com) abruft und zählt, wie oft die Wörter »Peace«, »Love« und »Arduino« erwähnt werden. Mit diesen Werten erzeugen wir eine Farbe und stellen sie auf der Lampe dar. Die Lampe selbst hat einen Taster, den wir zum Ein- und Ausschalten verwenden können, und einen Lichtsensor für die automatische Aktivierung.
Schauen wir uns an, was wir erreichen wollen und welche Dinge wir dafür brauchen. Zuerst benötigen wir Arduino, um eine Verbindung zum Internet herstellen zu können. Da das Arduino-Board nur einen USB-Port hat, können wir es nicht direkt mit einem Internetanschluss verbinden, sodass wir uns überlegen müssen, wie wir die beiden überbrücken. Normalerweise führen Leute eine Anwendung auf einem Computer aus, der sich mit dem Internet verbindet, die Daten verarbeitet und Arduino etwas einfache, gefilterte Information zusendet.
Der Arduino ist ein simpler Computer mit kleinem Speicher; er kann große Dateien nicht einfach verarbeiten, und wenn wir eine Verbindung mit einem RSS-Feed herstellen, erhalten wir eine sehr ausführliche XML-Datei, die erheblich mehr RAM erfordern würde. Andererseits hat dein Laptop- oder Desktop-Computer viel mehr RAM und ist für diese Art von Arbeit weit besser geeignet, sodass wir zum Vereinfachen der XML einen Proxy implementieren, indem wir die Programmiersprache für die Ausführung auf deinem Computer verwenden.
Arduino basiert auf Processing. Wir lieben diese Sprache und verwenden sie, um Anfängern das Programmieren beizubringen und um schönen Code zu schreiben. Processing und der Arduino bilden ein perfektes Team. Ein weiterer Vorteil ist, dass Processing quelloffen ist und auf allen wichtigen Plattformen läuft (macOS, Linux und Windows). Es kann auch eigenständige Anwendungen erzeugen, die auf diesen Plattformen laufen. Darüber hinaus gibt es eine lebhafte und hilfsbereite Processing-Community, und du findest Tausende vorgefertigter Beispielprogramme.
Hol dir Processing (engl. für »Verarbeitung«) von https://processing.org/download.
Der Proxy erledigt die folgende Arbeit für uns: Er lädt den RSS-Feed von http://makezine.com herunter und extrahiert alle Wörter aus der sich ergebenden XML-Datei. Dann, beim Durchgehen aller Wörter, zählt er die Anzahl der im Text auftauchenden Wörter »Peace«, »Love« und »Arduino«. Mit diesen drei Zahlen berechnen wir einen Farbwert und schicken ihn an Arduino. Der Arduino-Code sendet wiederum die vom Sensor gemessene Lichtmenge an den Computer, die dann vom Processing-Code auf dem Computermonitor angezeigt wird.
Auf der Hardwareseite kombinieren wir das Drucktastschalter-Beispiel, das Lichtsensor-Beispiel, die PWM-LED-Steuerung (mit 3 multipliziert!) und die serielle Kommunikation. Schau, ob du jede dieser Schaltungen identifizieren kannst, während du sie in »Zusammenbau der Schaltung« auf Seite 93 baust. So werden normalerweise Projekte durchgeführt.
Da es sich bei Arduino um ein einfaches Gerät handelt, müssen wir auch die Farbe auf eine einfache Art und Weise festlegen. Wir verwenden die standardisierte Form, in der Farben in HTML angegeben werden: # gefolgt von sechs hexadezimalen Zeichen.
Hexadezimale Zahlen sind praktisch, da jede 8-Bit-Zahl in genau zwei Zeichen gespeichert wird; bei Dezimalzahlen variiert dies von ein bis drei Zeichen. Die Berechenbarkeit macht zudem den Code einfacher: Wir warten, bis wir ein # sehen, und dann lesen wir die folgenden sechs Zeichen in einen Puffer ein (eine als vorübergehender Wartebereich für Daten verwendete Variable). Abschließend wandeln wir jede Gruppe aus zwei Zeichen in ein Byte um, das die Helligkeit einer der drei LEDs repräsentiert.
Die zwei auszuführenden Sketche: der Processing-Sketch und der Arduino-Sketch. Beispiel 6–1 ist der Code für den Processing-Sketch. Du kannst ihn auch über den Link für die Beispielcodes von der Katalogseite der englischen Originalausgabe des Buches herunterladen (https://makezine.com/go/arduino-4e-github/).
Beispiel 6–1Arduino-Netzwerk-Lampe
Teile des Codes sind von einem Blog-Beitrag von Tod E. Kurt inspiriert (http://todbot.com).
import processing.serial.*;
import java.net.*;
import java.io.*;
import java.util.*;
String feed = "http://makezine.com/feed/";
int interval = 5 * 60 * 1000; // retrieve feed every five minutes;
int lastTime; // the last time we fetched the content
int love = 0;
int peace = 0;
int arduino = 0;
int light = 0; // light level measured by the lamp
Serial port;
color c;
String cs;
String buffer = ""; // Accumulates characters coming from Arduino
PFont font;
void setup() {
size(640, 480);
frameRate(10); // we don't need fast updates
font = createFont("Helvetica", 24);
fill(255);
textFont(font, 32);
// IMPORTANT NOTE:
// The first serial port retrieved by Serial.list()
// should be your Arduino. If not, uncomment the next
// line by deleting the // before it, and re-run the
// sketch to see a list of serial ports. Then, change
// the 0 in between [ and ] to the number of the port
// that your Arduino is connected to.
//println(Serial.list());
String arduinoPort = Serial.list()[0];
port = new Serial(this, arduinoPort, 9600); // connect to Arduino
lastTime = millis();
fetchData();
}
void draw() {
background( c );
int n = (lastTime + interval - millis())/1000;
// Build a colour based on the 3 values
c = color(peace, love, arduino);
cs = "#" + hex(c, 6); // Prepare a string to be sent to Arduino
text("Arduino Networked Lamp", 10, 40);
text("Reading feed:", 10, 100);
text(feed, 10, 140);
text("Next update in "+ n + " seconds", 10, 450);
text("peace", 10, 200);
text(" " + peace, 130, 200);
rect(200, 172, peace, 28);
text("love ", 10, 240);
text(" " + love, 130, 240);
rect(200, 212, love, 28);
text("arduino ", 10, 280);
text(" " + arduino, 130, 280);
rect(200, 252, arduino, 28);
// write the colour string to the screen
text("sending", 10, 340);
text(cs, 200, 340);
text("light level", 10, 380);
rect(200, 352, light/10.23, 28); // this turns 1023 into 100
if (n <= 0) {
fetchData();
lastTime = millis();
}
port.write(cs); // send data to Arduino
if (port.available() > 0) { // check if there is data waiting
int inByte = port.read(); // read one byte
if (inByte != 10) { // if byte is not newline
buffer = buffer + char(inByte); // just add it to the buffer
} else {
// newline reached, let's process the data
if (buffer.length() > 1) { // make sure there is enough data
// chop off the last character, it's a carriage return
// (a carriage return is the character at the end of a
// line of text)
buffer = buffer.substring(0, buffer.length() -1);
// turn the buffer from string into an integer number
light = int(buffer);
// clean the buffer for the next read cycle
buffer = "";
// We're likely falling behind in taking readings
// from Arduino. So let's clear the backlog of
// incoming sensor readings so the next reading is
// up-to-date.
port.clear();
}
}
}
}
void fetchData() {
// we use these strings to parse the feed
String chunk;
// zero the counters
love = 0;
peace = 0;
arduino = 0;
try {
URL url = new URL(feed); // An object to represent the URL
// prepare a connection
URLConnection conn = url.openConnection();
conn.connect(); // now connect to the Website
// this is a bit of virtual plumbing as we connect
// the data coming from the connection to a buffered
// reader that reads the data one line at a time.
BufferedReader in = new
BufferedReader(new InputStreamReader(conn.getInput
Stream()));
// read each line from the feed
while ( (data = in.readLine()) != null) {
StringTokenizer st =
new StringTokenizer(data, "\"<>,.()[] ");// break it down
while (st.hasMoreTokens ()) {
// each chunk of data is made lowercase
chunk= st.nextToken().toLowerCase() ;
if (chunk.indexOf("love") >= 0 ) // found "love"?
love++; // increment love by 1
if (chunk.indexOf("peace") >= 0) // found "peace"?
peace++; // increment peace by 1
if (chunk.indexOf("arduino") >= 0) // found "arduino"?
arduino++; // increment arduino by 1
}
}
// Set 64 to be the maximum number of references we care about.
if (peace > 64) peace = 64;
if (love > 64) love = 64;
if (arduino > 64) arduino = 64;
peace = peace * 4; // multiply by 4 so that the max is
255,
love = love * 4; // which comes in handy when building a
arduino = arduino * 4; // colour that is made of 4 bytes
(ARGB)
catch (Exception ex) { // If there was an error, stop the sketch
ex.printStackTrace(); System.out.
println("ERROR: "+ex.getMessage());
}
}
Eine Sache gilt es noch zu tun, bevor der Processing-Sketch korrekt abläuft: Du musst bestätigen, dass der Sketch den korrekten seriellen Port für die Kommunikation mit Arduino verwendet. Du musst warten, bis du die Arduino-Schaltung zusammengebaut und den Arduino-Sketch hochgeladen hast, bevor du das bestätigen kannst. Auf manchen Systemen läuft dieser Processing-Sketch ganz ordentlich. Wenn du allerdings keine Aktivität auf dem Arduino erkennst und keine Angabe vom Lichtsensor auf dem Bildschirm erscheint, suche nach dem Kommentar WICHTIGER HINWEIS im Processing-Sketch und folge den dortigen Anweisungen.
Wenn du unter MacOS arbeitest, ist die Wahrscheinlichkeit hoch, dass dein Arduino am letzten aufgelisteten seriellen Port ist. Wenn das der Fall ist, kannst du die 0 in Serial.list()[0] mit Serial.list().length -1 ersetzen. Das zieht von der Länge der Liste aller seriellen Ports einen ab; Bereichsindizes zählen ab Null, aber length nennt dir die Größe der Liste (ab Eins zählend), sodass du eins abziehen musst, um den tatsächlichen Index zu erhalten.
Beispiel 6–2 ist der Arduino-Sketch. Du kannst ihn auch über den Link für die Beispielcodes von der Katalogseite der englischen Originalausgabe des Buches herunterladen (https://makezine.com/go/arduino-4e-github/).
Beispiel 6–2Arduino-Netzwerk-Lampe (Arduino-Sketch)
const int SENSOR = 0;
const int R_LED = 9;
const int G_LED = 10;
const int B_LED = 11;
const int BUTTON = 12;
int val = 0; // variable to store the value coming from the sensor
int btn = LOW;
int old_btn = LOW;
int state = 0;
char buffer[7] ;
int pointer = 0;
byte inByte = 0;
byte r = 0;
byte g = 0;
byte b = 0;
void setup() {
Serial.begin(9600); // open the serial port
pinMode(BUTTON, INPUT);
}
void loop() {
val = analogRead(SENSOR); // read the value from the sensor
Serial.println(val); // print the value to
// the serial port
if (Serial.available() > 0) {
// read the incoming byte:
inByte = Serial.read();
// If the marker's found, next 6 characters are the colour
if (inByte == '#') {
while (pointer < 6) { // accumulate 6 chars
buffer[pointer] = Serial.read(); // store in the buffer
pointer++; // move the pointer forward by 1
}
// now we have the 3 numbers stored as hex numbers
// we need to decode them into 3 bytes r, g and b
r = hex2dec(buffer[1]) + hex2dec(buffer[0]) * 16;
g = hex2dec(buffer[3]) + hex2dec(buffer[2]) * 16;
b = hex2dec(buffer[5]) + hex2dec(buffer[4]) * 16;
pointer = 0; // reset the pointer so we can reuse the buffer
}
}
btn = digitalRead(BUTTON); // read input value and store it
// Check if there was a transition
if ((btn == HIGH) && (old_btn == LOW)){
state = 1 - state;
}
old_btn = btn; // val is now old, let's store it
if (state == 1) { // if the lamp is on
analogWrite(R_LED, r); // turn the leds on
analogWrite(G_LED, g); // at the colour
analogWrite(B_LED, b); // sent by the computer
} else {
analogWrite(R_LED, 0); // otherwise turn off
analogWrite(G_LED, 0);
analogWrite(B_LED, 0);
}
delay(100); // wait 100ms between each send
}
int hex2dec(byte c) { // converts one HEX character into a number
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
}
}
Abb. 6–2 zeigt den Zusammenbau der Schaltung. Verwende genau wie bei der »Lichtsteuerung mit PWM« auf Seite 60 in Kapitel 5 einen 220-Ω-Widerstand (rot-rot-braun) für jede LED und genau wie bei »Analoge Eingabe« auf Seite 71 einen 10-kΩ-Widerstand mit dem Fotowiderstand.
Erinnere dich, dass LEDs wie bei der »Lichtsteuerung mit PWM« auf Seite 60 polarisiert sind: In dieser Schaltung muss die Anode (langer Leiter, positiv) auf die rechte Seite gehen, die Kathode (kurzer Leiter, negativ) nach links. Abb. 6–2 zeigt auch die abgeflachte Seite der LED, die die Kathode kennzeichnet.
Bau die Schaltung wie dargestellt, verwende eine rote, eine grüne und eine blaue LED. Als Nächstes lade die Sketche in Arduino und Processing. Lade den Arduino-Sketch in den Arduino hoch, dann führe den Processing-Sketch aus und probiere ihn aus (du musst den Taster drücken, damit sich die Lampe einschaltet). Wenn du irgendwelche Probleme hast, schau in Kapitel 11 nach.
Statt drei separate LEDs zu verwenden, kannst du eine einzelne RGB-LED einsetzen, die vier Leiter aufweist. Du schließt sie auf sehr ähnliche Weise wie die in Abb. 6–2 gezeigten LEDs an, mit einer Änderung: Statt dreier separater Anschlüsse am Masse-Pin des Arduino führt ein einzelner Leiter (gemeinsame Kathode genannt) zur Masse.
Adafruit verkauft eine RGB-LED mit vier Leitern für ein paar Euro: https://www.adafruit.com/product/159. Im Gegensatz zu einzelnen einfarbigen LEDs ist der längste Leiter bei dieser Art von RGB-LED jener, der zur Masse führt. Die drei kürzeren Leiter müssen an den Arduino-Pins 9, 10 und 11 angeschlossen werden (mit einem 220-Ω-Widerstand zwischen den Leitern und den Pins, genau wie bei den separaten roten, grünen und blauen LEDs).
Das im Vorwort erwähnte Set enthält ebenfalls eine RGB-LED.
Der Arduino-Sketch ist für den Betrieb mit einer RGB-LED mit gemeinsamer Kathode ausgelegt (bei der der lange Anschlussdraht mit Masse verbunden wird). Wenn du eine falsche Ausgabe erhältst, hast du möglicherweise eine RGB-LED mit gemeinsamer Anode. Wenn das der Fall ist, ändere den Code an den Stellen, wo du die Intensität der LED einstellst, wie dargestellt (du kehrst die Werte im Grunde genommen um; wo du 0 verwendet hattest, nutzt du jetzt 255):
if (state == 1) { // wenn die Lampe an ist
analogWrite(R_LED, 255 - r); // schalte die LEDs
analogWrite(G_LED, 255 - g); // in der vom Computer
analogWrite(B_LED, 255 - b); // gesendeten Farbe ein
} else {
analogWrite(R_LED, 255); // andernfalls abschalten
analogWrite(G_LED, 255);
analogWrite(B_LED, 255);
}
Lass uns nun die Konstruktion abschließen, indem wir die Steckplatine in eine Glaskugel stecken. Der einfachste und billigste Weg ist hier der Kauf einer FADO-Tischleuchte von IKEA. Diese wird gegenwärtig für etwa 15 € angeboten.
Entpacke die Lampe und entferne das Kabel, das von unten her in die Lampe hineinführt. Du wirst sie nicht mehr an die Wandsteckdose anschließen.
Du kannst zum Befestigen des Arduino auf der Steckplatine ein Gummiband verwenden und dann die Steckplatine mit Heißleim an die Rückseite der Lampe kleben, wie in Abb. 6–1 dargestellt. Lass etwas Platz, damit du die LED einsetzen kannst, und klebe sie dann fest.
Löte längere Drähte an die RGB-LED und klebe diese dort fest, wo die Glühbirne saß. Verbinde die Drähte der LED mit der Steckplatine (an der Stelle, an der sie sich befanden, bevor du sie entfernt hast). Du kannst ein wenig Zeit sparen, indem du dir vergegenwärtigst, dass du nur eine Verbindung zur Masse benötigst, egal ob du die RGB-LED oder drei separate LEDs verwendest.
Such dir jetzt ein schönes Stück Holz mit einem Loch in der Mitte, das als Ständer für die Kugel dienen kann. Oder schneide den oberen Teil des Kartons, in dem die Lampe verpackt war, so zurecht, dass er eine Höhe von etwa 5 cm erhält. Dann schneide ein Loch mit zur Lampe passenden Durchmesser hinein. Verstärke die Innenseite des Kartons mit Heißleim an allen inneren Kanten, damit der Sockel stabiler wird.
Platziere die Kugel auf dem Ständer, führe das USB-Kabel oben aus der Lampe heraus und verbinde es mit dem Computer.
Starte deinen Processing-Code, drück den Ein/Aus-Taster und erlebe, wie die Lampe zum Leben erwacht. Lade deine Freunde ein und bring sie zum Staunen!
Versuche zur Übung, Code hinzuzufügen, der die Lampe einschaltet, wenn es im Zimmer dunkel wird. Weitere mögliche Verfeinerungen wären:
Denk dir verschiedene Dinge aus, experimentiere und habe Spaß!