blog · git · desktop · images · contact


Arduino-Wecker – Teil 0: Gehversuche mit Uno und Display

2014-06-29

Bisher war ich beim Wecker-Selbstbau etwas zögerlich, weil ich halt überhaupt keine Erfahrung auf dem Gebiet hatte. Wo fängt man an, wie fängt man an und womit? Man will ja auch nicht komplett blind Bauteile kaufen – auch, wenn die einzeln alle billig sind, kosten sie in der Summe schon viel Geld.

Ich bekam dann zum Glück einen Arduino Uno und ein ganzes Kästchen voller Teile zum Spielen ausgeliehen. :-) Vielen Dank! Ein bisschen rumprobiert und Zack, angefixt.

Ich will den Wecker schrittweise bauen und ausbauen: Erst eine Uhr, dann eine Funkuhr, dann Buzzer und Knöpfe, schließlich richtiger Ton über einen Lautsprecher und mit entsprechendem Sound-Modul. Schritt Null ist aber erst einmal: Eine 7-Segment-Anzeige über einen Arduino ansteuern.

Was habe ich bisher gekauft?

Warum der Lötkolben? Weil man die 7-Segment-Anzeige mit dem I2C-Backpack verlöten muss. Warum keine Kabel? Habe ich durchaus bestellt, aber nicht aufgepasst – jetzt kommen sie aus China und haben eine halbe Ewigkeit als Lieferzeit. Eigentlich wollte ich nämlich nicht sechs Wochen warten, sondern möglichst sofort loslegen. Deswegen darf man mir auch zu Recht vorwerfen: „Das hättest du günstiger haben können!“ Im Moment verwende ich noch die ausgeliehenen Kabel, aber die nächste Bestellrunde bei einem deutschen Versandhändler steht bald an.

Okay, mein letztes Löt-Erlebnis ist bestimmt 20 Jahre her. Ich hatte als Kind mal einen Radio-Bausatz zusammengelötet… Seit dem nichts mehr. Ich habe mir also zunächst ein bisschen alte Hardware geschnappt (dafür hebt man die ja schließlich auf) und dort Bauteile ab- und wieder angelötet. Diese Trockenübungen waren auch bitter nötig. Heute habe ich mich dann endlich an das Adafruit-Display getraut und das Löten hat auch ganz gut funktioniert, obgleich natürlich noch viel Luft nach oben ist. Erst einmal bin ich sehr froh, dass nichts kaputt gegangen ist. ;-)

Das Display ist ganz gut dokumentiert. Man kriegt da schnell etwas angezeigt:

Foto: 1337

Der Einfachheit halber verwende ich die Adafruit-LED-Backpack-Library, die auch bei Adafruit verlinkt ist. Man könnte das Display mittels Wire auch ein bisschen manueller ansteuern, aber ich glaube nicht, dass ich das im Moment brauche.

Mit der Funktion „writeDigitRaw()“ und einer Bitmaske kann man jede LED einzeln ansteuern. Ich muss gestehen, dass das etwas ist, wovon ich früher als Kind immer geträumt habe. ;-) Diese 7-Segment-Anzeigen haben es mir echt angetan. Mit dem folgenden bisschen Code kann man dann schnell sehen, welches Bit für welche LED steht. Der Positionsparameter ist einfacher: Von links nach rechts ist es 0 bis 4, wobei die 2 der Doppelpunkt in der Mitte ist.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>

Adafruit_7segment matrix = Adafruit_7segment();

void setup()
{
  matrix.begin(0x70);  /* i2c address */
  matrix.setBrightness(15);
}

void loop()
{
  for (int i = -1; i < 8; i++)
  {
    int m = i == -1 ? 0 : 1 << i;
    matrix.print(m, DEC);
    matrix.writeDigitRaw(0, m);
    matrix.writeDisplay();
    delay(1000);
  }
}

Animation der Bitmasken

So weit, so gut.

Jetzt habe ich natürlich ein Problem. Der Arduino hat keine persistente Uhr. Ich bin noch ein bisschen unschlüssig, wie ich damit umgehen möchte. Brauche ich eine RTC mit Batterie? Das wäre dann vermutlich ein DS1307, entweder so wie im Link einzeln oder als fertiges Bauteil. Da ich jetzt keine Angst mehr vor’m Löten habe, kann ich es auch einzeln kaufen. ;-) Wahrscheinlich ist es sinnvoll, soetwas mit einzubauen. Aber brauche ich es wirklich? Der Quartz im Arduino soll eine Ganggenauigkeit von ungefähr einer Sekunde pro Tag haben. Und mein Plan ist ja eine Funkuhr. Beim initialen Einschalten wäre es also okay, wenn keine gespeicherte Uhrzeit verfügbar wäre und ich per DCF77 synchronisieren muss. Das hätte den Vorteil, dass ich keine Batterie im Gerät haben muss – ja, die halten ewig, trotzdem mag ich den Gedanken nicht. Mal sehen.

