Arduino

Aus m-wiki
Zur Navigation springen Zur Suche springen

Einführung

Arduino Uno

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.

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.

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). 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() { }

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