Tinkering Arduino

eine kleine Welt der Elektronik.

Hello World, Einstieg in Eine Neue Welt

So, nachdem ich hoffentlich schon ein wenig die Neugier geweckt habe, möchte ich auch gleich mit dem ersten sehr kleinen Projekt starten. Wie im ersten Beitrag schon angekündigt, soll das Standardbeispiel der wohl meisten Programmiersprachen ein “Hello World!” ausgeben.

Ich gehe an dieser Stelle über das übliche “unboxing” hinaus. Es gibt nicht viel spannendes an der Schachtel, außer das sie einem wahnsinnig klein vorkommt.

In C auf einem Desktop Rechner oder Notebook hätte man diese Ausgabe relativ schnell hinbekommen und am Ende “Hello World!” auf der Kommandozeile stehen gehabt.

Da der Arduino ohne zusätzliche Bauteile wie ein Flüssigkristallanzeige (LCD) oder ein OLED Display kein “Hello World” schreiben kann, beschränken wir uns auf die Leuchtdiode (LED), die sich auf den meisten Arduinos befindet. Diese können wir so blinken lassen dass die gewünschte Information ausgegeben wird. Was liegt da näher, als den Morsecode zu verwenden. Dort ist neben dem Morsealphabet auch erklärt, wie das Verhältnis zwischen kurzen Zeichen (dit) und langen Zeichen (dah) und den Pausen ist, denn die Morsegeschwindigkeit ist nicht fest definiert. Beim Senden kommt es auch auf die Fähigkeiten der sendenden und der empfangenden Person an.

Die auf dem Arduino verbaute LED soll ein “Hello World”, oder besser ein “di-di-di-dit dit di-dah-di-dit di-dah-di-dit dah-dah-dah / di-dah-dah dah-dah-dah di-dah-dit di-dah-di-dit dah-di-dit” blinken. Legen wir also los:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
  Hello World,
  Use an LED to morse the "hello world".
  
  hello world
  .... . .-.. .-.. ---   .-- --- .-. .-.. -..
  di-di-di-dit dit di-dah-di-dit di-dah-di-dit dah-dah-dah   di-dah-dah dah-dah-dah di-dah-dit di-dah-di-dit dah-di-dit
*/

// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;

// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
}

Zeilen 1-8: enthalten eine Beschreibung über das Programm. Bei diesem Block handelt es sich um einen Mehrzeiligen Kommentar, der mit einem /* beginnt und einem */ abgeschlossen wird.

Zeile 10-12: beginnt mit einzeiligen Kommentaren und einer Definition in Zeile 8. Einzeilige Kommentare beginnen mit //. Sie müssen nicht abgeschlossen werden. In Zeile 8 wird led definiert. Beim Kompilieren des Programms wird jedes Auftreten von led durch den ganzzahligen Wert 13 ersetzt. Man könnte auch im ganzen Programm statt des Namens led den Wert 13 einsetzen, aber bei komplexeren Programmen kann man nach spätestens zwei Wochen nicht mehr nachvollziehen, was das Programm macht. Da wir an dem digitalen Ausgang 13 eine LED nutzen ist led ein brauchbarer Name.

Zeile 14-18: beginnt wieder mit einem einzeiligen Kommentar und einer Funktion mit dem Namen setup(). Diese Funktion ist in jedem Arduino Programm Pflicht und wird ausgeführt, wenn der Reset-Knopf gedrückt oder der Arduino mit Spannung versorgt wird. Spannend scheint noch die Zeile 13. Hier wird eine Funktion der Arduino IDE aufgerufen, die für den PIN 13 (wir erinnern und, dass led durch den Wert 13 ersetzt wird) definiert, dass es sich hier um eine Ausgang handelt.

