Arduino
Einführung
Der Arduino ist ein Mikrokontroller, welcher über eine Entwicklungsumgebung programmiert und geladen wird. Anschliessend läuft das Programm (Sketch genannt) selbständig auf dem Kontroller. Im Gegensatz zu einem Minicomputer wie dem Raspberry Pi kann aber immer nur ein Programm gleichzeitig auf dem System laufen.
Der Arduino sollte auf gewisse Maximalströme beschränkt werden: Siehe dazu die Antwort im Diskussionsforum.
Im Gegensatz zum Raspi hat der Arduino mehrere PWM-Pins. Diese sind mit einer Tilde (~) gekennzeichnet. Es handelt sich um die Pins 3, 5, 6, 9, 10, 11. Die Frequenz auf den Pins beträgt 490 Hz, wobei die Pins 5 and 6 wegen internen Timern den doppelten Takt von 980 Hz haben.
Installation und Programmierung
Programme für den Arduino werden in der Entwicklungsumgebung (Download) geschrieben, dann auf den Arduino übertragen und dort ausgeführt. Für die Installation des USB-Treibers wird der Arduino nach der Installation der IDE mit dem PC verbunden. Installiert man das Ganze in eine virtuelle Maschine, so muss man das Gerät anschliessend darauf mappen. Danach fragt das Betriebsystem nach einem Treiber, der sich im Unterverzeichnis der IDE befindet, falls WIndows den Treiber nicht selbständig installiert.
Für die Verbindung zum Gerät findet man anschliessend den virtuellen Comport in der Computerverwaltung.
Die Sprache ist an c++ angelehnt, doch man braucht sich nicht um Header-Dateien zu kümmern, sondern schreibt einfach das gewünschte Programm. Die Datei ist reiner Text und wird mit der Endung .ino gespeichert. Die Programmierung ist ähnlich wie Python und die Initialisierung der Hardware ähnlich wie beim Raspberry Pi.
Bibliotheken verwenden
Die IDE hat schon diverse Bibliotheken drin (Sketch -> Include Library). Möchte man aber für Sensoren externe Bibliotheken einbinden, so kann man das Tutorial von Heise verwenden.
Variablen
Variablen, die ausserhalb der Funktionen definiert werden, sind global und daher in allen Funktionen sichtbar. Im Gegensatz zu Python kann man diese dann in einer Funktion ohne Schlüsselwort verändern.
Verwendet man vor der Definition const
, so ist es eine Konstante, deren Werte nicht mehr geändert werden darf.
Für die Variablen gibt es folgende Bereiche:
- int -> ganze Zahlen von ??
- float ?
Befehle
Auflistung der wichtigsten Befehle. Die Referenz der Funktionen befindet sich auf der Referenz-Homepage.
Name | Beschrieb | Beispiel |
---|---|---|
digitalRead(Pin) | Liest den aktuellen Zustand eines digitalen Pins, dessen Nummer man im Parameter mitgibt. Als Rückgabe erhält man HIGH oder LOW. | zustand = digitalRead(4); |
analogRead(Pin) | Liest den Wert des analogen Pins im Parameter. Der Rückgabewert liegt zwischen 0..1023. | zuckergehalt = analogRead(A2); |
analogWrite(Pin, Wert) | Schreibt auf einen digitalen PWM-Pin den gewünschten Wert (0..255). Mehr Details zu PWM auf dem Arduino in der Einführung. | analogWrite(11, 214); |
digitalWrite(Pin, Zustand) | Setzt den übergebenen Pin in den gewünschten Zustand (HIGH / LOW). | digitalWrite(7, HIGH); |
pinMode(Pin, Modus) | Digitale Pins als Ein- oder Ausgänge festlegen. | pinMode(3, OUTPUT); |
if(1.Wert Bedingung 2.Wert) {} else {} | Vergleicht, ob die Bedingung zutrifft. In diesem Fall wird der if-Teil ausgeführt. Optional kann im nichtzutreffenden Fall in den else-Teil verzweigt werden. | if(alpha != beta) { print("Ungleich"); } else { print("Gleich"); } |
delay(Zeit) | Stoppt die Programmausführung für die angegebene Zeitdauer in Milisekunden. Während dieser Zeit werden auch keine Schalter eingelesen. Entsprechend sollte stattdessen mit Timer und/oder Interrupts gearbeitet werden. | delay(500); |
Serial.begin(Baudrate) | Starten der seriellen Schnittstelle mit der angegebenen Baudrate (Standard 9600). Der Start wird im setup() durchgeführt. | Serial.begin(9600) |
Serial.print(Inhalt) | Ausgabe von Text oder Variablen auf die serielle Schnittstelle. Bei Text muss dieser in Anführungszeichen stehen. Zeilenumbruch mit println. In Kombination mit F kann die Ausgabe formatiert werden. | Serial.print("Text: ");Serial.println(Variable) |
for(int Start; Endbedingung; Zähler) {} | Zählt die angegebene Anzahl Schleifendurchgänge. | for(int i = 0; i >= 5; i++) {} |
setup() | Standardmässige Funktion, welche beim Start des Controllers 1 Mal durchlaufen wird. | void setup() { } |
loop() | Standardmässige Funktion, welche nach dem Verlassen der setup()-Funktion dauerhaft durchlaufen wird, bis man den Kontroller abschaltet. | void loop() { } |
tone() | Im Gegensatz zu analogWrite() wird hier keine fixe Frequenz verwendet, sondern man gibt die Frequenz für den Pin vor, wobei aber das Tastverhältnis immer 50% beträgt. Der generierte Ton ist entsprechend immer gleich laut. | tone(9, 456); |
Taster einlesen und LED einschalten
// button.ino - light a led by pressing button // (c) BotBook.com - Karvinen, Karvinen, Valtokari // globale Variablen definieren, die in loop() und setup() bekannt sein müssen int buttonPin = 2; // Die Pin-Nummern sind auf dem Arduino direkt ersichtlich. int ledPin = 13; // Die eingebaute LED ist mit dem Pin 13 gekoppelt. int buttonStatus = LOW; // Initialisierung des Status void setup() { // Diese Routine wird beim Start einmalig durchlaufen und dient zur Initialisierung der Hardware. pinMode(ledPin, OUTPUT); // Pin als Output definieren pinMode(buttonPin, INPUT); // Pin als Input definieren digitalWrite(buttonPin, HIGH); // Den internen Pull-Up-Widerstand aktivieren. // Die vorherigen zwei Befehle können folgendermassen zusammengefasst werden: pinMode(buttonPin, INPUT-PULLUP); } // end setup() void loop() { // Diese Schleife wird bis zum Abschalten des Arduino unendlich abgearbeitet buttonStatus = digitalRead(buttonPin); // Den Status des Tasters einlesen if (LOW == buttonStatus) { // Wenn der Widerstand auf GND gezogen wird, ist der Taster gedrückt. // Dadurch dass man die Konstante nach vorne nimmt, verhindert man Fehler, falls man ein = vergisst. digitalWrite(ledPin, HIGH); // LED einschalten } // end if else { digitalWrite(ledPin, LOW); // sonst die LED ausschalten } // end else } // end loop()
Das Programm kann von der Webseite vom Sensorbuch heruntergeladen werden.
Serielle Schnittstelle
Der Arduino kann über die digitalen Pins 0 und 1 seriell mit einem Gerät kommunizieren. Möchte man aber nur Werte vom Arduino auslesen oder debuggen, so kann man dies direkt über das USB-Verbindungskabel erledigen.
Hier ein Beispiel für die Ausgabe eines analogen Wertes über die serielle Schnittstelle. Ausführliche Kommentare stehen im Abschnitt oben.
// squeeze_serial.ino - flexiforce squeeze level to serial // (c) BotBook.com - Karvinen, Karvinen, Valtokari int squeezePin = A0; int force = -1; // Da die analoge Schnittstelle Werte von 0-1023 liefert sieht man, ob Werte ankommen void setup() { Serial.begin(9600); // Verbindung mit 9600 bit/s herstellen. Im Arduino Monitor ist dies die Standard-Geschwindigkeit. } // end setup() void loop() { force = analogRead(squeezePin); Serial.println(force); // Den Wert auf die serielle Schnittstelle ausgeben. delay(500); } // end loop()
Sensoren
Genau wie beim Raspi gibt es analoge und digitale Sensoren für den Arduino. Dieser hat aber direkt fünf analoge Eingänge, so dass diese Sensoren direkt verarbeitet werden können. Beim 5V-Arduino werden alle Pegel > 3V als HIGH und alle < 1.5V als LOW angesehen. Zustände dazwischen sind undefiniert und sollten nicht für digitale Pins verwendet werden. Damit nicht alle Sensoren und Aktoren doppelt unter dem Raspi und dem Arduino geführt werden, auch wenn das Auslesen schlussendlich ähnlich ist, verweise ich jeweils auf den anderen Abschnitt und erfasse nur die jeweiligen Unterschiede. Für weitere Infos also unter dem Raspi nachschauen.
Druck (Kraft)
Der Anschluss wird beim Raspi beschrieben und das Einlesen findet sich im Abschnitt über den Potentiometer.
Infrarot (Entfernung)
Sensor / Schranke
Hier sendet eine eine Infrarot-Diode Licht aus, das reflektiert und von der lichtempfindlichen Empfängerdiode wieder aufgefangen wird. Starkes Sonnenlicht oder sehr dunkle Oberflächen können aber dazu führen, dass der Empfänger den ausgesendeten Strahl nicht detektiert. Je nach Gerät kann man die Empfindlichkeit mit einem kleinen Trimm-Poti einstellen.
Fürs verarbeiten des Signals kann man das gleiche Programm wie für den Taster einlesen verwenden, da der Sensor nur ein digitales Signal schaltet, kann man nicht erkennen, wie weit man von etwas entfernt ist, sondern nur, dass sich etwas im eingestellten Bereich des Senders befindet. Um den (genauen) Abstand zu detektieren braucht es einen analogen Abstandsmesser.
Abstandsmesser
Für analoge Abstandswerte kann man den [GP2Y0A] verwenden, der im Bereich von 10-80cm arbeitet. Da hier das Sonnenlicht oder extrem dunkle oder gestreute Fläche Falschmessungen liefern können, gibt es als Alternative Ultraschallsensoren.
Potentiometer
Dieser variable WIderstand fungiert als Spannungsteiler. Entsprechend wird eine Seite mit 5V gespiesen und die andere Seite an GND angeschlossen. Der mittlere Pin hingegen an A0. Entsprechend der Stellung ist die Differenzspannung nun grösser oder kleiner. Diese Spannung wird dann umgerechnet und man erhält einen Wert zwischen 0 und 1023. Je nach Auflösung des Analogbausteines kann der Wert noch verfeinert werden. Es ist egal, wie man das Poti anschliesst. Je nach Anschluss sind einfach die ma. und min. Werte vertauscht. In einem Programm muss man dies dann halt kalibrieren oder das Poti umgekehrt anschliessen.
Dieses Verfahren wird bei allen analogen Sensoren verwendet. Man muss dabei nur wissen, welche Stellung welchen Wert liefert, um die Sensoren korrekt auszuwerten. Wenn der Sensor feine Werte liefert, muss man evtl. einen zusätzlichen Analogbaustein verwenden, welcher diese Werte besser auflöst als der interne Baustein des Arduino, der 10 Bit liefert.
Um den Arduino nicht zu überlasten, sollte das Potentiometer einen Mindestwert von 50 nicht unterschreiten, damit nicht mehr als 100mA fliessen. Allgemein sollte mit möglichst grossen Widerständen gearbeitet werden, um den thermischen Verlust möglichst klein zu halten. Entsprechend kann man gut ein 10k-Poti einsetzen.
// pot.ino - control LED blinking speed with potentiometer // (c) BotBook.com - Karvinen, Karvinen, Valtokari // Es wird nur "neues" kommentiert. Grundlegende Kommentare im Abschnitt Programmierung int potPin = A0; // Analoge Eingänge werden mit Ax gekennzeichnet int ledPin = 13; int poti = 0; // Mögliche Werte von 0 bis 1023 void setup() { pinMode(ledPin, OUTPUT); } // end setup() void loop() { poti = analogRead(potPin); // Die Funktion liest den Wert am gegebenen Pin ein digitalWrite(ledPin, HIGH);// LED einschalten delay(poti/10); // Warten um den Wert / 10 Millisekunden digitalWrite(ledPin, LOW); delay(poti/10); } // end loop()
Schall
Sensoren und ein Link mit Anwendungen befinden sich beim Raspi. Hier noch ein möglicher Besipielcode, bei dem man die Berechnungen sieht.
// hc-sr04.ino - distance using ultrasonic ping sensor // (c) BotBook.com - Karvinen, Karvinen, Valtokari int trigPin = 2; // löst die Messung aus int echoPin = 3; // beendet die Messung float v = 331.5 + 0.6 * 20; // Schallgeschwindigkeit bei 20°C in m/s. Mit einem Temperaturfühler könnte man hier auch die Variable anpassen. void setup() { Serial.begin(115200); pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); } // end function setup() float distanceCm(){ digitalWrite(trigPin, LOW); // Trigger-Pin-Flanke generieren delayMicroseconds(3); // Warten, damit die Flanken sauber generiert werden digitalWrite(trigPin, HIGH); delayMicroseconds(5); digitalWrite(trigPin, LOW); float tUs = pulseIn(echoPin, HIGH); // Die Funktion liefert die gemessene Zeit in Mikrosekunden float t = tUs / 1000.0 / 1000.0 / 2.0; // Die Zeit in Sekunden umrechnen return t * v * 100; // Distanz in cm zurückliefern } // end function distanceCm() void loop() { int d = distanceCm(); // Rückgabewert in ganze cm Serial.println(d, DEC); // Wert als Dezimalzahl auf der ser. Konsole ausgeben delay(200); } // end function loop()
Temperatursensor
Damit können Temperaturen direkt eingelesen werden. Ein Beschrieb findet sich beim Raspi. Vollständige Kommentare zum Programm im Abschnitt zum Potentiometer. Infos zur ser. Schnittstelle im entsprechenden Abschnitt.
// temperature_lm35.ino - LM35 temperature in Celcius to serial // (c) BotBook.com - Karvinen, Karvinen, Valtokari int lmPin = A0; void setup() { Serial.begin(9600); } // end setup() float tempC() { float raw = analogRead(lmPin); // Dieser Wert könnte auch direkt im loop ausgelesen und als Parameter der Funktion übergeben werden. float percent = raw / 1023.0; // Prozentwert des Rohwertes als Floatzahl berechnen float volts = percent * 5.0; // Beim LM35 ergibt sich damit die absolute Spannung in Millivolt. Diese hängt von der Reverenzspannung (5V) ab. return 100.0 * volts; // Jetzt hat man die Temeperatur in °C } // end tempC() void loop() { Serial.println(tempC()); delay(200); } // end loop()
1-wire Temperatursensor
Auch der Arduino unterstützt die 1-Wire-Schnittstelle. Es gibt dazu auf der Arduino-Homepage eine Anleitung. Um die entsprechenden Geräte einzubinden, gibt es allgemeine oder spezifische Bibliotheken.
// Example testing sketch for various DHT humidity/temperature sensors (https://github.com/adafruit/DHT-sensor-library/blob/master/examples/DHTtester/DHTtester.ino) // Written by ladyada, public domain // REQUIRES the following Arduino libraries: // - DHT Sensor Library: https://github.com/adafruit/DHT-sensor-library // - Adafruit Unified Sensor Lib: https://github.com/adafruit/Adafruit_Sensor #include "DHT.h" #define DHTPIN 8 // Digital pin connected to the DHT sensor //#define DHTTYPE DHT11 // DHT 11 #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 //#define DHTTYPE DHT21 // DHT 21 (AM2301) // Connect pin 1 (on the left) of the sensor to +5V // Connect pin 2 of the sensor to whatever your DHTPIN is // Connect pin 3 (on the right) of the sensor to GROUND (if your sensor has 3 pins) // Connect pin 4 (on the right) of the sensor to GROUND and leave the pin 3 EMPTY (if your sensor has 4 pins) // Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor DHT dht(DHTPIN, DHTTYPE); // Initialize DHT sensor. void setup() { Serial.begin(9600); Serial.println(F("DHTxx test!")); dht.begin(); } // end setup() void loop() { delay(2000); // Wait a few seconds between measurements. // Reading temperature or humidity takes about 250 milliseconds! // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) float h = dht.readHumidity(); float t = dht.readTemperature(); // Read temperature as Celsius (the default) float f = dht.readTemperature(true); // Read temperature as Fahrenheit (isFahrenheit = true) // Check if any reads failed and exit early (to try again). if (isnan(h) || isnan(t) || isnan(f)) { Serial.println(F("Failed to read from DHT sensor!")); return; } // end if float hif = dht.computeHeatIndex(f, h); // Compute heat index in Fahrenheit (the default) float hic = dht.computeHeatIndex(t, h, false); // Compute heat index in Celsius (isFahreheit = false) Serial.print(F("Luftfeuchte: ")); Serial.print(h); Serial.print(F("% Temperatur: ")); Serial.print(t); Serial.print(F("°C ")); Serial.print(f); Serial.print(F("°F Gefühlte Temperatur: ")); Serial.print(hic); Serial.print(F("°C ")); Serial.print(hif); Serial.println(F("°F")); } // end loop()
Aktoren
Servo-Motor
Durch die Servo-Bibliothek können Motoren einfach mit dem anzusteuernden PWM-Pin angeschlossen werden:
#include <Servo.h> // Bibliothek, um die Ansteuerung zu abstrahieren. Servo myServo; const int motor = 9; const int poti = A0; int potiWert = 0; int winkel = 0; void setup() { myServo.attach(motor); } // end setup() void loop() { potiWert = analogRead(poti); winkel = map(potiWert, 0, 1023, 0, 255); // Den Eingangswert von 0..1023 auf 0..255 mappen, da der PWM-Ausgang nur diesen Bereich hat myServo.write(winkel); // Ausgabe der neuen Position delay(15); // Wartezeit, damit sich der Motor bewegen kann } // end loop()