2014
HOME
Arduino Rheinturmuhr mit 39 LED
Arduino-Simulator und LED-Rheinturmuhr
Arduino Rheinturmuhr auf TFT-Display
Arduino Rheinturmuhr mit TFT/DCF77-Funk

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
.
Startseite Bücher Software Digital RTV Musik Kontakt

Für Inhalt und weitere Verzweigung externer Links sind die Betreiber der dortigen Seiten verantwortlich - H.-J. Berndt