Es gibt eine weitere Funktion, die jedes Arduino Programm enthält. Die Funktion loop() wurde in dieser ersten Version noch nicht optimiert. Sie wird wie der Name schon verrät die ganze Zeit ausgeführt, bis der Reset-Knopf gedrückt wird, oder die Stromversorgung unterbrochen wird. Kommt das Programm an Ende der Funktion an, beginnt es wieder von vorn.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// define the timespan how long a dit is. here 500ms
int dit_delay = 500;

// the loop routine runs over and over again forever:
void loop() {
  // h
  // ....
  // di-di-di-dit
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1*dit_delay);        // wait for a dit_delays
  digitalWrite(led, LOW);    // turn the LED off (LOW is the voltage level)
  delay(1*dit_delay);        // wait for 1 dit_delays
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1*dit_delay);        // wait for a dit_delays
  digitalWrite(led, LOW);    // turn the LED off (LOW is the voltage level)
  delay(1*dit_delay);        // wait for 1 dit_delays
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1*dit_delay);        // wait for a dit_delays
  digitalWrite(led, LOW);    // turn the LED off (LOW is the voltage level)
  delay(1*dit_delay);        // wait for 1 dit_delays
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1*dit_delay);        // wait for a dit_delays
  digitalWrite(led, LOW);    // turn the LED off (LOW is the voltage level)
  delay(3*dit_delay);        // wait for 3 dit_delays

  // e
  // .
  // dit
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1*dit_delay);        // wait for a dit_delays
  digitalWrite(led, LOW);    // turn the LED off (LOW is the voltage level)
  delay(3*dit_delay);        // wait for 3 dit_delays

  // ...
}

Die Version 1: hello_world_v1.ino.

Zeile 1-2: definiert und eine weitere Konstante dit_delay. Diese nutzen wir später, um die Geschwindigkeit zu steuern. Im Programm wird der vorgegebene Wert ersetzt. Wir können aber die Geschwindigkeit des Blinkens erhöhen, in dem wir die Zeitspanne für ein dit verringern.

Dann beginnt der Funktionsaufrug loop().

Zeile 9: zeigt die erste unbekannte Stelle. Hier wird die Funktion digitalWrite()
der Arduino IDE aufgerufen. Hier wird der PIN 13 auf HIGH gesetzt, demnach die LED mit einer Spannung von 5 Volt versorgt.

Zeile 10: ruft eine weitere Funktion auf. delay() definiert eine Zeitspanne in Millisekunden, die das Programm wartet, bis die nächste Zeile im Programmablauf aufgerufen wird. Da PIN 13 auf HIGH gesetzt wurde, bleibt dieser Zustand für die definierte Zeitspanne erhalten. Wir hatten in Zeile 2 den Wert auf 500 festgesetzt.

Zeile 11: setzt Pin 13 auf LOW. Damit liegt an diesem Ausgang keine Spannung mehr an und die LED erlischt.

Zeile 12: definiert den Zeitraum, wie lang das Programm wieder warten soll.

Das könnte nun ewig so weiter gehen, aber selbst der Kommentar für die einzelnen Buchstaben ist wenig hilfreich. Stellen wir uns vor, wir wollten ganze Sätze so aufschreiben und müssten einen Fehler ausbessern. Keine Chance. Was können wir also tun?

Wir können nicht nur auf durch die IDE bereitgestellte Funktionen zurückgreifen, sondern auch eigene Funktionen schreiben und diese aufrufen. Wenn man sich den Artikel zum Morsecode anschaut gibt es fünf Funktionen die man benötigt. Eine Funktion dit(), eine Funktion dah() und drei Funktionen die die unterschiedlichen Pausen definieren. Die Pause zwischen zwei gesendeten Symbolen, sonst wäre die Unterscheidung zwischen drei kurzen Symbolen (dit) und einem langen Symbol (dah), welches drei mal so lang ist wie ein kurzes Symbol, nicht zu machen. Neben der Pause zwischen zwei Symbolen gibt es die Pause zwischen Buchstaben und die zwischen Worten.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void dit() {
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1*dit_delay);        // wait for a ditdelays
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
}

