Version 2016-01-09
Diese Einführung basiert auf den Tutorials auf arduino.cc, der Einführung von .:oomlout:., Tipps aus der Literatur, dem arduino-Forum und den IC-Tutorials von doctronics. Eigentlich ist bei diesen hervorragenden Quellen kein eigenes, weiteres Skript mehr nötig. Bei meinen Schülern enstand jedoch der Wunsch nach einem an den Unterricht angelehnten, deutschsprachigen Skript. Diese Seite enthält nicht meinen Unterrichtsgang, jedoch viele im Unterricht verwendete Beispiele.
Im Unterricht verwende ich sehr gerne Teile (Sensoren, Servos, Arduino-Shields) aus den Sortimenten von Sparkfun und Parallax, welche in Deutschland günstig bei Elmicro erhältlich sind. Standardteile sind z.B. bei Conrad, csd-electronics, Pollin, Reichelt, Segor und Watterott erhältlich.
Die Fotos/Grafiken enstammen den zitierten Datenblättern oder wurden selbst fotografiert oder mit fritzing erstellt oder enthalten Quellenangaben. Formeln wurden mit dem online-TEX-Formelgenerator von codecogs.com erzeugt.
Über Tipps und Hinweise freue ich mich! Bitte schicke eine Email an frerk@popovic.info.
Diese Seite steht unter der creative-commons-Lizenz CC BY-SA.
Unser arduino wird C-ähnlich programmiert.Jedes Programm besteht dabei aus zwei Blöcken: Dem setup()-Block, der genau einmal ausgeführt wird, und dem loop()-Block, der anschließend so lange wiederholt wird, bis das Gerät ausgeschaltet wird:
void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(9600); }
void loop() {
Serial.println("Hallo Welt!" );
// wait 10 milliseconds before the next loop
delay(10); }
In Beispiel wird die der Serial Monitor benutzt, um über ihn eine Nachricht auszugeben. Auch wenn du an dieser Stelle noch nicht verstanden hast, wie das gemacht wird, solltest du es dir merken, denn später werden wir diese Ausgaben oft zur Fehlersuche verwenden.
Achtung: Wenn du die serielle Schnittstelle benutzt, darfst du die Ports 0 und 1 nicht verwenden!
Kommentare dienen zur besseren Verständlichkeit des Quelltextes, sie werden beim Compilieren der Programme ignoriert. Du erkennst sie daran, dass sie in der Entwicklungsumgebung grau dargestellt sind. Kommentare kannst du mittels //... oder /* ... */ erzeugen.
Der digitale Port 13 besitzt eine integrierte LED, d.h. du benötigst eigentlich keine separate. Falls du dennoch eine anschließen möchtest, achte auf einen passenden Vorwiderstand und darauf, dass die LED richtig herum sitzt (abgeflachte Seite Richtung GND).
/*
Blink
Eine Leuchtdiode wird eine Sekunde an, und dann wieder eine Sekunde ausgeschaltet. Dieser Vorgang wird endlos wiederholt.
*/
void setup() { // Port 13 wird als Ausgang festgelegt
pinMode(13, OUTPUT); }
void loop() {
digitalWrite(13, HIGH); // Port 13 wird high geschaltet
delay(1000); // eine Sekunde warten
digitalWrite(13, LOW); // Port 13 wird low geschaltet
delay(1000); // eine Sekunde warten
}
Eine LED lässt sich im Gegensatz zu einer herkömmlichen Glühbirne nicht dimmen, sondern lediglich ein- oder ausschalten. Wird eine LED jedoch sehr schnell abwechselnd ein- und wieder ausgeschaltet, so nehmen wir Menschen eine geringere Helligkeit wahr.
Beachte: Nur die mit der "~" gekennzeichneten Ports können für die PWM genutzt werden.
Achtung: analogWrite()
hat nichts mit analogen Ports zu tun. Du benutzt die (digitalen) mit ~
als PWM-Ports markierten Ports und brauchst sie vorher nicht einmal
mittels
pinMode() als Ausgang
deklarieren.
Digitale Ports können wahlweise als Ein- oder Ausgang festgelegt werden. Benutze folgendes Programm, um einen Wert einzulesen:
/*
DigitalReadSerial
Die Daten an Port 2 werden eingelesen und auf dem Serial Monitor ausgegeben.
*/
void setup() {
Serial.begin(9600);
pinMode(2, INPUT);
}
void loop() {
int sensorValue = digitalRead(2);
Serial.println(sensorValue, DEC);
}
Tipp: Wenn du einmal zu wenige digitale Ports hast, kannst du einfach die analogen Ports A0 bis A5 als digitale Ports verwenden. Sie erhalten dann die Nummern von 14 bis 19 . Alternativ kannst du ein Schieberegister oder einen Portexpander verwenden.
Der Mikrocontroller hat einen Analog-Digitalwandler an Board. Werte, die du auf den analogen Ports (z.B. A0) einliest, werden in digitale Werte gewandelt. Die Auflösung beträgt dabei 10 bit, d.h. die Messwerte liegen zwischen 0 und 1023:
Beachte: Die analogen Ports werden nicht als Ein- bzw. Ausgang definiert!
Baue eine Spannungsteilerschaltung mittels Potentiometer (veränderbarer Widerstand) so auf, dass du das Potenzial an A0 variieren kannst.
const int analogInPin = A0; // Analog input pin
int sensorValue = 0; // value read
void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(9600); }
void loop() {
// Lese den analogen Wert ein
sensorValue = analogRead(analogInPin);
// Gib den Wert mittels Serial Monitor aus
Serial.print("sensor = " ); Serial.print(sensorValue);
// Warte 0,5 Sekunden
delay(500); }
Ein LDR (light dependent resistor = Fotowiderstand) ist ein Bauteil, dessen Widerstand sich mit der Helligkeit ändert.
Ein NTC (negative temperature coefficient thermistor = Heißleiter) ist ein Bauteil, dessen Widerstand sich mit zunehmender Temperatur kleiner wird.
Tipp: Später lernst du den Sensor LM35DZ kennen, der dir direkt die Temperatur in °Celsius liefert.
Du kannst einen Lautsprecher ohne Transistor direkt am Port betreiben. In diesem Beispiel ändert sich die Tonhöhe mit dem Lichteinfall:
// Original von Tom Igoe, siehe http://arduino.cc/en/Tutorial/Tone2
const int ldrPin = A0;
const int lautsprecher = 9;
void setup() {
Serial.begin(9600);
}
void loop() {
int sensorReading = analogRead(ldrPin);
Serial.println(sensorReading);
// auf die Ausgabefrequenz zwischen 120 und 1500 Hz mappen
int tonHoehe = map(sensorReading, 400, 1000, 120, 1500);
tone(lautsprecher, tonHoehe, 10);
delay(1); // benutze "noTone(lautsprecher);", um die Tonausgabe zu unterbrechen
}
Bemerkung: Verwende eine Transistorschaltung (z.B. den BD437), um ein größere Lautstärke zu erzielen und ein Potentiometer, um die Lautstärke regeln zu können.
Die Arduinos besitzen drei Arten von Speicher:
Beachte: Die Daten im flash-memory und im eeprom bleiben auch ohne Batterie erhalten, die Daten im SRAM sind dagegen verloren. Wenn dir während der Laufzeit das SRAM zur Neige geht, kann es sein, dass das Programm sich unerwartet beendet. Abhilfe kannst du schaffen, indem du z.B. eher byte als int als Variablentyp wählst (du sparst damit ein Byte) oder indem du Daten ins flash-memory auslagerst, siehe PROGMEM.
Es gibt Situationen in denen sich der µC "aufhängt", z.B. wenn millis() nach 49 Tagen einen overflow produziert. Dafür gibt es den "Rettungshund" wdt: Kommt zu lange kein Lebenszeichen, so wird ein Interrupt ausgelöst, der einen Reset des Controllers bewirkt. Der watch-dog-timer muss jedoch explizit gestartet werden:
--->Lücke<---
Schließe einen Push-Button ("Schließer") von 5 V aus an den digitalen Port 2 an und von dort mit einem 10 kΩ - pull-down-Widerstand an Masse.
/*
Quelle http://www.arduino.cc/en/Tutorial/Button
*/
const int buttonPin = 2; // Port, an den der Push-Button angeschlossen wird
const int ledPin = 13; // Port, an den die LED angeschlossen ist
int buttonState = 0; // Variable, die den Zustand des Push-Buttons speichert
void setup() {
// Lege den lED-Port als Ausgang fest:
pinMode(ledPin, OUTPUT);
// der buttonPin-Port wird als Eingang festgelegt:
pinMode(buttonPin, INPUT); }
void loop(){
// Der Zustand des Schalters wird ausgelesen und gespeichert:
buttonState = digitalRead(buttonPin);
// Prüfe, ob der Schalter gedrückt ist.
// Wenn ja, so ist buttonState high und
if (buttonState == HIGH) {
// die LED wird eingeschaltet:
digitalWrite(ledPin, HIGH); }
else {
// ansonsten wird sie ausgeschaltet:
digitalWrite(ledPin, LOW); }
}
Überprüft man die Aussage "a = b" so kann die Antwort nur true oder false lauten. Bei den meisten Programmiersprachen muss man den Vergleichsoperator mit zwei Gleichheitszeichen schreiben:
a == b | überprüft, ob a = b ist |
a != b | überprüft, ob a ungleich b ist. |
a > b | überprüft, ob a echt größer als b ist |
a <= b | überprüft, ob a kleiner oder gleich b ist |
Bislang haben wir lediglich die Endloswiederholung mittels loop() benutzt. Nun wollen wir eine while()-Schleife benutzen, um eine LED nach dem Einschalten genau 27 mal blinken lassen. Du brauchst keine neue Schaltung aufzubauen, da wir die integrierte LED an Port13 nutzen.
int i = 27;
int pausenZeit = 500;
void setup() {
pinMode(13, OUTPUT);
while (i>0){
i--; // i wird um 1 erniedrigt, kurz für i = i-1;
digitalWrite(13, HIGH);
delay(pausenZeit);
digitalWrite(13, LOW);
delay(pausenZeit);
}
}
void loop() { }
Eine Leuchtdiode an Port 13 soll genau 27 mal blinken. Danach soll nichts mehr passieren.
const int ledPin = 13;
int pausenZeit = 500;
void setup() {
pinMode(ledPin, OUTPUT);
for (int i = 0; i < 27; i++) {
digitalWrite(ledPin, HIGH);
delay(pausenZeit);
digitalWrite(ledPin, LOW);
delay(pausenZeit);
}
}
void loop() { }
Wenn du ein Lauflicht aus z.B. 5 LEDs basteln möchtest, so könntest du jede nacheinander ein- und wieder ausschalten. Bei vielen LEDs wird das aber sehr umständlich. Wir verwenden in diesem Beispiel Arrays, um das Problem elegant zu lösen.
Ein array kannst du dir wie einen Schuladenkasten vorstellen. Die Schuladen sind mit der 0 beginnend nummeriert, hier fünf Schubladen von 0 - 4. Wie bei Variablen üblich musst du vorher festlegen, was in die Schublade reingesteckt werden darf, d.h. den Datentyp angeben (hier int).
int meineLEDs[5]; // ein Schubladenkasten der Länge fünf, also mit Schubladen von 0 bis 4 wird angelegen
void setup() {
// Das array füllen:
for (int i = 0; i < 5; i++) {
// Die 5 LEDs hängen an den Ports 2 bis 6:
meineLEDs[i] = i + 2; // meineLEDs[0]=2, meineLEDs[1]=3, ..., meineLEDs[4]=6
}
Serial.begin(9600);
// LED-Ports 2 bis 6 sind Ausgänge:
for (int i = 0; i < 5; i++) {
pinMode(meineLEDs[i], OUTPUT);
}
}
void loop() {
// LED-Lauflicht:
for (int i = 0; i < 5; i++) {
digitalWrite(meineLEDs[i], HIGH);
delay(1000);
Serial.print("Jetzt leuchtet die LED an Port Nr. "); Serial.println(meineLEDs[i]);
digitalWrite(meineLEDs[i], LOW);
}
}
Die Ausgabe am serial monitor:
Achtung: Anders als wenn du z.B. in Java programmierst bekommst du in C (und damit auch beim Arduino) keine Fehlermeldung des Compilers, wenn du versuchst auf eine "Schublade" zuzugreifen, die es gar nicht gibt. Versucht man im obigen Beispiel auf die nicht vorhandene "6. Schublade" zuzugreifen, so wird ein zufälliger Wert zurückgeliefert:
werden z.B. im Fahrstuhl zur Anzeige des Stockwerkes benutzt. Sie bestehen aus 7 (bzw. 8) LED's. Es gibt Modelle mit gemeinsamer Anode, z.B. SA52-11 (Datenblatt von Kingbright), und Modelle mit gemeinsamer Kathode, z.B. SC52-11. Die Verdrahtung ist zum Glück bis auf die Vertauschung von +5V und Masse identisch.
Die einfachste Variante zum Anschluss einer 7-Segment-Anzeige ist die direkte Verkabelung der einzelnen LED's. Geeignete Vorwiderstände müssen verwendet werden:
Besonders wenn mehrere 7-Segment-Anzeigen angeschlossen werden sollen, werden die Ports sehr schnell knapp. Abhilfe schafft ein "shift register": Das 74HC595 (Datenblatt von Philips) hat einen seriellen Eingang (DS), 8 parallele Ausgänge (Q0 bis Q7) und einen seriellen Ausgang (Q7'). Letzteren kannst du zur Kaskadierung von Anzeigen nutzen.
Man schickt Bit für Bit Information vom µC an das Schieberegister, welches jedes Bit bei einem neu eintreffenden um eine Position weiter schiebt. Arduinos kennen zum Glück ShiftOut(), damit wird ein Byte an Information, z.B. 11001100, an den IC gesendet und man benötigt dazu lediglich drei Ports. Der 74HC595 gibt diese Information an seinen 8 parallelen Ausgängen aus, d.h. er kann 8 Ports steuern!
Achtung: In der Grafik wurde zwecks Übersichtlichkeit auf die passenden Vorwiderstände verzichtet!
Pinbelegung | Ablauf | |||||||||||||||||||
![]() |
|
|
Wir benutzen shiftOut(dataPin, clockPin, bitOrder, value), um ein Byte an Daten an den 74HC595 zu übertragen und den clock-pin zu togglen. Zur Syntax: Bei der bitOrder muss festgelegt werden, wie die Binärzahl codiert ist: Bei uns ist üblicherweise das unwichtigste Bit (20 = 1) rechts, d.h. wir müssen MSBFIRST (most significant bit first) als bitOrder wählen.
/*
Schieberegister 74HC595
*/
const int clockPin = 12; // toggeln während der Datenübertragung
const int latchPin = 8; // vor dem Senden der Daten an den IC auf LOW ziehen, danach wieder HIGH
const int dataPin = 11; // an den seriellen Eingang des IC; jeweils 1 Byte übertragen
void setup() {
pinMode(clockPin, OUTPUT);
pinMode(latchPin, OUTPUT);
pinMode(dataPin, OUTPUT);
}
void loop() {
digitalWrite(latchPin, LOW);
// Datenübertragung: das Byte wird von rechts 2^0 nach links 2^7 codiert (least significant bit first)
shiftOut(dataPin, clockPin, MSBFIRST,255);
digitalWrite(latchPin, HIGH);
}
Tipp: Diesen Baustein kannst du sehr gut zur Porterweiterung benutzen!
Bemerkung:
Normalerweise
sendet man immer ein Bit an Information an das Schieberegister und
toggelt danach den clock-Pin. Die Methode shiftOut() macht im Prinzip
genau das 8 mal hintereinander und überträgt damit
insgesamt
1 Byte an Information.
Ein Timer (via Interrupt, siehe MsTimer2-Bibliothek) zählt die abgelaufene Zeit in Sekunden und wird durch Drücken des linken Schließers (= push button) gestartet und durch das Drücken des rechten Schließers gestoppt. Die Taster wurden mittels Bounce-Bibliothek entprellt (enprellen = debouncen).
Der etwas umfangreichere Sketch zum Download: kaffee_timer_v3.
Für den Anschluss des 74HC4511 (Datenblatt von Philips) benötigst du vier Ports des µC: Binär codiert lassen sich alle Zahlen von 0 bis 9 durch 4 bit darstellen. Der Baustein selbst benötigt somit vier Eingänge für das halbe Byte an Information ("nibble") und 7 Ausgänge für die LED's a bis g (Benennung siehe oben):
BCD
Eingang
|
Ausgang
|
Display
|
|||||||||
D
|
C
|
B
|
A
|
a
|
b
|
c
|
d
|
e
|
f
|
g
|
|
0
|
0
|
0
|
0
|
1
|
1
|
1
|
1
|
1
|
1
|
0
|
![]() |
0
|
0
|
0
|
1
|
0
|
1
|
1
|
0
|
0
|
0
|
0
|
![]() |
0
|
0
|
1
|
0
|
1
|
1
|
0
|
1
|
1
|
0
|
1
|
![]() |
0
|
0
|
1
|
1
|
1
|
1
|
1
|
1
|
0
|
0
|
1
|
![]() |
0
|
1
|
0
|
0
|
0
|
1
|
1
|
0
|
0
|
1
|
1
|
![]() |
0
|
1
|
0
|
1
|
1
|
0
|
1
|
1
|
0
|
1
|
1
|
![]() |
0
|
1
|
1
|
0
|
0
|
0
|
1
|
1
|
1
|
1
|
1
|
![]() |
0
|
1
|
1
|
1
|
1
|
1
|
1
|
0
|
0
|
0
|
0
|
![]() |
1
|
0
|
0
|
0
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
![]() |
1
|
0
|
0
|
1
|
1
|
1
|
1
|
0
|
0
|
1
|
1
|
![]() |
Pinbelegung | Bemerkungen | |||||||||||||||
![]() |
|
|
Danke
an Mona und Nadine
für den Aufbau der Schaltung (hier mit dem BS1 Project Board
von
Parallax):
Bemerkung: Du kannst einen Zählerbaustein, z.B. 4510, verwenden um weitere Ports des µC einzusparen.
Ohne Zweifel die eleganteste Methode einen Timer, einer Uhr oder ähnliches mittels 7-Segment-Anzeigen zu realisieren. Nachteil: der Chip kostet gut 10 € (z.B. bei Segor).
Vorarbeit: Willst du eine vierstellige Zahl ausgeben, so musst du zunächst die einzelnen Stellen (Einer, Zehner, ...) extrahieren. Dies leistet der folgende Sketch, der lediglich die Einer, Zehner, Hunderter und Tausender einer vierstelligen Zahl auf dem serial monitor ausgibt.
/* Extrahiert die einzelnen Stellen einer vierstelligen Zahl*/
int einer;
int zehner;
int hunderter;
int tausender;
void setup() {
Serial.begin(9600);
}
void loop() {
for (int i = 0; i < 10000; i++) {
Serial.println(i);
einer = i % 10;
zehner = (i / 10) % 10;
hunderter = (i / 100) % 10;
tausender = (i / 1000) % 10;
Serial.print("Einer: ");
Serial.print(einer);
Serial.print(" Zehner: ");
Serial.print(zehner);
Serial.print(" Hunderter: ");
Serial.print(hunderter);
Serial.print(" Tausender: ");
Serial.println(tausender);
delay(50);
}
}
Das Ergebnis sieht dann so aus:
Mit dieser Vorarbeit können wir nun die vier Stellen einer Zahl zwischen 0 und 9999 auf einem 4-fach-7-Segment-Display, z.B. dem CL5642BH, ausgeben. Um solch ein Display anzusteuern bräuchte man sehr viele Ports, nämlich 7 x 4 = 28 Stück, zzgl. 6 Ports für die Punkte.
Um Ports einzusparen steuern wir nun jede Stelle nacheinander an. Das geschieht so schnell, dass unser Auge dies nicht wahrnimmt.
Um z.B. die Zahl 83 auszugeben muss zuerst kurz DIG4 angesteuert werden, also 13 HIGH und 1, 2, 3, 4, 7 LOW (A, B, C, D und G) und 5, 6, 8 HIGH. Um die Zahl 8 an DIG3 auszugeben muss dann kurz 12 HIGH geschaltet werden und zeitgleich 1 - 7 LOW und 8 HIGH sein. Anschließend müssen jeweils DIG2 und DIG1 kurz angesteuert werden und zwar so, dass alle Ports 1 - 8 HIGH sind (die 7-Segment-Anzeigen haben eine gemeinsame Anode!).
Bei einer Vorwärtsspannung von 2,1 V und einer maximalen Stromstärke von 20 mA brauchst du eigentlich einen Vorwiderstand von 145 Ohm pro LED.
Lücke
Du kannst dir die Vorwiderstände sparen, indem du die LEDs via PWM ansteuerst, das erleichtert den Aufbau erheblich:
Lücke
--->Lücke<---
Bei verschiedenen Sensoren ist es so, dass sie beim Eintreten eines Ereignisses (z.B. ein Klatschen) ein LOW-Signal kurz auf HIGH ziehen. Ist der µC gerade mit etwas anderem beschäftigt kann man das Signal zwischenspeichern indem man z.B. ein R-S-Flipflop verwendet (alternativ könnten man Interrupts verwenden). Hier benutzen wir das CD4043:
Wir nutzen dabei nur ein Flipflop (der Baustein enthält 4 Stück), dessen Ausgang Q wir an Port 2 einlesen. Die Eingänge des Flipflops S für set und R für reset sind über einen Pull-Down-Widerstand auf low gezogen. Bei Drücken des jeweiligen Buttons werden R bzw. S high.
Beachte:
Bei der Nutzung von CMOS-Bausteinen musst du alle nicht benutzten
Eingänge auf GND
legen!
// Dank an Pascal und Julien!
const int Q = 2; // Ausgang Q des RS-Flipflops an den digitalen Port 2
const int ledPin = 13; // Port, an den die LED angeschlossen ist
void setup() {
pinMode(ledPin, OUTPUT); // lED-Port ist Ausgang
pinMode(Q, INPUT); //Port 2 ist Eingang
Serial.begin(9600); // Serielle Schnittstelle bereitstellen
}
void loop(){
if (digitalRead(Q) == HIGH) { // prüft, ob Q high ist
Serial.println("Ich bin high"); // Ausgabe erfolgt am Serial Monitor
digitalWrite(ledPin, HIGH); }
else {
// ansonsten wird das ledPin ausgeschaltet:
digitalWrite(ledPin, LOW); }
}
In bestimmten Situationen reichen die vorhandenen Ports des µC nicht aus. Oben hast du die Möglichkeit kennen gelernt, mit einem Schieberegister Abhilfe zu schaffen. Hier verwenden wir den I2C-Bus, um den Port-Erweiterungs-IC PCF8574 (Datenblatt von ) zu nutzen:
--->Lücke<---
Anstelle dieses IC kannst du auch ein Mux-Shield (Multiplexer-Shield mit 48 I/O Ports) verwenden.
Zwar besitzen Arduinos die Möglichkeit Zeiten zu messen, z.B. mittels millis(). Es wird jedoch die Zeit gemessen, seit der µC eingeschaltet wurde. Ein RTC-Baustein hat den Vorteil, dass er sich auch bei ausgeschaltetem Board die Uhrzeit merken kann, da er eine separate Batterie (meist die Knopfzelle CR2032) besitzt.
Wir verwenden den Uhrenbaustein DS1307 von Maxim (pinkompatibel zum PCF8583, jedoch zusätzlich mit EEPROM und Alarmfunktion) auf einem Board von Sparkfun, um Uhrzeiten abfragen zu können. Vorweg kurz die Anschlüsse:
5 V | werden zur Abfrage der Uhrzeit benötigt. Wenn hier keine 5 V anlegen verwendet der Baustein die Batterie im "Schlafzustand". |
GND | muss zwingend auf 0 V gesetzt werden. |
SDA | Daten-Pin des I2C-Bus ist bei den meisten Arduinos mit dem analogen Pin 4, also A4 zu verbinden. |
SCL | Clock-Pin des I2C-Bus ist (meist) mit A5 zu verbinden. |
SQW | benötigen wir nicht. Der Pin kann verwendet werden um ein Rechtecksignal auszugeben. |
Der Chip liefert immer 7 Byte an Information über den I2C-Bus, nämlich Datum und Uhrzeit - allerdings BCD-codiert. BCD bedeutet dabei binär codierte Dezimalzahl (Binary Coded Decimal). Was ist damit gemeint? Ganz einfach: Jede einzelne Ziffer wird binär codiert, damit wäre z.B. 27 = 27 = 0010.0111, also immer schön in Binärhäppchen :-). Beachte: 27 = 11011b wäre die Dezimalzahl, wenn man sie komplett binär codieren würde!
Wir müssen, nachdem wir die Uhr einmal gestellt haben, die empfangenen Informationen noch passend decodieren. Glücklicherweise gibt es dazu Bibliotheken, z.B. von adafruit. Folgende Schritte sind notwendig:
// Wir verwenden die Uhr DS1307, angeschlossen über den I2C-Bus
// siehe http://learn.adafruit.com/ds1307-real-time-clock-breakout-board-kit/overview
// Der Code von adafruit wurde dazu modifiziert
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 RTC;
void setup () {
Serial.begin(57600);
Wire.begin();
RTC.begin();
if (! RTC.isrunning()) {
Serial.println("RTC is NOT running!");
// following line sets the RTC to the date & time this sketch was compiled
RTC.adjust(DateTime(__DATE__, __TIME__));
}
}
void loop () {
// Uhrzeit auslesen und in "now" speichern:
DateTime now = RTC.now();
// Datum und Uhrzeit auf dem serial monitor ausgeben:
Serial.print(now.day(), DEC);
Serial.print('.'); Serial.print(now.month(), DEC);
Serial.print('.');
Serial.print(now.year(), DEC);
Serial.print(' ');
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
// Wecker 2 h in die Zukunft stellen:
DateTime future (now.unixtime() + 2 * 3600L);
// Bemerkung: siehe http://arduino.cc/en/Reference/IntegerConstants für das "L"
// Weckzeit ausgeben:
Serial.print(future.hour(), DEC);
Serial.print(':');
Serial.println(future.minute(), DEC);
delay(3000);
}
Und so sieht das ganze dann aus:
Dank der LCD-Bibliothek ist es sehr einfach ein LCD mit dem Arduino zu verwenden: Die blauen Leitungen entsprechen den Datenleitungen (4 im 4bit-Modus, sonst 8), rot/schwarz auf der linken Seite sind die Versorgungsspannung des Chips und rot/schwarz auf der rechten Seite dienen der LED-Hintergrundbeleuchtung. Das orangene Kabel dient zur Einstellung des Kontrastes, wobei das Potenzial am mittleren Pin eines 10 kΩ-Potis abgegriffen wird. Die beiden grünen Kabel (register select und enable) dienen der Kommunikation zwischen Arduino und LCD, das mittlere schwarze legt fest, dass wir das LCD nur zur Ausgabe ("Schreiben") benutzen wollen.
Tipp: Die Kontrasteinstellung soll eigentlich mit einem Potenzial zwischen 0 und 1,5 V erfolgen. Wenn man lediglich einen Poti verwendet, spielt sich die Kontrastveränderung in einem sehr kleinen Drehbereich ab. Zur Abhilfe kann ein großer Widerstand (z.B. 20 kΩ) zum Poti in Reihe geschaltet werden. Damit ist die Potenzialdifferenz am Poti selbst deutlich geringer.
Du kannst den Verkabelungsaufwand deutlich verringern, indem du ein serielles LCD verwendest: Verbinde den TX-Pin (t für transmit) des Arduinos mit dem RX-Pin (r für receive) des Displays. Beachte, dass manche Displays einen Jumper besitzen, den du ggf. noch setzen musst.
Für detailliertere Infos siehe http://playground.arduino.cc/Learning/SerialLCD, hier einfach ein Beispielcode, um Text darzustellen.
// Copyleft 2006 by djmatic (verkürzt)
void setup(){
Serial.begin(19200);
}
void loop(){
backlightOn(0); // Hintergrundbeleuchtung dauerhaft aktivieren
clearLCD();
Serial.write("Herzlich"); cursorSet(0, 1);
Serial.write("Willkommen!");
delay(1000);
}
// clear the LCD
void clearLCD(){
Serial.write(12);
}
// Cursor setzen
void cursorSet(int xpos, int ypos){
Serial.write(254);
Serial.write(71); Serial.write(xpos); // Spalte
Serial.write(ypos); // Zeile
}
// Kontrast einstellen
void setContrast(int contrast){
Serial.write(254);
Serial.write(80);
Serial.write(contrast);
}
// Hintergrundbeleuchtung aktivieren
void backlightOn(int minutes){
Serial.write(254);
Serial.write(66);
Serial.write(minutes); // 0 für dauerhaft EIN
}
// Hintergrundbeleuchtung ausschalten
void backlightOff(){
Serial.write(254);
Serial.write(70);
}
Der Sensor LM35DZ liefert einen Messwert, den man sehr einfach in die Temperatur in °Celsius umrechnen kann.
Achtung: Im Datenblatt ist das Bauteil von unten gesehen (BOTTOM VIEW) abgebildet!
float tempMesswert;
float tempCelsius;
void setup()
{
Serial.begin(9600); //opens serial port, sets data rate to 9600 bps
}
void loop()
{
tempMesswert = analogRead(0);
tempCelsius = (5.0 * tempMesswert * 100.0)/1024.0; //Umrechnung in ° Celsius
Serial.print((byte)tempCelsius);
delay(1000);
}
Tipp zum Weiterschmökern: Messwerte mit Processing darstellen.
Der Reflexoptokoppler CNY70 ist mit einer Infrarot-Sendediode (Wellenlänge 950 nm, d.h. dein Auge sieht das Licht nicht, jedoch viele Handykameras) und einem Fototransistor als Empfänger ausgestattet. Dieses Bauteil kann man für viele verschiedene Dinge verwenden, z.B. als berührungsloser Sensor, um zu detektieren, ob eine Tür offen ist: Die Infrarotdiode sendet ein Licht aus und der Fototransistor detektiert, ob dieses reflektiert wurde. Man kann auch zwei von diesen Teilen verwenden, um einen Roboter eine Linie nachfahren zu lassen: siehe Liniensensor.
Nutze für die Infrarot LED einen Widerstand von 220 Ω, für den Fototransistor einen 10 kΩ Widerstand (=pullup).
Hinweis:
1) Du kannst den CNY70 clever diagonal einbauen, siehe Foto (fehlt noch).
2) Man kann den CNY70 zweckentfremden und zwei Stück davon als Lichtschranke für kurze Distanzen (10 cm sind möglich) verwenden.
Achtung: Je nach Hersteller sind die Anschlüsse verschieden: Vishay CNY70 bzw. Temic CNY70.
Diese Lichtschranke besteht aus einer Infrarot-Sendediode (hellblau; Wellenlänge: 950 nm) und einem Fototransistor als Empfänger (schwarz). Der große Vorteil dieses Pärchens ist der Preis: bei Pollin kosten 10 Paare weniger als 1 €! Die Inbetriebnahme der Temic K153P ist einfach:
Die Infrarotdiode sendet ein
Licht, das für unser Auge nicht
sichtbar ist, aus. Solange kein Licht auf die Basis des Fototransistors
trifft, sperrt dieser, d.h. am Port des µC liegt HIGH an.
Sobald jedoch Licht auf die Basis des Fototransistors trifft
öffnet sich die C-E-Strecke und am Port liegt LOW an.
Nachteile: Der Fototranistor ist empfindlich für Streulicht. Somit sind nur sehr begrenzte Reichweiten möglich.
Experimentiere mit verschiedenen Vorwiderständen (Poti verwenden) für den Fototransistor, um zu testen, welche Reichweite möglich ist.
Die Infrarotdiode sendet ein Licht, das für unser Auge nicht sichtbar ist, aus. Mit dem Fotoapparat kannst du diese Licht jedoch sichtbar machen, siehe Abbildung oben. Solange kein Licht auf die Basis des Fototransistors trifft, sperrt dieser, d.h. am Port des µC liegt LOW an. Sobald jedoch Licht auf die Basis des Fototransistors trifft öffnet sich die C-E-Strecke und am Port liegt HIGH an.
Achtung: Im Datenblatt des TCST2000 wird ein Pulldown-Widerstand von 100 Ohm am Transistor empfohlen. In der Praxis musste ich jedoch zwischen 500 Ohm und 10 kOhm verwenden.
int lichtSchranke = 2;
void setup() {
Serial.begin(9600);
pinMode(lichtSchranke, INPUT);
}
void loop() {
Serial.println(digitalRead(lichtSchranke));
delay(1);
}
Der Sharp GP2Y0A02YK0F ist z.B. bei Segor günstig erhältlich und deckt eine Reichweite von 20 cm - 150 cm ab. Alternativ gibt es noch einen deutlich teureren Sensor (Sharp GP2 Y0A710 K0F), der bis zu 550 cm Entfernungen misst.
Tipp: In jedem Fall die passenden JST-3-Pin-Stecker bzw. Verbindungskabel mitbestellen.
Der große Vorteil gegenüber den oben verwendeten Lichtschranken ist, dass bei diesem Modell das gesendete Infrarotlicht moduliert wird, d.h. es ist nicht störanfällig für Streustrahlung. Verwendet werden solche Sensoren z.B. bei Urinalen, in der Robotik, ...
Der Ausgang des Sensors liefert ein Potenzial zwischen 0 V und 5 V und wird an einen analogen Port angeschlossen. Am analogen Port haben wir eine 10 bit Auflösung, d.h. (210=1024 verschiedene) Messwerte von 0 bis 1023 sind möglich. Nun muss man den Messwert noch in die Entfernung zurückrechnen. Dazu muss kann man die Kurve im Datenblatt durch eine Hyperbel annähern (noch besser wäre evtl. eine Exponentialfunktion). Bei mir klappt es ganz gut mit folgender Formel:
const int irPin = 1; // analoger Eingang für den Abstandssensor
float distance = 0; // Objektentfernung
void setup() {
Serial.begin(9600); // serielle Schnittstelle bereitstellen
}
void loop() {
distance = 10650 * pow(analogRead(irPin),-0.935)-15; // Achtung: Sensor eichen!
Serial.println(distance);
delay(500);
}
Wichtig: die Zahlen 10650 und -15 sollten dem Sensor angepasst werden, d.h. man stellt ein Hinderniss in 50 cm und 100 cm Entfernung und ändert die Zahl so lange, bis man eine bestmögliche Übereinstimmung hat. Klar sollte sein, dass der Sensor nur in den vorgesehenen Distanzen ein gutes Messergebnis liefert. Möchte man dennoch kleinere Entfernungen damit messen, so müsste man die Abstandsformel ändern oder besser einen für diesen Messbereich ausgelegten Sensor verwenden.
Bemerkung: Abgebildet ist ein halbes "screw-shield". Diese Dinger sind sehr praktisch, da man Sensorkabel anschrauben und außerdem weitere Shields aufstecken kann.
Eigentlich gibt es
günstigere Ultraschall-Entfernungsmesser als den
PING)))
von Parallax
(Datenblatt).
Da wir dieses Bauteil jedoch ohnehin vorrätig haben
hier ein
Beispiel:
Die Entfernung zu einem Gegenstand (Segel auf Modellauto)
wird gemessen. Wenn diese kleiner als 50 cm wird, soll eine
LED
leuchten und ein Elektromagnet
eine Kugel fallen lassen. Der Elektromagnet wird via Transistor (BD437)
angeschlossen, da er eine hohe Stromstärke benötigt.
Warnung: Wird der PING-Sensor versehentlich mit 9 V verbunden geht er kaputt. Zu erkennen ist dies am verschmorten Aufkleber auf dem IC (Foto folgt).
// Ursprünglich Idee von David A. Mellis, Tom Igoe
// siehe http://www.arduino.cc/en/Tutorial/Ping
#define magnetPin 2
#define pingPin 7
#define ledPin 13
long duration, cm;
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
pinMode(magnetPin, OUTPUT); }
void loop(){ // der Port pingPin wird als Ausgang geschaltet und ein 5 ms HIGH-Impuls ausgegeben.
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
// nun wird der pingPin Port als Eingang geschaltet und gemessen, wie lange es dauert, bis das Eche eintrifft.
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);
// Zeit in Entfernung umrechnen:
cm = duration / 29 / 2; // Schallgeschwindigkeit 29 cm/s; halbe Strecke
Serial.print(cm);
Serial.println("cm");
if(cm < 50){
digitalWrite(ledPin, HIGH);
digitalWrite(magnetPin, LOW);
}
else{
digitalWrite(ledPin, LOW);
digitalWrite(magnetPin , HIGH);
}
delay(100);
}
Deutlich günstiger als der PING-Sensor von Parallax ist der HC-SR04 von Cytron Technologies. Er benötigt ein 10 µs langes Triggersignal, woraufhin er acht 40 kHz Impulse aussendet und misst, wie lange es bis zum Eintreffen des Echos benötigt (150 µS bis 25.000 µs). Bei fehlendem Echo werden 38.000 µs zurückgemeldet. Diese Zeit wird über den Echo-Pin ausgegeben und zwar in Form eines Impulses, dessen Weite gerade der Signallaufzeit (in µs und für hin & zurück) entspricht. Die Entfernung (in cm) berechnet sich aus der Signallaufzeit in µs durch:
Es gibt zwar die Ultrasonic-Bibliothek, aber der Code ist auch schnell selbst geschrieben:
# define echoPin 6
# define trigPin 7
long distance;
void setup(){
pinMode(echoPin,INPUT);
pinMode(trigPin,OUTPUT);
Serial.begin(9600);
}
void loop(){
digitalWrite(trigPin,HIGH);
delayMicroseconds(1000);
digitalWrite(trigPin,LOW);
distance = pulseIn(echoPin,HIGH)*0.017;
Serial.print(distance);
Serial.println(" cm");
}
Einen Getriebemotor könntest du, wie einen Lautsprecher, über eine einfache Transistorschaltung ansteuern (du bräuchtest aber eine zusätzliche Diode, damit die Induktionsspannung beim Abschalten den Transistor nicht zerstört). Oft möchte man jedoch die Drehrichtung des Motors ändern und dann ist es ganz praktisch, wenn man einen speziellen Motortreiber-IC verwendet. Hier stelle ich euch den L293D vor, mit dem man zwei Motoren ansteuern kann. Man benötigt dann auch keine Schutzdiode mehr, denn diese ist bereits integriert.
![]() |
|
# define motorPin1 2
# define motorPin2 7
void setup() {
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
}
void loop() {
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, LOW);
delay(1000);
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, HIGH);
delay(1000);
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
delay(4000);
}
Achtung: Bei stärkeren Motoren ist eine externe Stromversorgung zu verwenden (wichtig: GND jeweils verbinden). Beträgt der Strom pro Motor 0,6 A oder mehr muss ein größerer Motortreiber verwendet werden, z.B. L298 (max. 2 A je Motor). Auf mikrocontroller.net gibt es eine schöne Übersicht von H-Brücken.
const int motorPin1 = 2;
const int motorPin2 = 7;
const int enablePin = 9;
int speed = 127; // Geschwindigkeit 50%, max. 255
void setup() {
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
}
void loop() {
analogWrite(enablePin, speed);
digitalWrite(motorPin1, HIGH); digitalWrite(motorPin2, LOW);
delay(1000);
digitalWrite(motorPin1, LOW); digitalWrite(motorPin2, HIGH);
delay(1000);
analogWrite(enablePin, 0); // Motor aus
delay(4000);
}
Servomotoren werden z.B. bei RC-Cars für die Lenkung eingesetzt. Sie bestehen aus der Steuerelektronik und einem Gleichstrommotor in einem Gehäuse. Über einen Poti misst die Elektronik den Drehwinkel des Motors und regelt ggf. nach. So schaut der Winzling geöffnet aus:
xxx Abb. geöffneter Servo xxx
Es gibt Standardservos (Drehbereich 0° - 180°) und kontinuierliche Servos (z.B. von Parallax). Beide haben drei Anschlüsse:
An die Signalleitung gibt man nun zwischen einer Millisekunden und 2 ms HIGH aus, gefolgt von ca. 20 ms LOW. Gibt man für 1 ms HIGH und anschließend LOW aus, so dreht sich der Motor auf 0° (gegen den Uhrzeigersinn). Gibt man 2 ms HIGH und anschließend LOW aus, so dreht sich der Motor in die 180°-Position:
const int servoPin = 10;Bemerkung: Eigentlich müsste das HIGH-Signal zwischen 1000 µs und 2000 µs lang sein. In meinen Tests ergab sich jedoch, dass das Signal zwischen 500 µs und 2.500 µs lang sein muss, damit die volle Winkelweite von 180° überstrichen wird. Die Ursache für dieses Verhalten ist mir unklar.
void setup() {
pinMode(servoPin, OUTPUT);
}
void loop() {
for(int winkel=0; winkel<180; winkel++){
servoAngle(winkel);
}
for(int winkel=180; winkel>0; winkel--){
servoAngle(winkel);
}
}
void servoAngle(int angle){
digitalWrite(servoPin, HIGH);
angle = map(angle,0,180,500,2500); // Winkel in Zeit umrechnen, siehe Bemerkung!
delayMicroseconds(angle);
digitalWrite(servoPin, LOW);
delayMicroseconds(15000);
}
Einfacher wird es, wenn du die vorhandene Servo-Bibliothek nutzt. Du musst für jeden Servo lediglich noch angeben, an welchem Port er angeschlossen ist.
#include <Servo.h>
Servo meinServo;
void setup(){
meinServo.attach(10); // Servo an Pin 10
}
void loop() {
for(int i = 0; i<180; i++){
meinServo.write(i);
delay(19);
}
for(int i = 180; i>0; i--){
meinServo.write(i);
delay(19);
}
}
Es gibt unterschiedliche Typen
von Schrittmotoren, die z.B. in
Festplatten, Druckern, Scannern ... eingesetzt werden. Wir betrachten
hier lediglich den bipolaren Typ mit vier Anschlüssen.
Zahlreiche Infos
über weitere Typen findest du im Roboternetz
unter Schrittmotoren.
Zunächst musst du mit
einem Multimeter herausfinden, welche beiden
Anschlüsse jeweils zu einer Spule gehören (der
Widerstand ist unendlich, wenn du die falschen Kabel erwischst).
Im nächsten Schritt benutzt du den bereits oben verwendeten Motortreiberbaustein L293D um die Spulen des Schrittmotors anzusteuern. Der Baustein ist nötig, da die Stromstärke durch die Ports alleine zu gering wäre.
Schließe den Schrittmotor wie beschrieben und abgebildet an:
![]() |
|
Wir benutzen ein Beispiel von
Tom Igoe aus den offiziellen Arduino-Beispielen,
um den Motor eine Drehung durchführen zu lassen. Dazu musst du
noch
herausfinden, wie viele Schritte einer Umdrehung entspricht. Oft sind
es wie hier 200.
#include <Stepper.h>
const int stepsPerRevolution = 200; // Anzahl Schritte pro Umdrehung
// Motortreiber an die Pins 8-11 anschließen:
Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);
void setup() {
myStepper.setSpeed(60); // 60 U/Min.
Serial.begin(9600);
}
void loop() {
Serial.println("clockwise"); // Drehrichtung: Uhrzeigersinn
myStepper.step(stepsPerRevolution);
delay(500);
Serial.println("counterclockwise"); // Drehrichtung: gegen den Uhrzeigersinn
myStepper.step(-stepsPerRevolution);
delay(500);
}
Wenn man das Schließen eines Schalters detektieren möchte steht man vor dem Problem, dass der Schließvorgang nicht ideal (rot gezeichnet) verläuft. Der Übergang von Masse auf 5 V (und umgekehrt) sieht im Gegenteil so aus, dass der Taster prellt (schwarz dargestellt):
Es gibt prinzipiell zwei Möglichkeiten, den gewünschten Übergang sauber zu detektieren:
Per Software sorgt man dafür, dass der Übergang nur dann detektiert wird, wenn mehrere Millisekunden durchgehend das neue Potenzial anliegt. Du kannst selbst das Debounce-Beispiel nachbasteln oder gleich die passende Bounce-Bibliothek verwenden:
--->Lücke<---
Ein umfangreicheres Beispiel unter Verwendung dieser Bibliothek findest du beim Bau des Kaffee-Timers (Schieberegister mit Push-Buttons).
Möchte man einen Taster, der einen Interrupt auslöst, entprellen, ist es ratsam per Hardware zu debouncen. Dies wird mit einem sogenannten RC-Glied (weil es aus einem Widerstand und einem Kondensator besteht) erledigt:
Hier
steckt etwas Physik dahinter: Wenn der Schalter länger
geöffnet war,
wurde der Kondensator C zunächst über die
Widerstände R1
und R2
geladen und
beim Eingang des
Schmitt-Triggers
(z.B. NAND-Gatter
CD4093) liegt damit "high" an, da
die obere Platte des
Kondensators auf 5 V liegt. Da der invertierende Schmitt-Trigger
das
Signal umdreht detektiert der µC "low".
Schließt man nun
den Schalter, entlädt sich nun der Kondensator - wie schnell
das geht, hängt neben dem Kondensator selbst vom Widerstand R2
ab. Man muss diese nun so wählen, dass der Lade- bzw.
Entladevorgang deutlich länger als der Prellvorgang (ca. 10
ms)
dauert.
Wähle z.B. R1
= 10 kΩ, R2
= 22 kΩ und C = 1 µF.
Bemerkungen:
Link-Tipp: Auf mikrocontroller.net/articles/Entprellung ist das obige Beispiel detaillierter erklärt. Außerdem kannst du nachlesen, wie man mittels RS-Flipflop entprellen kann.
hat einen Eingang und einen Ausgang. Du kannst ihn dir als elektrischen Schalter vorstellen: Bei Überschreiten einer bestimmten Schwelle Uhigh wird sein Ausgang so lange auf high gezogen, bis eine anderen Schwelle Ulow unterschritten wird. Erst dann wird der Ausgang so lange auf low gezogen, bis wieder die obere Schwelle Uhigh überschritten wird. Der Schmitt-Trigger hat bewusst diese Hysterese. In der Abbildung ist das Schaltverhalten des Schmitt-Triggers grün dargestellt.
Bei einem Interrupt wird durch
ein Ereignis (z.B. Timer, Signal von außen, ...) das
Hauptprogramm loop() unterbrochen
und ein vorab festgelegtes Unterprogramm
ausgeführt. Nach Abarbeitung des Unterprogrammes wird das
Hauptprogramm an der ursprünglichen Stelle weiter
ausgeführt.
Unser Arduino-Board kann
zwei Interrupts erfassen: Interrupt0 am digitalen Port2 und Interrupt1
an
Port D3. Man muss dem µC allerdings zunächst
mitteilen, dass er auf Interrupts reagieren soll und welches
Unterprogramm er beim Auftreten eines Interrupts ausführen
soll.
Dies erledigt man im setup()
mit der Methode attachInterrupt(interrupt,
function, mode).
int klatschSensor = 2; // Klatschschalter, z.B. von Parallax, an Port D2 anschließen
int ledPin = 13;
volatile int zustand = LOW; /* Bemerkung: Das keyword "volatile" ist eine Anweisung für den Compiler:
Wird eine Variable so deklariert, wird sie aus dem RAM und nicht aus Registern zu laden. */
void setup()
{
pinMode(klatschSensor, INPUT);
pinMode(ledPin, OUTPUT);
/*
Hier wird der Interrupt aktiviert: Ändert sich das Signal am Pin2 (="CHANGE"),
so wird das Hauptprogramm "loop()" unterbrochen und zustandAendern ausgeführt
*/
attachInterrupt(0, zustandAendern, CHANGE);
}
void loop()
{
digitalWrite(ledPin, zustand);
}
void zustandAendern()
{
zustand = !zustand; // logische Negation
}
Neben "CHANGE" sind folgende Modi möglich:
LOW | Wenn D2 low ist wird ausgelöst. |
CHANGE | Ändert sich der Zustand von D2 wird ausgelöst. |
RISING | Bei steigender Flanke, d.h. einem Übergang von HIGH zu LOW von D2 wird ausgelöst |
FALLING | Bei fallender Flanke an D2 wird ausgelöst. |
Mit detachInterrupt(0) kann Interrupt0 deaktiviert werden. Soll ein bestimmter (evtl. zeitsensibler) Code innerhalb von loop() nicht unterbrochen werden, so kann man die Annahme von Interrupts mit noInterrupts() vorübergehend verweigern und anschließend mit interrupts() wieder zulassen.
kann prima verwendet werden, um Daten mit anderen Geräten auszutauschen, oder um Debug-Infos an den "Serial Monitor" zu liefern. Das Uno-Board hat lediglich eine serielle Schnittstelle; weitere können jedoch per Software emuliert werden. Das Mega-Board hat vier serielle Schnittstellen.
Die Daten werden binär kodiert byteweise (= 8 bitweise) versendet. Folgende Grafik stammt von Gerald Deppe und erklärt das ganz schön, siehe auch UART bei Wikipedia.
Der Austausch von Daten zwischen zwei Geräten kann gleichzeitig erfolgen, denn es gibt jeweils zwei Datenleitungen: TX ("transmit") und RX ("receive"). Möchte man zwei Unos seriell verbinden, muss man die Verkabelung kreuzen. Wir benutzen die serielle Schnittestelle z.B. zur drahtlosen Übertragung von Information mittels XBee.
Die Dezimalzahl 76 kann unterschiedlich kodiert ausgegeben werden, siehe Serial Monitor:
Unter Shields versteht Platinen, die so gebaut wurden, dass sie gerade auf das Arduino-Board aufgesteckt werden können. Mittlerweile gibt es eine riesige Anzahl an Shields, eine Übersicht gibt es unter shieldlist.org/.
Wenn man ein LCD und einige Buttons benötigt ist das LCD-keypad-shield von DFRobot hervorragend geeignet. Leider ist mein Nachbau-Exemplar nicht empfehlenswert, da die DOWN-Taste manchmal streikt und zudem die digitalen Ports D2 und D3 nicht korrekt nach außen geführt sind, was mir eine Stunde Fehlersuche bescherte :-(.
In diesem Beispiel habe ich eine Uhr (ohne weitere Funktionalität) aufgebaut. Sie kann z.B. problemlos zu einem Wecker erweitert werden (Achtung: Weckzeit im EEPROM speichern lassen, sonst ist diese bei Stromausfall weg!). [Bemerkung: Foto ohne RTC]
In meinen Augen ist dieses Beispiel dennoch sehr interessant, denn du kannst etwas über das Debouncen (= Entprellen von Tastern) lernen. Hintergrund: Bei diesem Shield werden die fünf Taster (select, left, right, up, down) über einen (sic!) analogen Port eingelesen. Man kann also die üblichen Debounce-Bibliotheken und -Beispiele nicht verwenden.
#include <LiquidCrystal.h>
#include <DateTime.h>
#include <DateTimeStrings.h>
#include <Wire.h>
#include "RTClib.h"
// Zum Debuggen über den Serial Monitor hier eine "1" eintragen, sonst "0"
#define debug 1
RTC_DS1307 RTC;
// Belegung Keypad-Shield:
// D4 4, D5 5, D6 6, D7 7, RS 8, Enable 9
LiquidCrystal lcd(8,9,4,5,6,7);
// Tastenbelegung:
// 1: select, 2: left, 3: up, 4: down, 5: right, 6: reset
int buttonValue = 0;
int selected = 0;
int lastButtonState = 0;
long lastDebounceTime = 0; long debounceDelay = 110;
void setup(){
lcd.begin(16, 2);
Serial.begin(9600);
Wire.begin();
RTC.begin();
delay(500);
lcd.setCursor(0, 0); // 0. Spalte, 1. Zeile
lcd.print("RTC mit dem ");
lcd.setCursor(0,1);
lcd.print("Keypad-Shield");
delay(500); lcd.clear(); // Auskommentieren, falls die Uhr neu gestellt werden muss:
//RTC.adjust(DateTime(__DATE__, __TIME__));
}
void loop() {
uhrZeitAnzeigen();
selected = buttonAbfrage();
if(debug){
if(selected != 0) Serial.println(selected);
}
selectedAnzeigen();
}
int buttonAbfrage(){
buttonValue = analogRead(0);
if(900<buttonValue) selected = 0;
if(600<buttonValue && buttonValue <901) selected = 1;
if(400<buttonValue && buttonValue <601) selected = 2;
if(80<buttonValue && buttonValue <201) selected = 3;
if(250<buttonValue && buttonValue <350) selected = 4;
if(buttonValue==0) selected = 5;
// Logik für's Debouncen (=Entprellen):
if(selected != lastButtonState) {
lastDebounceTime = millis();
lastButtonState = selected;
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if(selected == lastButtonState) {
lastDebounceTime = millis();
return selected;
}
}
return 0; // Default-Wert zurückgeben
}
void uhrZeitAnzeigen(){
DateTime now = RTC.now();
lcd.setCursor(0, 0);
if(now.hour()<10) lcd.print("0"); // ggf. "0" ergänzen
lcd.print(now.hour(), DEC);
lcd.print(":");
if(now.minute()<10) lcd.print("0");
lcd.print(now.minute(), DEC);
lcd.setCursor(0,1);
lcd.print("Uhrzeit");
}
void selectedAnzeigen(){
lcd.setCursor(15,1);
if(selected != 0) lcd.print(selected);
}
In diesem Beispiel wird die Temperatur mittels LM35DZ am analogen Port 0 gemessen und der Messwert auf einer Webseite ausgegeben, d.h. auf dem Arduino läuft ein WebServer.
/*
* Web Server im Original von David A. Mellis, Tom Igoe
* Belegte Pins:
* Ethernet shield attached to pins 10, 11, 12, 13
* LM35DZ am analogen Port 0
*/
#include <SPI.h>
#include <Ethernet.h>
float tempCelsius;
// MAC-Adresse von der Unterseite des Ethernetshield abschreiben:
byte mac[] = {0x90, 0xA2, 0xDA, 0x0D, 0x44, 0x6F};
//IP-Adresse passend zum Heimnetz
IPAddress ip(192,168,178, 21);
EthernetServer server(80); // Port 80
void setup() {
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());
}
void loop() {
//Temperaturmesswert einlesen und umrechnen:
tempCelsius = (5.0 * analogRead(0) * 100.0)/1024.0; Serial.print((byte)tempCelsius); // listen for incoming clients
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
// 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) {
// 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();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
// add a meta refresh tag, so the browser pulls again every 5 seconds:
client.println("<meta http-equiv=\"refresh\" content=\"5\">");
client.print("Die Temperatur betraegt ");
client.print(tempCelsius);
client.print(" Grad Celsius <br />");
client.println("</html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disonnected");
}
}
Etwas aufwändiger als die Verwendung des µC als Twitter-client ist es, selbst Nachrichten zu versenden (twittern). Z.B. wenn ein Einbrecher das Haus betritt oder die Temperatur im Serverschrank zu hoch ist oder ...
#include <SPI.h>
#include <Ethernet.h>
#include <Twitter.h>
// MAC-Adresse von der Unterseite des Ethernetshield abschreiben:
byte mac[] = {0x90, 0xA2, 0xDA, 0x0D, 0x44, 0x6F};
// wenn man eine IP-Adresse angeben möchte (anstelle DHCP)
// byte ip[] = { 192, 168, 178, 21 };
//Security token erzeugen und eintragen: http://arduino-tweet.appspot.com/oauth/twitter/login
Twitter twitter("Twitter-SECURITY-TOKEN hier eintragen");
// Twitter-Nachricht eintragen:
char msg[] = "Testo rockt!";
void setup()
{
delay(2000);
//Ethernet.begin(mac, ip); falls die IP-Adresse genutzt wird
Ethernet.begin(mac);
Serial.begin(9600);
Serial.println("connecting ...");
if (twitter.post(msg)) {
// Statusabfrage:
int status = twitter.wait(&Serial);
if (status == 200) {
Serial.println("OK.");
}
else {
Serial.print("failed : code ");
Serial.println(status);
}
}
else {
Serial.println("connection failed.");
}
}
void loop()
{
}
Die getwitterte Nachricht muss sich (von Zeit zu Zeit?) ändern, sonst wird die Annahme verweigert (Code 403).
Man kann die Netzwerkeinstellungen für den Arduino entweder händisch eintragen oder via DHCP vom Router bekommen. Ich bevorzuge letztere Variante. Bei vielen Routern kann man einstellen, dass bestimmten Geräten immer die gleiche IP-Adresse zugewiesen wird. In der Geräteübersicht kann man dann den Arduino einfacher identifizieren. Hier am Beispiel einer FritzBox:
Mit dem WiFi-Shield kannst du dich mit einem (verschlüsselten) WLAN verbinden. Das Shield kannst du mit dem Arduino Uno oder Mega verwenden. Es enthält zudem einen Micro-SD-Karten-Slot.
Achtung1: Der Arduino kommuinziert via SPI (serielle periphere Schnittstelle) mit dem WiFi-Shield und mit der SD-Karte. Deshalb sind beim Uno die Pins 11, 12 und 13 nicht zu benutzen (beim Mega 50, 51 und 52). Auf beiden Boards Uno und Mega sind die Ports 10 für WiFi und 4 für die SD-Karte reserviert. Beim Mega darf zusätzlich 53 nicht verwendet werden, bzw. muss als OUTPUT geschaltet sein.
Achtung2: Da sowohl WiFi als auch die SD-Karte via SPI-Bus angesteuert werden, können nicht beide zeitgleich angesprochen werden.
Benutze das Beispiel ConnectWithWPA aus der Arduino-IDE , um das Shield zu testen. Du musst lediglich die SSID deines WLANs und dein WPA2-Passwort eingeben. Du kannst z.B. dein Smartphone verwenden, um ein WLAN zur Verfügung zu stellen (tethering). Die Ausgabe auf dem Serial Monitor sollte so aussehen::
Bemerkung:
Falls
dein WiFi-Shield ein Firmware-Upgrade benötigt kannst du
dieses wie bei dfrobot beschrieben
über den Mini-USB-Anschluss einspielen. Es klappte bei mir
nicht, das Update wie auf arduino.cc
beschrieben einzuspielen (es fehlte die msvcr100.dll).
Benutze das Beispiel WiFiWebServer aus der Arduino IDE, um in deinem WLAN einen WebServer zu implementieren. Die IP-Nummer, die zum Aufruf im Browser benötigst, erfährst du nach dem Upload des Sketches vom Serial Monitor:
Das XBee-Shield kann man auch für WLAN, Bluetooth und RFID verwenden. In diesem Abschnitt wollen wir unseren Arduino ins WPA-gesicherte WLAN einbinden. Wir verwenden das RN-XV Board Roving RN-171 von Sparkfun, das ich über Elmicro bezogen habe.
Die Teile sind zwar schnell zusammengestöpselt ...
... müssen dann aber noch konfiguriert werden. Die Befehle dazu sind der Bedienungsanleitung entnommen. Hier die notwendigen Schritte:
Sender | Empfänger | Erklärung | |
+++ | +++ | (ohne Entertaste!) Kommunikation starten, Antwort "OK" | |
TWRE↵ | TWRE↵ | Auf Herstellereinstellungen resetten | |
ATMY1234↵ | ATMY5678↵ | XBee ID festlegen | |
ATDL5678↵ | ATDL1234↵ | Ziel-XBee festlegen (low byte) | |
ATDH0↵ | ATDH0↵ | Ziel-XBee (high byte) | |
ATID0↵ | ATID0↵ | Netzwerk-ID festlegen | |
ATWR↵ | ATWR↵ | Daten dauerhaft auf dem XBee speichern. |
Dieses Beispiel kann man zur Fehlersuche benutzen wenn nicht klar ist, ob der Sender überhaupt sendet:
Transmitter | Receiver |
Arduino ohne
Drahtbrücke mit Xbee-Shield
(Jumper auf Xbee): Arduino sendet im Wechsel H und L |
Arduino im Resetmodus
(Drahtbrücke!)
mit XBee-Shield (Jumper auf USB) Terminal der Software X-CTU |
Sender | Empfänger |
Der Sender sendet im Wechsel
ein H und L (beide binär kodiert).
Der Empfänger schaltet je nach Zustand die LED an Port 13 an
bzw.
aus.
Das Arduino Uno Board hat gegenüber den anderen (wie z.B. Leonardo, ...) den großen Vorteil, dass man den µC aus seiner Fassung nehmen und für Projekte auf ein Breadboard stecken oder eine Platine löten kann. Zunächst wird kurz gezeigt, wie man einen im Arduino-Board programmierten Atmega standalone betreibt. Im zweiten Schritt werden wir einen Arduino Bootloader auf einen nackten Atmega 328 brennen.
Ihr möchtet einen defekten Atmega 328 auf einem Arduinoboard ersetzen oder plant ein Projekt und möchtet einen fabrikneuen Atmega mit dem Arduino Bootloader versehen? Hier lernt ihr, wie ihr den Bootloader mit einem Uno-Board brennt.
[Achtung: bin noch dabei das Prozedere zu testen!]
Wenn du eine LED direkt an 5 V anschließt geht sie wahrscheinlich kaputt!
Du musst erst herausfinden, welche Vorwärtsspannung deine LED hat - meist sind dies etwa 1,5 Volt. Damit kannst du den Wert eines geeigneten Vorwiderstands berechnen, das geht ganz einfach:
Wenn du einen Sensor, z.B. einen Temperaturfühler, mit dem Arduino auslesen willst, benötigst du eine Spannungsteilerschaltung. Die Schaltung heißt so, weil sich die Spannung in zwei Teile aufteilt: je größer ein Widerstand, desto größer ist die Spannung, die an ihm abfällt. Im gezeigten Beispiel ist der untere Widerstand 9x so groß wie der obere, d.h. am Eingangsport würden 4,5 V anliegen.
Liest du den Eingangsport nun mit digitalRead(10) aus, so wird high eingelesen.
Wählst du statt dessen
einen analogen Eingangsport, z.B. A0, so wäre der Wert etwa
921.
Grund: Die Auflösung beträgt 10 bit, d.h. der
kleinste erfassbare Wert ist 0 und der höchste 1023 (=
2^10-1).
Das
Potenzial 4,5 V entspricht 9/10 des Gesamtpotenzials. Nimmst du nun 90%
von 1023, erhältst du etwa 921.
a) Schließe einen Push-Button von 5 V aus an den analogen Port A2 an und logge die Messwerte über den Serial Monitor (Taster offen / Taster zu).
b) Verbinde nun den Taster zusätzlich von A2 aus über einen 10 kΩ - Widerstand mit GND und logge erneut die Messwerte in beiden Tasterstellungen.
Ist ein digitaler Port als Eingang geschaltet, so kann man elegant interne Pullups setzen, indem man
pinMode(pinNummer, INPUT);
digitalWrite(pinNummer, HIGH);
verwendet. Intern wird der Eingang dadurch über einen 20 kΩ - Widerstand mit HIGH verbunden und kann dann durch ein externes Signal auf LOW gezogen werden. Das ist prima, denn so sparst du dir etwas Verkabelungsaufwand!
Einen kleinen Nachteil gibt es gegenüber dem pull-down-Widerstand aus dem Beispiel oben: Der Stromverbrauch ist höher, da permanent ein (allerdings sehr kleiner) Strom fliesst.
Um z.B. ein Relais mit dem µC zu steuern, oder um Musik in vernünftiger Lautstärke über einen Lautsprecher auszugeben reicht die maximale zulässige Stromstärke der Ports nicht aus. Abhilfe schafft ein Transistor (oder MOSFET), wobei es zwei verschiedene Möglichkeiten gibt, den Transistor zu betreiben:
Hier wurde neben dem Widerstand ein Trimmpoti verwendet (genauer zwei Beinchen desselben), um die Lautstärke regeln zu können.
Beachte, dass du je nach Transistor im Datenblatt nachsehen musst, wo sich die Basis, der Collector und der Emitter befinden.
verwendest du, wenn du eine stabilisierte Ausgangsspannung benötigst. Du kannst sie z.B. auch verwenden, um Servomotoren, die 6 V benötigen, an einer 9 V - Batterie zu betreiben.
Du benötigst zwei Kondensatoren und ggf. eine Diode (siehe Bemerkung). Auszug aus dem Datenblatt des Fairchild LM7806:
![]() |
|
Bemerkung: Verwende bei induktiven Lasten eine Freilaufdiode (z.B. 1N4007) zwischen Aus- und Eingang. Sie schützt den Festpannungsregler, denn er geht kaputt, wenn die Ausgangsspannung größer als die Eingangsspannung ist (z.B. beim Abschalten einer induktiven Last).
Die Funktionsweise eines Relais
wird sehr schön bei Strippenstrolch
erklärt, außerdem kannst du dir dieses kleine
Filmchen
ansehen.
Wir wollen den Steuerstromkreis unseres Relais' natürlich mit
dem
µC ansteuern, das ganze sieht dann so aus:
Die LED dient lediglich zur Darstellung, wann das Relais anziehen soll. Eine "normale" Diode wird immer antiparallel zur Spule des Relais' geschaltet, um Spannungsspitzen (durch den Induktionsvorgang beim Ausschaltvorgang) abzufangen.
Folgende Bücher lese ich gerne:
Titel | Autor | Verlag |
ISBN |
Arduino Cookbook (engl.) | Michael Margolis | O'Reilly | 978-0-596-80247-9 |
Arduino: Ein schneller Einstieg in die Mikrocontroller-Entwicklung | Maik Schmidt | dpunkt.verlag | 978-3-89864-764-9 |
Arduino: Mikrocontroller-Porgrammierung mit Arduino/Freeduino | Ulli Sommer | Franzis | 978-3-645-65034-2 |
Arduino: Physical Computing für Bastler, Designer & Geeks | Odendahl, Finn & Wenger | O'Reilly | 978-3-89721-893-2 |
Arduino: Praxiseinstieg | Thomas Brühlmann | mitp | 978-3-8266-5605-7 |
Link | Beschreibung |
hackaday.com/ | Herrlich verrückte Bastelbeispiele |
blog.littlebirdelectronics.com | Nette Tutorials |
jeremyblum.com | Videotutorials |
ladyada.net | Neben dem Einsteigertutorial viele hacks |
macherzin.net | Viele nette Beispiele in deutscher Sprache. |
tronixstuff | Eine Einführung und gute Tutorials |