Für das erste Rumspielen schiebe ich die aktuelle Zeit einfach per seriellem Interface bzw. USB rüber:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>

Adafruit_7segment matrix = Adafruit_7segment();

unsigned long time_start = 0;

void setup()
{
  unsigned long s_hour, s_minute, s_second;

  matrix.begin(0x70);  /* i2c address */
  matrix.setBrightness(15);

  /* Show "read" until we got the current time */
  matrix.writeDigitRaw(0, 0b01010000);
  matrix.writeDigitRaw(1, 0b01111001);
  matrix.writeDigitRaw(3, 0b01110111);
  matrix.writeDigitRaw(4, 0b11011110);
  matrix.writeDisplay();

  Serial.begin(9600);
  while (Serial.available() < 9)
    delay(100);

  s_hour = Serial.parseInt();
  s_minute = Serial.parseInt();
  s_second = Serial.parseInt();
  time_start = s_hour * 3600 + s_minute * 60 + s_second;

  /* Show what we have read */
  matrix.print(s_hour, DEC);
  matrix.writeDisplay();
  delay(1000);

  matrix.print(s_minute, DEC);
  matrix.writeDisplay();
  delay(1000);

  matrix.print(s_second, DEC);
  matrix.writeDisplay();
  delay(1000);
}

void loop()
{
  unsigned char s_hour, s_minute, s_second;
  unsigned long t = time_start + millis() / 1000;
  boolean show_seconds = false;

  s_hour = t / 3600;
  t %= 3600;
  s_minute = t / 60;
  t %= 60;
  s_second = t;

  if (!show_seconds)
  {
    if (s_hour < 10)
      matrix.writeDigitNum(0, 0);
    else
      matrix.writeDigitNum(0, s_hour / 10);
    matrix.writeDigitNum(1, s_hour % 10);

    if (s_minute < 10)
      matrix.writeDigitNum(3, 0);
    else
      matrix.writeDigitNum(3, s_minute / 10);
    matrix.writeDigitNum(4, s_minute % 10);
  }
  else
  {
    if (s_minute < 10)
      matrix.writeDigitNum(0, 0);
    else
      matrix.writeDigitNum(0, s_minute / 10);
    matrix.writeDigitNum(1, s_minute % 10);

    if (s_second < 10)
      matrix.writeDigitNum(3, 0);
    else
      matrix.writeDigitNum(3, s_second / 10);
    matrix.writeDigitNum(4, s_second % 10);
  }

  matrix.drawColon(s_second % 2);

  matrix.writeDisplay();
  delay(100);
}

Ja, ist alles noch extrem wackelig und ungenau. Das ist aber erst einmal nicht schlimm. Das ist noch keine ernsthafte Uhr, sondern nur die ersten Gehversuche.

Nach dem Einschalten steht „read.“ auf dem Display:

Foto: „read“

Über’s Terminal schubse ich dann die Zeit hoch:

$ (sleep 5; date '+%H%n%M%n%S') >/dev/ttyACM0

Warum das „sleep 5“? Weil der Arduino sich resettet, sobald er eine serielle Verbindung hat. Und dann braucht er einen Moment, bis er „empfangsbereit“ ist. Dann wird die Zeit geschrieben und die Verbindung wieder geschlossen, er läuft dann alleine weiter – natürlich nur, solange er am Strom hängt.

Foto: Zeitanzeige

Im Code oben kann man „show_seconds“ auf „true“ setzen, dann zeigt er Minuten und Sekunden an statt Stunden und Minuten. Auch das ist nur zum Spielen.

Was noch relevant ist, ist die Helligkeit. Meinen letzten gekauften Wecker musste ich nämlich zurückgeben, weil er einfach krass hell war. Mein Display jetzt ist auch deutlich heller als mein alter Radiowecker. Der Unterschied ist noch ein bisschen größer, als es dieses Bild erahnen lässt:

Foto: Helligkeit

Das ist die niedrigste Helligkeitsstufe des Displays. Vielleicht muss ich im finalen Wecker dann ein getöntes Plexiglas einbauen oder irgendetwas anderes in dieser Richtung. Oder ich mache es ganz anders, nicht mit fertigem Display. Wird sich zeigen.

Ob der Wecker am Ende benutzbar wird oder nicht, ist mir schon fast egal. Auf jeden Fall macht diese Bastelei super viel Spaß. Die ganzen Möglichkeiten, die man auf diesem Feld hat… Wow.

Comments?