Projektvorstellung UHR3 - die dritte Version meiner TEXTUHR auf Arduino-Basis
|
Ich zähle mich nach wie
vor zu den handwerklich eher Minderbegabten. Aber seit ich mir eine CNC-Fräse
zugelegt habe (eigentlich nur zum Platinen-Fräsen), ersetzt die Maschine einiges
an fehlendem Geschick :-) |
Elektronik und Aufbau:
Das Herz der Uhr ist ein ATMega328, der neben Spannungsregler und sonstiger Minimalbeschaltung (Quarz/Kondensatoren/Widerstände) auf einer
selbstgefrästen Platine werkelt. Die Platine hat ansonsten nur noch jede Menge Pfostenstecker, über die die gesamte Peripherie
angeschlossen ist:
Der Sketch (das "Programm"):
#include <Adafruit_NeoPixel.h> // Library für Stripe-Ansteuerung einbinden #include <Time.h> // Library mit Zeit-Funktionen einbinden #include \"DCF77.h\" // Library für DCF77-Modul einbinden #include \"Wire.h\" // Library für I2C (RTC) einbinden #include \"UC121902-TNARX-A.h\" // Library fürs Display einbinden UC121902_TNARX_A::Display display (6,7,8); // Display einstellen an Pins 6,7 und 8 #define PIXELPIN A0 // Datenpin für Stripe: A0 #define NUMPIXELS 37 // Stripe hat 37 LEDs/Pixels Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIXELPIN, NEO_GRB + NEO_KHZ800); //Stripe-Daten einstllen #define DS3231_ADDRESS 0x68 // I2C Adresse für RTC #define DCF_PIN 2 // Pin für DCF77-Modul festlegen #define DCF_INTERRUPT 0 // Interrupt für DCF77 festlegen #define hsensor A1 // Pin für Helligkeitssensor festlegen const byte zero = 0x00; // Hilfsvariable für I2C-Kommunikation time_t DCFtime; // Variable für die DCF-Zeit DCF77 DCF = DCF77(DCF_PIN,DCF_INTERRUPT); // DCF77 einstellen tmElements_t tm; // zum Berechnen des Adventskalenders const int advPin[]={0,9,10,11,12}; // Pins für Adventskerzen unsigned long lastdcf=0; // Variable zum Speichern, wann letzter DCF-Sync war unsigned long lastrtc=0; // Variable zum Speichern, wann letzter RTC-Sync war byte lastsecond; // Variable zum Speichern des letzten Sekundenwertes byte lastminute; // Variable zum Speichern des letzten Minutenwertes byte lastday; // Variable zum Speichern des letzten Tageswertes String disp; // String-Variable für Displayinhalt byte dispcount; // Zähler zum Wechsel des Displaywertes int hell; // Helligkeitswert aus dem Sensor int ledhell=255; // Helligkeit der LEDs int lasthell; // Variable zum Speichern der letzten LED-Helligkeit int mehrrot; // Variable für höheren Rot-Wert (abends) int mehrblau; // Variable für höheren Blau-Wert (morgens) void setup() { // Initalisierungen (wird nur am Anfang 1x ausgeführt) DCF.Start(); // Initalisieren von DCF Wire.begin(); // Initalisieren der RTC-Kommunikation display.begin(); // Initalisieren des Displays pixels.begin(); // Initalisieren des Stripes delay(30); // kurze Pause pinMode(hsensor, INPUT); // Pin für Helligkeitssensor als Eingang setzen for (int jj=1;jj<5;jj++){ pinMode(advPin[jj],OUTPUT); // Adventskerzen-Pins als Ausgang definieren } ledlauf(); // alle LEDs durchlaufen lassen } void loop() { // Hauptprogramm (läuft endlos) if (now()-lastdcf > 90 || lastdcf==0) dcfsync(); // DCFsync starten, wenn noch nicht erfolgt nach 90 Sek if (now()-lastrtc > 360 || lastrtc==0) rtcget(); // RTC-Zeit holen, wenn nicht erfolgt oder nach 6 Minuten if(second()!=lastsecond){ // wenn sich Sekundenwert geändert hat (= 1 Sek vorbei)... hell=analogRead(hsensor); // Helligkeitswert einlesen ledhell=20; // Helligkeit erst auf Minimum setzen und dann... if (hell>100) ledhell=30; // if (hell>300) ledhell=60; // if (hell>500) ledhell=80; // ...je heller desto höher if (hell>800) ledhell=120; // if (hell>900) ledhell=220; // if (ledhell!=lasthell){ // falls sich die Helligkeit geändert hat... zeitzuled(hour(),minute()); // Zeit auf den Stripe ausgeben (mit neuer Helligkeit) und lasthell=ledhell; // Helligkeit für nächsten Vergleich merken } zeitdisplay(); // Displayausgabe lastsecond=second(); // Sekundenwert für nächsten Vergleich merken } if(minute() != lastminute){ // wenn sich Minutenwert geändert hat (= 1 Min vorbei)... zeitzuled(hour(),minute()); // Zeit auf den Stripe ausgeben lastminute=minute(); // Minutenwert für nächsten Vergleich merken } if(day() != lastday){ // wenn sich Tageswert geändert hat (= 1 Tag vorbei)... advent(); // Adventsberechnung (und ggf -ausgabe) starten lastday=day(); // Tageswert für nächsten Vergleich merken } } void zeitdisplay(){ // *** Funktion Displayausgabe disp=\" \"; // Stringvariable für Displayinhalt leeren if (dispcount<6){ // wenn wengier als 6 Sek vergangen... disp += \" \"; // Leerzeichen hinzufügen if (hour()<10) disp += \"0\"; // falls Stunde einstellig, Null einfügen disp += hour(); // Stunde einfügen disp += \" \"; // Leerzeichen einfügen if (minute()<10) disp += \"0\"; // falls Miunte einstellig, Null einfügen disp += minute(); // Minute einfügen disp += \" \"; // Leerzeichen einfügen if (second()<10) disp += \"0\"; // falls Sekunde einstellig, Null einfügen disp += second(); // Sekunde einfügen } else{ // ... sonst (6 oder mehr Sek vergangen) if (day()<10) disp += \"0\"; // falls Tag einstellig, Null einfügen disp += day(); // Tag einfügen disp += \" \"; // Leerzeichen einfügen if (month()<10) disp += \"0\"; // falls Monat einstellig, Null einfügen disp += month(); // Monat einfügen disp += \" \"; // Leerzeichen einfügen disp += year(); // Jahr einfügen (ist vierstellig) } display.print(disp); // generierten String auf Display schreiben if (now()-lastdcf > 180 || lastdcf==0) { // wenn seit >180 Sekunden kein DCF-Singal... display.prog.toggle(); // ... PROG-Feld des Displays blinken lassen } else{ // ... sonst ... display.prog.turnOff(); // ... PROG-Feld des Displays aus } dispcount++; // Zähler um eins erhöhen if (dispcount==10) dispcount=0; // bei 10 auf Null setzen (nach 10 Sekunden) } void dcfsync(){ // *** Funktion DCF-Sync DCFtime = DCF.getTime(); // Zeitinfo von DCF77 holen if (DCFtime!=0) // wenn Zeitinfo vorliegt... { setTime(DCFtime); // Zeit nach DCF-Zeit setzen lastdcf=now(); // merken, wann letzter Sync war Wire.beginTransmission(DS3231_ADDRESS); // Verbindung zu RTC-Modul herstellen Wire.write(zero); // RTC stoppen Wire.write(decToBcd(second(DCFtime))); Wire.write(decToBcd(minute(DCFtime))); Wire.write(decToBcd(hour(DCFtime))); // RTC nach DCF-Zeit stellen Wire.write(decToBcd(weekday(DCFtime))); Wire.write(decToBcd(day(DCFtime))); Wire.write(decToBcd(month(DCFtime))); Wire.write(decToBcd(year(DCFtime)-2000)); Wire.endTransmission(); // RTC wieder starten } } void rtcget(){ // *** Funktion RTC-Sync Wire.beginTransmission(DS3231_ADDRESS); // Verbindung zu RTC-Modul herstellen Wire.write(zero); Wire.endTransmission(); Wire.requestFrom(DS3231_ADDRESS, 7); int second = bcdToDec(Wire.read()); int minute = bcdToDec(Wire.read()); int hour = bcdToDec(Wire.read() & 0b111111); // Zeitinfo von RTC holen int weekDay = bcdToDec(Wire.read()); int monthDay = bcdToDec(Wire.read()); int month = bcdToDec(Wire.read()); int year = bcdToDec(Wire.read()); setTime(hour,minute,second,monthDay,month,year); // Zeit nach RTC-Zeit setzen lastrtc=now(); // merken, wann letzter Sync war } void zeitzuled(int h, int m) // *** Funktion Stripeausgabe { /* LEDs und Anzeigen 35->fünf 33->zehn 31->viertel 29->vor 27->nach 25->halb 23->1 21->2 19->3 17->4 15->5 13->6 11->7 9->8 7->9 5->10 3->11 1->12 */ pxoff(); // Funktion aufrufen, das alle Pixel auf AUS setzt mehrblau=0; // Variable für mehr blau auf Null mehrrot=0; // Variable für mehr rot auf Null if (h<7) mehrblau=20; // Wenn Stunde kleiner als 7, Blauwert um 20 erhöhen if (h>18) mehrrot=20; // Wenn Stunde größer als 18, Rotwert um 20 erhöhen if (m>17) h+=1; // ab Minute 18 -> 1 Std größer anzeigen (13:20 -> zehn vor halb ZWEI) if (h>12) h-=12; // 12h-Format, also nach 12 Uhr 12 Std abziehen if (h==0) h=12; // 0 uhr = 12 pxon(25-h*2); // Stunde anzeigen (z.B. Std 6 -> Pixel 13) if (m>2 && m<8) { pxon(35); pxon(27); } // fünf nach if (m>7 && m<13) { pxon(33); pxon(27); } // zehn nach if (m>12 && m<18) { pxon(31); pxon(27); } // viertel nach if (m>17 && m<23) { pxon(33); pxon(29); pxon(25); } // zehn vor halb if (m>22 && m<28) { pxon(35); pxon(29); pxon(25); } // fünf vor halb if (m>27 && m<33) { pxon(25); } // halb if (m>32 && m<38) { pxon(35); pxon(27); pxon(25); } // fünf nach halb if (m>37 && m<43) { pxon(33); pxon(27); pxon(25); } // zehn nach halb if (m>42 && m<48) { pxon(31); pxon(29); } // viertel vor if (m>47 && m<53) { pxon(33); pxon(29); } // zehn vor if (m>52 && m<58) { pxon(35); pxon(29); } // fünf vor pixels.show(); // alle Pixel gesetzt, Daten auf Stripe schreiben } // *** Hilfsroutinen für die Umrechnung der Werte für die RTC: byte bcdToDec(byte val) { return ( (val/16*10) + (val%16) ); // Binär codierte Dezimalzahl in normale umwandeln } byte decToBcd(byte val){ return ( (val/10*16) + (val%10) ); // Normal codierte Dezimalzahl in binäre umwandeln } void pxon(int px){ // *** Funktion für Pixelansteuerung pixels.setPixelColor(px,pixels.Color(ledhell+mehrrot,ledhell,ledhell+mehrblau)); // Farben (RGB) des Pixel px setzen (Helligkeit und ggf mehr rot/blau) pixels.setPixelColor(px+1,pixels.Color(ledhell+mehrrot,ledhell,ledhell+mehrblau)); // dto für px + 1 (jedes Feld besteht aus 2 Pixel) } void pxoff(){ // *** Funktion für alle Pixel aus for (int i=0;i<NUMPIXELS;i++){ // für jedes Pixel von 0 bis NUMPIXELS (Anzahl der Pixel)... pixels.setPixelColor(i,pixels.Color(0,0,0)); // ... setze alle 3 Farben (RGB) auf Null } } void advent() { tm.Second = tm.Hour = tm.Minute = 0; // Datum erzeugen: 00:00:00 Uhr am... tm.Day = 24; // 24. tm.Month = 12; // 12. tm.Year = year()-1970; // ...des aktuellen Jahres (minus 1970, weil Timestamp) time_t xmas=makeTime(tm); // Timestamp aus Weihnachts-Datum erzeugen time_t adv1=xmas-86400*weekday(xmas)-1728000; // 1. Advent berechnen time_t adv2=adv1+604800; // 2. Advent = 1. Adv + 7 Tage (=604800 Sek) time_t adv3=adv2+604800; // 3. Advent time_t adv4=adv3+604800; // 4. Advent for (int jj=1;jj<5;jj++){ digitalWrite(advPin[jj],0); // alle Kerzen aus } if (now()<=xmas){ // wenn aktueller Timestamp vor Weihnachten... if(now()>=adv1) digitalWrite(advPin[1], 1); // wenn nach 1. Advent -> Kerze 1 an if(now()>=adv2) digitalWrite(advPin[2], 1); // dto 2 if(now()>=adv3) digitalWrite(advPin[3], 1); // dto 3 if(now()>=adv4) digitalWrite(advPin[4], 1); // dto 4 } } void ledlauf(){ // *** Funktion, um alle LEDs durchlaufen zu lassen (Effekt beim Start) for (int ii=NUMPIXELS;ii>0;ii--){ // für jedes Pixel von NUMPIXELS (Anzahl der Pixel) bis absteigend... pxoff(); // alle Pixel aus pxon(ii); // Pixel ii ein pixels.show(); // anzeigen delay(100); // 0,1 Sekunden warten } } |
Ausblick / ToDo / Dankesworte:
Falls jemand Vorschläge hat oder
Fehler findet, darf er/sie sich gerne melden!
An dieser Stelle herzlichen Dank für die vielen Rückmeldungen auf die erste Version. Es war immer schön, mit anderen Uhrenbastlern aus ganz Europa zu fachsimpeln und Erfahrungen
auszutauschen!
Ein besonderer Gruß geht an phi, Frank und Matthias! ;)
Alle in erwähnten
Marken- und Warenzeichen oder Produktnamen sind Eigentum ihrer jeweiligen Inhaber.
Für die Inhalte externer Links sind die jeweiligen Seitenbetreiber verantwortlich.
Keine Haftung für eventuelle Schäden.