Anleitung: Wortuhr im Bilderrahmen
Ein Mikrocontroller steuert 144 kleine LEDs, die sich in einem 12x12-großen Raster in einem Bilderrahmen verstecken. Eine schwarze Schablone mit transparenten Buchstaben lässt die Uhr Wörter und Sätze bilden. Dank des batteriebetriebenen Echtzeituhr-Moduls weiß die Wortuhr auch nach unterbrochenener Stromversorgung noch, wie spät es ist und hin und wieder zeigt sie auch mal eine versteckte Extra-Botschaft an.
Das Material
Die folgende Aufstellung führt die benötigten Utensilien zum Nachbau der Wortuhr auf. Die größte Schwierigkeit besteht darin, ein Gehäuse mit ausreichender Tiefe zu finden, in dem alles verstaut werden kann. Die Wahl fiel hier auf einen Bilderrahmen des Modells „Ribba“ von IKEA.
- Quadratischer, tiefer Rahmen, Innenmaße ca. 21 cm x 21 cm x 4 cm
- Arduino Nano (oder nachgebautes Produkt)
- LED-Streifen WS2812B mit 144 LEDs mit Abständen von 1,67 cm (60 LEDs/m)
- RTC-Modul DS3231
- Kondensator mit der Kapazität 1000 µF
- Widerstand mit etwa 400–500 Ω
- Netzteil 5 V, 2 A, mit passender Buchse
- 2 Mini-USB-Kabel, eines mit Anschluss für den PC
- Litzen, Steckverbinder, Lötkolben u.ä.
- Schwarze Pappe, bedruckbare Folien, Bastelmaterial
Die Elektronik
Die WS2812B-LEDs werden in zwölf Streifen mit je 12 LEDs zertrennt und im Abstand von 1,67 cm (Mitte-Mitte) auf die Rückwand des Bilderrahmens geklebt. Nun muss jeder Streifen mit 5 V und mit Masse vom Netzteil versorgt werden. Außerdem werden die Datenleitungen miteinander verbunden, wobei auf die richtige Polarität geachtet werden muss: Din
der ersten LED wird über einen Widerstand mit etwa 400–500 Ω mit einem Digitalpin des Arduinos verbunden. Do
der letzten LED dieser Zeile wird mit Din
der nächsten Zeile verbunden und so weiter. Die LED-Kette läuft dann beispielsweise serpentinenartig von oben nach unten:
Um die aktuelle Zeit abrufen zu können, wird das Echtzeituhr-Modul DS3231 mit den SDA- und SCL-Pins des Arduinos verbunden. Die Uhr wird mit 3,3 V Spannung versorgt. Damit die Zeit auch ohne Netzspannung weiterläuft, braucht das Modul eine handelsübliche CR2032-Batterie oder einen Akku vom Typ LIR-2032.
Der Arduino selbst wird über den USB-Anschluss mit Strom versorgt. Dazu kann ein Mini-USB-Kabel verwendet werden, das durchgeschnitten und vorsichtig abisoliert wird. Die rote Ader aus dem Kabel wird mit 5 V verbunden, die schwarze Ader mit Masse. Ein Kondensator mit 1000 µF puffert etwaige Spannungsspitzen aus dem Netzteil.
Die Schablone
Die Schablone besteht aus 144 Buchstaben und passt auf eine DIN-A4-Seite. Die Formulierungen Es ist (um/kurz/viertel/fünf/zehn/zwanzig) (Minuten) (vor/nach) (halb) 1–12 Uhr (morgens/mittags/nachmittags/abends/nachts)
können mit Überlappungen in 132 Buchstaben untergebracht werden, sodass zwölf Buchstaben (in der Vorlage X) zur freien Verwendung übrig bleiben. Natürlich ist es auch denkbar, die bereits verwendeten Buchstaben für eine individuelle Botschaft mitzunutzen oder andere Formulierungen für die Uhrzeiten zu wählen. Zum Bearbeiten der Vorlage empfiehlt sich zum Beispiel Inkscape.
Mit einer dicktengleiche Schrift (monospace) liegen die Buchstaben, die in dieser Vorlage durch Leerzeichen getrennt sind, genau übereinander. Die Schablone muss nun – am besten in zweifacher Ausfertigung für eine stärkere Opazität (Lichtundurchlässigkeit) – auf Folie gedruckt werden.
Der Zusammenbau
Beim Zusammensetzen der Uhr ist noch etwas Bastelarbeit gefragt. In den Rahmen werden auf die Acrylglasscheibe die zwei bedruckten Folien (ggf. mit Passepartout aus schwarzer Pappe) exakt übereinander gelegt. Danach folgen einige Lagen genarbter Prospekthüllen, um das Licht zu streuen und so die Buchstaben gleichmäßiger auszuleuchten. Wichtig ist jetzt ein etwa 1 cm hohes Raster, das zum Beispiel aus 2 x 11 schwarzen Pappstreifen in stundenlanger Pfriemelei gebastelt werden kann (Zuschriften mit besseren Ideen sind herzlich willkommen). Zum Schluss wird die Rückwand mit den LEDs eingesetzt und die Elektronik so angebracht, dass sie gut sitzt, aber auch noch Spiel hat, um später noch den USB-Anschluss umstecken zu können. Auch sollte an eine Bohrung für den Stromanschluss gedacht werden. Allgemein können zu diesen Bastelschritten nicht viele Tipps gegeben werden, weil die Vorgehensweise stark davon abhängt, wie die Abmessungen des Rahmens sind. Auch hat IKEA den Aufbau des „Ribba“-Rahmens in den letzten Jahren verändert.Das Programm
Um die Uhr zum Leuchten zu bringen, werden die Bibliotheken Time
, DS1307RTC
und Adafruit NeoPixel
benötigt, die in den Paketquellen der Arduino IDE zu finden sind. Anschließend werden einige Konstanten definiert: Für jedes Wort auf der Uhr müssen die Positionen der LEDs abgezählt werden und diese werden dann zusammen mit der Wortlänge in einen Array geschrieben. Außerdem werden im RGB-Format {[0–255], [0–255], [0–255]} die Farben angegeben, in denen die Uhr leuchten soll. Die Farbe wechselt hier je nach Wochentag, wobei für die Zeit zwischen 22 und 8 Uhr jeweils zusätzlich eine gedimmte Farbe angegeben ist. Es erfordert durchaus etwas Ausprobieren, Farben zu finden, die die Buchstaben schön und gleichmäßig ausleuchten. Grundsätzlich sollte hier ein Bruchteil der maximalen LED-Helligkeitswerte ausreichen. Bei größerer Leuchtstärke reicht ein 2-A-Netzteil möglicherweise nicht mehr aus, um die LEDs mit Strom zu versorgen.
/*
Wortuhr.ino by Walfried Schneider, www.walfriedschneider.de
Controlling a wordclock with WS2812B LED strips and a DS3231 RTC module using the following libraries:
Time, 1.6.0, https://github.com/PaulStoffregen/Time
DS1307RTC, 1.4.1, https://github.com/PaulStoffregen/DS1307RTC
Adafruit NeoPixel, 1.8.1, https://github.com/adafruit/Adafruit_NeoPixel
*/
// Include libraries
#include "TimeLib.h"
#include "DS1307RTC.h"
#include "Adafruit_NeoPixel.h"
// Set the Arduino pin connected to the LEDs
#define LED_PIN 2
// Initialise 144 NeoPixel LEDs
Adafruit_NeoPixel Leds = Adafruit_NeoPixel(144, LED_PIN, NEO_GRB + NEO_KHZ800);
// Set the locations of words/letters
const byte esist[5+1] = {5,144,143,141,140,139};
const byte kurz[4+1] = {4,137,136,135,134};
const byte viertel[7+1] = {7,124,125,126,127,128,129,130};
const byte zwanzig[7+1] = {7,120,119,118,117,116,115,114};
const byte fuenf[4+1] = {4,112,111,110,109};
const byte zehn[4+1] = {4,97,98,99,100};
const byte minuten[7+1] = {7,102,103,104,105,106,107,108};
const byte vor[3+1] = {3,96,95,94};
const byte nach[4+1] = {4,93,92,91,90};
const byte halb[4+1] = {4,88,87,86,85};
const byte stunden[][7] = {{5,53,54,55,56,57},{3,48,47,46},{4,81,82,83,84},{4,73,74,75,76},{4,49,50,51,52},{4,57,58,59,60},{5,72,71,70,69,68},{6,45,44,43,42,41,40},{4,77,78,79,80},{4,40,39,38,37},{4,64,63,62,61},{3,67,66,65}};
const byte s[1+1] = {1,45};
const byte uhr[3+1] = {3,25,26,27};
const byte morgens[7+1] = {7,29,30,31,32,33,34,35};
const byte abends[6+1] = {6,24,23,22,21,20,19};
const byte nachts[6+1] = {6,18,17,16,15,14,13};
const byte nachm[4+1] = {4,2,3,4,5};
const byte mittags[7+1] = {7,6,7,8,9,10,11,12};
const byte botschaft[12+1] = {12,142,138,133,123,125,126,113,107,73,76,78,79};
// Specify colors for Su-Mo (day colors followed by night colors) in RGB mode
const byte colorset[14][3] = {{36,8,0},{0,24,8},{16,16,0},{24,0,0},{0,16,16},{0,32,0},{0,8,24},{9,2,0},{0,8,2},{8,8,0},{8,0,0},{0,4,4},{0,8,0},{0,4,12}};
// Declare necessary variables
byte c;
byte i;
// Sync with RTC and start LEDs
void setup() {
setSyncProvider(RTC.get);
Leds.begin();
i = 0;
}
// Update LEDs according to time and day of the week every 10 seconds
// Show special message every 102 * 10 seconds = 17 minutes
void loop() {
Leds.clear();
c = getColor(hour(), weekday());
if (i == 0){
turnOn(botschaft, c);
}
else {
setMinutes(minute(), c);
setHour(minute(), hour(), c);
};
Leds.show();
i = (i+1)%102;
delay(10000);
}
// Set LEDs according to (m)inutes: es ist, kurz, viertel, zwanzig, fünf, zehn, minuten, vor, nach, halb
void setMinutes(byte m, byte c) {
turnOn(esist, c);
if ((m > 0 && m < 3) || m == 28 || m == 29 || m == 31 || m == 32 || m > 57 ){turnOn(kurz, c);}
if ((m > 12 && m < 18) || (m > 42 && m < 48)){turnOn(viertel, c);}
if ((m > 17 && m < 23) ||(m > 37 && m < 43)){turnOn(zwanzig, c);}
if ((m > 2 && m < 8) ||(m > 22 && m < 28) || (m > 32 && m < 38) ||(m > 52 && m < 58)){turnOn(fuenf, c);}
if ((m > 7 && m < 13) || (m > 47 && m < 53)){turnOn(zehn, c);}
if ((m > 2 && m < 13) || (m > 17 && m < 28) || (m > 32 && m < 43) || (m > 47 && m < 58)){turnOn(minuten, c);}
if ((m > 22 && m < 30) || (m > 37)){turnOn(vor, c);}
if ((m > 0 && m < 23) || (m > 30 && m < 38)){turnOn(nach, c);}
if (m > 22 && m < 38){turnOn(halb, c);}
}
// Set LEDs according to (h)ours: 1-12, uhr, ein(s), nachts, morgens, mittags, nachmittags, abends
void setHour(byte m, byte h, byte c) {
byte h2;
if (m < 23) {h2 = h;}
else {h2 = h+1;}
turnOn(stunden[h2%12], c);
if (m < 3 || m > 57){turnOn(uhr, c);}
else if (h2%12 == 1) {turnOn(s, c);}
if (h2 < 6 || h2 > 22){turnOn(nachts, c);}
if (h2 > 5 && h2 < 12){turnOn(morgens, c);}
if (h2 > 11 && h2 < 18){turnOn(mittags, c);}
if (h2 > 14 && h2 < 18){turnOn(nachm, c);}
if (h2 > 17 && h2 < 23){turnOn(abends, c);}
}
// Turn the LEDs on
void turnOn(byte words[], byte c) {
for(byte i = 1; i < words[0] + 1; i++){
Leds.setPixelColor(words[i]-1, Leds.Color(colorset[c][0],colorset[c][1],colorset[c][2]));
};
};
// Get the day or night color for a time and day of the week
byte getColor(byte h, byte w) {
if (h > 8 && h < 22) {
return w-1;
}
else {
return w+6;
}
};
Die Wortuhr fragt alle 10 Sekunden die aktuelle Zeit ab und entscheidet dann nach einem Algorithmus, welche LEDs angeschaltet werden müssen. Alle 102 Zyklen, also alle 17 Minuten, wird als Extrafunktion statt der Uhrzeit die zusätzliche Botschaft angezeigt.
Die Uhrzeit einstellen
Das Programm der Uhr arbeitet stets mit der Lokalzeit. Vor der ersten Verwendung und jedes halbe Jahr (Stand: 2021 ;-)) muss die Uhrzeit neu eingestellt werden. Dabei ist das Skript TimeRTCSet
aus der Bibliothek Time
behilflich, das auf den Arduino hochgeladen werden muss und dann einen Zeitstempel auf der USB-Verbindung entgegennimmt. Unter Linux kann mit folgenden Befehlen die aktuelle Uhrzeit an das USB-Gerät ttyUSB0
gesendet werden:
exec 3<> /dev/ttyUSB0
date +T%s --date="7202 seconds" >&3
Hierbei gibt date +T%s
den aktuellen Unix-Zeitstempel aus und --date="7202 seconds"
korrigiert die Unixzeit zur Lokalzeit, indem beispielsweise eine ("3600 seconds"
, für MEZ) oder zwei Stunden ("7200 seconds"
, für MESZ) plus ggf. einige Sekunden technische Verzögerung angehängt werden. Die Befehle müssen von einem Unix-Benutzer ausgeführt werden, der Schreibzugriff auf /dev/ttyUSB0
hat. Zum Schluss wird das Wortuhr-Programm wieder auf den Mikrocontroller zurückgeschrieben.