void dah() {
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(3*dit_delay);        // wait for 3 ditdelays
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW  
}

void inter_element_gap() {
  delay(1*dit_delay);        // wait for 1 ditdelays
}

void short_gap() {
  delay(3*dit_delay);        // wait for 3 ditdelays
}

void medium_gap() {
  delay(7*dit_delay);        // wait for 7 ditdelays
}

Mit diesen Funktionen können wir das obige Programm gleich etwas verbessern.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// the loop routine runs over and over again forever:
void loop() {

  // h
  // ....
  // di-di-di-dit
  dit();
  inter_element_gap();
  dit();
  inter_element_gap();
  dit();
  inter_element_gap();
  dit();
  short_gap();

  // e
  // .
  // dit
  dit();
  short_gap();

  // ...
}

Die Version 2: hello_world_v2.ino.

Spätestens wenn wir eigene längere Texte so ausgeben wollen, stößt auch dieses Programm an seine Grenzen. Wie wäre es, wenn wir für alle 26 Buchstaben im Alphabet eine Funktion schreiben.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void e() {
  // e
  // .
  // dit
  dit();
}

void h() {
  // h
  // ....
  // di-di-di-dit
  dit();
  inter_element_gap();
  dit();
  inter_element_gap();
  dit();
  inter_element_gap();
  dit();
}

Exemplarisch habe ich hier die Funktionen h() und e() herausgegriffen Diese geben die Buchstaben e und h aus, ohne die Pause danach, denn wir wissen nicht, ob nach dem Buchstaben ein weiterer Buchstabe folgt oder eine noch längere Pause folgen muss, weil ein neues Wort beginnt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// the loop routine runs over and over again forever:
void loop() {
  h();
  short_gap();

  e();
  short_gap();

  l();
  short_gap();

  l();
  short_gap();

  o();
  medium_gap();

  w();
  short_gap();

  o();
  short_gap();

  r();
  short_gap();

  l();
  short_gap();

  d();
  medium_gap();
}

Die Version 3: hello_world_v3.ino.

Damit sieht unsere Hauptschleife schon sehr aufgeräumt aus. Mit ein bisschen Mathematik können wir hier noch einmal optimieren. Denn nach einem Buchstaben folgt, bis auf einige Ausnahmen, die wir hier nicht betrachten wollen, immer eine Pause. Diese ist so lang wie drei dit, wenn danach ein weiterer Buchstabe folgt, oder sieben dit lang, wenn danach kein weiterer Buchstabe folgt und statt dessen ein neues Wort beginnt. Die Pause zwischen zwei Wörtern kann man wiederum als Pause nach einem Buchstaben plus einer Pause von der Länge sechs weiteren dits betrachten. Stellen wir also die Funktionen um:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void e() {
  // e
  // .
  // dit
  dit();
  short_gap();
}

void h() {
  // h
  // ....
  // di-di-di-dit
  dit();
  inter_element_gap();
  dit();
  inter_element_gap();
  dit();
  inter_element_gap();
  dit();
  short_gap();
}

Dann müsste unsere Hauptfunktion loop() wie folgt aussehen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// the loop routine runs over and over again forever:
void loop() {
  h();
  e();
  l();
  l();
  o();
  medium_gap2();

  w();
  o();
  r();
  l();
  d();
  medium_gap2();
}

void medium_gap2() {
  delay(6*dit_delay);        // wait for further 6 dit_delays
}

Die Version 4: hello_world_v4.ino.

Damit kann man in der Hauptschleifen loop() schon recht gut lesen, was man ausgeben möchte.

Zum Schluss möchte ich gern noch eine Datei zum Download bereit stellen, die 26 Buchstaben des Alphabets umfasst.

Die Version 5: hello_world_v5.ino.

Viel Spass mit dem Programm, beim Spielen und verändern.

Comments