2014
|
20480 Pixel für die Arduino-Uhr
Rheinturmuhr mit Arduino auf TFT-Display |
|
Die Seite "Rheinturmuhr mit Arduino" scheint oft gesucht zu werden. Das Projekt aus 2010 wird nun ergänzt mit der TFT-Variante, da diese Displays inzwischen für einstellige Eurobeträge erhältlich sind. Dadurch sind 39 Leuchtdioden zu Darstellung fast zu teuer. Ist auch noch ein SD-Karten-Slot vorhanden, so kann die Uhr sogar als animiertes Foto erscheinen.
|
Mit dem SainSmart 1.8-Zoll-TFT-Display für unter € 10 werden keine LED mehr benötigt, da quasi 20480 Leuchtdioden in Form von Pixel verfügbar sind. Auf diesen 128x160 Pixel sollte ein Rheinturm seinen Platz finden. Die Lampen sind dann wohl sehr klein, aber auch fein. In Wirklichkeit sind sie hier ganze zwei Pixel breit, so dass die Uhr kurz unter dem Restaurant aufhört. Die Zwischenräume wurden so gewählt, dass die Antenne auch noch abgebildet werden kann und so der gesamte Turm Platz findet.
Die Details zum
Anschluss dieses Displays werden im Netz
ausführlich gezeigt. Darum hier nur der
Hinweis auf die Seite Tweaking4All,
die sehr ausführlich zeigt, wie zu
verfahren ist, um das Display zu testen.
Auch die Unterschiede zum ebenfalls weit
verbreiteten Adafruit-TFT sind dort sehr
gut und deutlich - ja fast unübersehbar
- dokumentiert. Benötigt
werden:
- Arduino Uno mit Programmierumgebung
- SainSmart 1.8"-TFT-Display 128x160 Pixel
- SD-Karte (optional)
|
Bibliotheken
Software-Vorbereitung
|
Außer
einer Spannungsversorgung (Vcc und Masse)
werden für die Display-Ansteuerung nur fünf
Verbindungen benötigt. Durch die serielle
Datenübertragung ist Geschwindigkeit etwas
gedrosselt, aber die Vorteile der wenig
aufwendigen Verdrahtung dürften überwiegen.
Außerdem werden in diesem Projekt nur jede
Sekunde Änderungen in der Anzeige notig.
Nach dem Auspacken der Hardware und dem
Bereitlegen der sieben Verbindungsstücke
muss zunächst die entsprechende Bibliothek
verfügbar sein. Auch dies wird auf Tweaking4All,
erläutert. Es handelt sich um
Adafruit_GFX.zip und Adafruit_ST37775.zip.
Später wird noch die SD.zip benötigt, um die
SD-Karte anzusprechen. Die Bibliotheken
werden entpackt und nach z.B. unter Windows
"C:\Users\....\arduino-1.0.6\libraries"
kopiert. Beim nächsten Start der IDE sollten
diese Bibliotheken nun auch mit Beispielen
auswählbar sein.. |
Verdrahtung
Hardware-Vorbereitung
|
Tweaking4All zeigt wie das 1.8"-Display mit dem Arduino verbunden wird. Einmal als Tabelle und auch als Bild. Dort werden auch die beiden Varianten für Low-Speed und High-Speed getrennt aufgeführt. Dieses Projekt benutzt nur die High-Speed-Variante, damit alles flüssig läuft. Betrachtet man die Anzeige von hinten und nummeriert die Anschlüsse beginnend mit der Spannungsversorgung, ergibt sich folgende Tabelle:
Display |
Arduino |
01 VCC
02 GND
03 SCL 04 SDA
05 RS/DC
06 RES
07 CS
08 MSO
09 SCLK
10 MOSI
11 CS
|
VCC
GND
13 SCL
11 SDA
09 RS/DC
08 RES
10 CS
|
Zu beachten sind die Verbindungen 8 und 9 beim Arduino.
Diese sind in der Adafruit-Variante vertauscht, wodurch die Beispiele aus der Bibliothek ohne
diese Änderung nicht funktionieren. Wenn alles richtig aufgebaut ist, können die Beispiele
aufgerufen und getestet werden.
Somit wäre die Initialisierung des Displays wie folgt im Sketch:
#include <SPI.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#define TFT_CS 10
#define TFT_RST 8
#define TFT_DC 9
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,TFT_DC,TFT_RST);
Zum Schluss wird ein TFT-Display-Objekt mit dem Namen 'tft' angelegt.
|
Turmbau
Dreiecke und Rechtecke
|
Mit
zwei Dreiecken und zwei Rechtecken entsteht
ein grafisches Kunstwerk - 'The Towerart' -
welches den Turm darstellt. Das obere
Dreieck stilisiert die Plattform und das
untere Dreieck den eigentlichen Mast vom
Boden bis zur Spitze. Die Rechtecke
symbolisieren die Antennen-Ebenen oberhalb
der Plattform. Außer den Grafik-Routinen zum
Zeichnen stellt die Bibliothek auch eine
RGB-Methode bereit. Mit ihr können Farben
auf die vertraute Rot-Grün-Blau-Art gesetzt
werden, auch wenn das Display nicht alles
darstellen kann. RGB(255,0,0) wäre dann z.
B. volles Rot. Die Methode nennt sich
Color565(red,green,blue) und wurde im Sketch
hier mit einem 'define' kurz zu 'rgb'
umdefiniert. Im Anschluss folgen die
Definitionen der zu verwendenden
Grafik-Farben:
#define rgb Color565
#define BACK rgb(0,0,255)
#define TOWER rgb(63,63,63)
#define LAMP_ON ST7735_YELLOW
#define LAMP_OFF rgb(127,127,127)
#define LAMP_POS rgb(255,0,0)
#define DIGITS LAMP_ON
...
In der setup()-Routine
erfolgt das einmalige Zeichnen des Turms.
Als Hilfsvariablen werden die halbe
Bildschirmbreite w2 und
die Bildschirmhöhe h
benutzt. Der Bildschirmhintergrund wird mit
der Farbe BACK
gezeichnet, der Turm erhält die Farbe TOWER.
...
int w2=tft.width()/2;
int h=tft.height();
tft.fillScreen(BACK);
tft.fillTriangle(w2-15,50,w2+15,50,w2+1,75,TOWER);//V
tft.fillTriangle(w2+1,5,w2-6,h,w2+8,h,TOWER);// ^
tft.fillRect(w2-14,43,30,3,TOWER);//=
tft.fillRect(w2-11,36,24,3,TOWER);//-
...
|
Lampen
2x1 Pixel
|
Die
Uhr setzt sich aus insgesamt 39 Lampen
zusammen. Diese Darstellung ist im Netz
ausführlich beschrieben. Damit die Anzeige
erkennbar bleibt, wurde die Größe der Lampen
zu 2x1 Pixel festgelegt. Mit der Methode
'Versuch und Irrtum' erfolgte die Ermittlung
für dieses TFT-Display experimentell.
Gezeichnet wird mit der schnellen drawFastHLine-Routine
und nicht mit zweimaligem Aufruf von drawPixel.
Die x-Koordinate wird auf die
Bildschirmmitte gesetzt, die Startposition
der y-Koordinate (unterste Lampe) auf die
Bildschirmhöhe minus 8 Pixel, also etwas
über dem Boden. Die Variablen i
enthält Nummer der Lampe, die aktuell
verarbeitet und positioniert werden soll.
Die do-Schleife arbeitet alle 39 Lampen ab.
Im Dekoder (weiter unten) wird entschieden,
ob die Variable 'is' den
Wert true oder false
hat, die aktuelle Lampe die Farbe LAMP_ON
oder LAMP_OFF bekommt.
Die y-Koordinate wird um 2 Pixel geändert
für die nächste Lampe und als Zwischenraum
der Lampenblöcke (9, 5, 9, 5, 9, 2) nochmals
4 Pixel. Zwischen die Blöcke werden die
'Positionsleuchten' aus Jux als Kreise mit
dem Radius 1 gezeichnet mit der Farbe LAMP_POS.
Auf dem Display erscheinen dann Kreuze
aufgrund der Auflösung.
x=tft.width()>>1;
y=tft.height()-8;
i=1; //1-39 Lamps
do //The Clock
{... //Decoder
tft.drawFastHLine(x, y, 2, is ? LAMP_ON : LAMP_OFF);
y-=2;
if(i==9 || i==9+5 || i==9+5+9 || i==9+5+9+5 || i==9+5+9+5+9)
{tft.fillCircle(x-2, y-1, 1, LAMP_POS);
tft.fillCircle(x+2, y-1, 1, LAMP_POS);
y-=4;
}
}while(++i<=39); //all done
|
Dekoder
Fallunterscheidung
|
Die
Fallunterscheidung switch mit
den vielen case in der
C-Syntax ist sehr schreibintensiv, trotzdem
übersichtlicher als endlose
if-then-else-Verschachtelungen. Für jede
Lampe i wird überprüft, ob sie an
oder aus ist. Die logische Variable 'is'
enthält diese Information in der Form true/false
und wird anschließend benutzt, um die Farbe
für die aktuelle Lampe festzulegen. Die 23.
Lampe ist demnach an, wenn die Einer-Stelle
der Minuten m01 größer
als die Differenz zwischen der aktuellen
Lampe (23) und der ersten Lampe (15) in
ihrem Block ist. An dieser Stelle möge sich
der fortgeschrittene Programmierer mit
eleganterem Kode schmücken.
switch (i) //Decode Time To Lamp
{case 1: case 2: case 3: case 4:
case 5: case 6: case 7: case 8:
case 9: is=s01>(i-1); break;
case 10: case 11: case 12: case 13:
case 14: is=s10>(i-10); break;
case 15: case 16: case 17: case 18:
case 19: case 20: case 21: case 22:
case 23: is=m01>(i-15); break;
case 24: case 25: case 26: case 27:
case 28: is=m10>(i-24); break;
case 29: case 30: case 31: case 32:
case 33: case 34: case 35: case 36:
case 37: is=h01>(i-29); break;
case 38: case 39: is=h10>(i-38); break;//Hours
}
Die eigentliche Uhr wurde aus der LED-Variante
der Rheinturmuhr übernommen. Mit Hilfe
der Arduino-Routine millis()
wird der Sekundentakt abgeleitet. Für eine
Demonstration ist das sicher ausreichend.
Funk-, GPS-, TC/IP und RTC-Uhr-Module gibt
es für einen genaueren Lauf. In dieser
Variante läuft die Uhr 10x schneller, wenn
der Analogeingang A0 auf
Masse gelegt wird. Dies wurde für
Demonstrationszwecke eingebaut und könnte
ein Ansatz zum Stellen der Uhr sein.
//Text Time Display
tft.setCursor(5, tft.height()-12);
sprintf(s,"%0d%0d:%0d%0d:%0d%0d",h10,h01,m10,m01,s10,s01);
tft.print(s);
while(TIMEREAD()<(t+interval)); //waiting ...
1000 ms or 100 ms
s01++; //s++;
if(s01>=10){s01=0;s10++;}
if(s10>=6){s10=0;m01++;} //m++;
if(m01>=10){m01=0;m10++;}
if(m10>=6){m10=0;h01++;} //h++;
if(h01>=10){h01=0;h10++;}
if(h10>=2 && h01>=4){h10=0;h01=0;}
interval=analogRead(A0)==0?100:1000;
|
Quelltext
RhineTowerArt gesamt
|
Die Darstellung
des Ergebnisses als Foto ist relativ blass. In
Wirklichkeit ist das Display subjektiv sehr brillant und
hell. Auch die eine oder andere Pixelkoordinate wurde
noch feinjustiert. Hier nun der gesamte Quelltext:
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#define TFT_CS 10
#define TFT_RST 8
#define TFT_DC 9
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
int s10=0,s01=0; // ONESECOND TENSECOND BLOCK
int m10=5,m01=9; // Start 23:59:00 Uhr
int h10=2,h01=3; //
unsigned long t,t0,interval=1000; //TIMEINIT
unsigned long TIMEREAD(void){return millis()-t0;}
//Colors
#define rgb Color565
#define BACK rgb(0,0,255)
#define TOWER rgb(63,63,63)
#define LAMP_ON ST7735_YELLOW
#define LAMP_OFF rgb(127,127,127)
#define LAMP_POS rgb(255,0,0)
#define DIGITS LAMP_ON
void setup(void) {
Serial.begin(9600);
Serial.print("Rheinturmuhr auf 1.8 TFT");
tft.initR(INITR_BLACKTAB);
// initialize like a ST7735S chip, black tab
// The Tower Art
int w2=tft.width()/2;
int h=tft.height();
tft.fillScreen(BACK);
tft.fillTriangle(w2-15,50,w2+15,50,w2+1,75,TOWER);//V
tft.fillTriangle(w2+1,5,w2-6,h,w2+8,h,TOWER);// ^
tft.fillRect(w2-14,43,30,3,TOWER);//=
tft.fillRect(w2-11,36,24,3,TOWER);//-
tft.setTextSize(1);
tft.setTextColor(DIGITS,BACK);
t0=millis();
}
void loop()
{char s[80]; int i,x,y,is;
t=TIMEREAD();
x=tft.width()>>1;
y=tft.height()-8;
i=1; //1-39 Lamps
do //The Clock
{switch (i) //Decode Time To Lamp
{case 1: case 2: case 3: case 4:
case 5: case 6: case 7: case 8:
case 9: is=s01>(i-1); break;
case 10: case 11: case 12: case 13:
case 14: is=s10>(i-10); break;
case 15: case 16: case 17: case 18:
case 19: case 20: case 21: case 22:
case 23: is=m01>(i-15);break;
case 24: case 25: case 26: case 27:
case 28: is=m10>(i-24); break;
case 29: case 30: case 31: case 32:
case 33: case 34: case 35: case 36:
case 37: is=h01>(i-29);break;
case 38: case 39: is=h10>(i-38); break;//Hours
}
tft.drawFastHLine(x, y, 2, is ? LAMP_ON : LAMP_OFF);
y-=2;
if(i==9 || i==9+5 || i==9+5+9 || i==9+5+9+5 || i==9+5+9+5+9)
{tft.fillCircle(x-2, y-1, 1, LAMP_POS);
tft.fillCircle(x+2, y-1, 1, LAMP_POS);
y-=4;
}
}while(++i<=39); //all done
//Text Time
tft.setCursor(5, tft.height()-12);
sprintf(s,"%0d%0d:%0d%0d:%0d%0d",h10,h01,m10,m01,s10,s01);
tft.print(s);
//waiting ...
while(TIMEREAD()<(t+interval));
//one second
s01++; //s++;
if(s01>=10){s01=0;s10++;}
if(s10>=6){s10=0;m01++;} //m++;
if(m01>=10){m01=0;m10++;}
if(m10>=6){m10=0;h01++;} //h++;
if(h01>=10){h01=0;h10++;}
if(h10>=2 && h01>=4){h10=0;h01=0;}
interval=analogRead(A0)==0?100:1000;
}
und hier dieser Quelltext der grafischen Variante als Arduino-ino-Datei
ab IDE 1.0 |
Foto von der SD-Karte Änderungen kleinerer Art
|
Das Display hat auf der Rückseite ein SD-Kartenslot mit einem Klappmechanismus. Falls ein Schnappmechanismus erwartet wird, kann das zu fatalen Problemen mechanischer Art führen ...
Die einfachste Art ein Foto auf dem
TFT-Display darzustellen ist die Anpassung des Beispiels
'spitftbitmap', welches zur Bibliothek 'Adafruit_ST7735' mitgeliefert wird. Zunächst sollte dieses Beispiel ausprobiert werden. Da es sich um ein Beispiel für das Adfruit-Dislplay handelt, muss im Quelltext wieder Anschluss 8 und 9 getauscht werden, damit das SainSmart-Display funktioniert. Auch die vier SD-Anschlüsse kommen nun hinzu:
Display |
Arduino |
01 VCC
02 GND
03 SCL
04 SDA
05 RS/DC
06 RES
07 CS
08 MSO
09 SCLK
10 MOSI
11 CS
|
VCC
GND
13 SCL
11 SDA
09 RS/DC
08 RES
10 CS
12 MISO
13 SCL
11 SDA
04 CS |
Die Doppelbelegung von Arduino-Pin 11 und 13 ist beabsichtigt und stört den Ladevorgang nicht, da entweder geladen oder gezeichnet wird und die CS ja getrennt angesteuert werden. Für das Beispiel ist eine Bilddatei in der Größe 120x160 Pixel im 24-Bit-BMP-Format erforderlich (PC-Grafikprogramm). Die gerade verfügbare SD-Karte hatte hier 4 GB und war FAT32 formatiert. Die Bitmap-Datei muss im Wurzelverzeichnis liegen und ist 60 kB groß. Im Beispielsketch wird der Name 'parrot.bmp' benutzt, der jedoch entsprechend (um und bei Zeile 62) geändert werden kann.
// change the name here!
bmpDraw("parrot.bmp", 0, 0);
Wenn der Testlauf erfolgreich verlaufen ist, können nun entsprechende Änderungen im obigen Quelltext der Rheinturmuhr erfolgen. Dies sind:
- Deklarationsteil: Zwei zusätzliche Zeilen
- Setup: Einbindung der SD-Karte mit Bitmap und entfernen der Turmgrafik
- Loop: Keinerlei Änderung erforderlich
- Kopieren: Die Bitmap-Laderoutinen unterhalb von Loop kopieren/übernehmen
Hier die Beschreibung der Änderungen im Detail:
Die zwei Zeilen im Deklarationsteil binden die SD-Karten-Bibliothek mit ein und sind für das Laden des Turmbildes erforderlich.
#include <Adafruit_GFX.h>
#include <SPI.h>
#include <SD.h>
#define TFT_CS 10
#define TFT_RST 8 // Vertauschen bei SainSmart
#define TFT_DC 9 // Vertauschen
#define SD_CS 4 // Chip select SD card
In Setup() erfolgen die meisten Änderungen, da die Turmgrafik nun als Bitmap von der SD-Karte dem Display zugeführt wird. Die Serial-Anweisungen dienen nur der Kontrolle und können bei reibungsloser Funktion auch entfallen. Hier das komplette neue Setup:
void setup(void)
{Serial.begin(9600);
tft.initR(INITR_BLACKTAB);
Serial.println("Rheinturmuhr auf 1.8 TFT mit SD-Card rt.bmp");
Serial.print("Initializing SD card...");
if (!SD.begin(SD_CS))
{Serial.println("failed!");
return;
}
Serial.println("OK!");
// change the name here!
bmpDraw("rt.bmp", 0, 0);
// wait 5 seconds
delay(5000);
tft.setTextSize(1);
tft.setTextColor(DIGITS,BACK_TXT);
t0=millis();
}
In der Hauptschleife loop() sind keinerlei Änderungen erforderlich, da die Lampen wie bei der Turmgrafik an der selben Stelle erscheinen. Sollten die Koordinaten nicht ganz passen, ist eine Feinkorrektur der Anfangskoordinaten x und y erforderlich. Auch die Texthintergrundfarbe könnte bei einem anderen Hintergrundbild stilvoller sein.
Die letzte und wichtigste Änderung ist das Einfügen der Bitmap-Laderoutine aus dem Beispiel 'spitftbitmap'. Alles was sich dort unterhalb der Routine loop() befindet muss komplett an die gleiche Stelle kopiert werden (bmpDraw, read16, read32). Auch dort werden Serial-Aufrufe zur Kontrolle benutzt! Bei Verzicht auf Rückmeldungen des Arduino ist dies zu berücksichtigen.
Nach diesen Änderungen und vorhandener Bilddatei 'rt.bmp' kann der Sketch gestartet werden. Zunächst baut sich das Bitmap-Bild auf dem Display auf. Dann erfolgt eine dramatische Pause von 5 Sekunden und voilà ... es ist die letzte Minute des Tages angebrochen.
Hier der Quelltext der SD-Card-Variante für die IDE ab 1.0.
|
Arduino Rheinturmuhr mit 39 LED
Arduino-Simulator und LED-Rheinturmuhr
Arduino Rheinturmuhr auf TFT-Display
Arduino Rheinturmuhr mit TFT/DCF77-Funk
Weitere Software
|