ich habe mir mal aus Spaß einen Ring mit WS2812B bestellt und mit einem Nono-Clone eine Uhr daraus gebastelt.
Dazu habe ich diesen Sketch verwendet und etwas modifiziert:
dieser verwendet diese Library für die Uhr:
Als RTC verwende ich ein Modul mit DS3231.
Wenn die Uhr frei läuft, geht sie mehrere Sekunden pro Minute nach. Wenn ich die interne Uhr mit der RTC synchronisiere, stimmt die Zeit wieder. Da aber die interne Uhr nur einmal in der Stunde mit der RTC synchronisiert wird, macht sich das natürlich auf der Anzeige durch einen Sprung bemerkbar.
Als Workaround könnte ich nun die Zeit jede Minute mit der RTC synchronisieren, was aber irgendwie auch nicht besonders befriedigend ist und am Ende jeder Minute trotzdem einen Sprung des Sekundenzeigers hervorruft.
Um das Problem zu beheben habe ich mal in der Library nach der Stelle gesucht, wo die Zeit überhaupt gebildet wird. Diese muss ja irgendwo von der Hardware (Quarz/ interner Timer) abhängen.
Versuchsweise habe ich das Programm mal auf einen Pro Micro geschoben und es mit diesem versucht, aber das brachte keine Besserung.
Leider konnte ich die Frequenz der Quarze nicht messen, weil der Tastkopf vom Oszilloskop beide zu sehr belastet und die Controller dann einfach anhalten.
Gibt es irgendwo eine Einstellung versteckt, mit der man die interne Uhr des Arduino anpassen kann? ich vermute mal, die verwendet einen der Hardwaretimer.
Wenn nicht, werde ich wohl tatsächlich alle 20 Sekunden die interne Uhr mit der RTC synchronisieren müssen.
Das könnten tatsächlich Resonatoren sein, bei der Baugröße, zumal weder beim Nanoclone noch beim ProMicro Kondensatoren vorhanden sind. Vielleicht wäre es noch eine Möglichkeit, einen echten 16MhZ Quarz mit 22pf Kondensatoren aufzulöten. Aber dazu würde ich dann tatsächlich einen nackten ATMega328 verwenden.
Der Uhrenquarz ist auf dem RTC-Modul schon drauf und der DS3231 hat auch noch einen 32KhZ-Ausgang und den Squarewave-Ausgang, den man auf 1Hz programmieren kann. Beides würde als Grundlage für eine Uhr sicher besser sein, als der Resonator.
Am naheliegendsten wäre es für mich, den 1Hz-Ausgang mit dem Arduino zu verbinden, den in einem Interrupt abfragen und daraus die Zeit ableiten.
Wenn ich mal eine wirklich genaue Uhr benötige, werde ich das mal versuchen. Aber für dieses Projekt ist mir das ehrlich gesagt zu aufwändig. Da benutze ich dann wohl doch den Workaround und stelle die interne Uhr alle 20 Sekunden oder so aus der RTC nach.
Meine Hoffnung war eben, daß es irgendwo eine Möglichkeit gibt, die interne Uhr zu trimmen.
Dazu müsste ich erst mal wieder meinen AVRISP rauskramen und mich wieder mit den Fuses befassen.
weil ich einen fertigen Sketch verwendet habe, der eine interne Uhr verwendet. Daß diese nicht ganz so genau ist, wie die RTC war mir klar, aber daß sie so ungenau ist (4-6 Sekunden pro Minute) hätte ich nicht gedacht.
Es sollte nicht schaden, diese sekündlich abzufragen.
Ungetestet: Stattdessen könntest Du die Berechnung kalibrieren. In \libraries\Time\Time.cpp mal mit dem Wert 1000 spielen:
time_t now() {
// calculate number of seconds passed since last call to now()
while (millis() - prevMillis >= 1000) {
// millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference
sysTime++;
prevMillis += 1000;
Das setzt natürlich voraus, Dein Programm enthält keinen Zeitfresser. Kommentiere beispielsweise mal strip.show(); aus, ob dadurch millis() beeinflußt wird, schließlich werden ja bei der Datenausgabe die Interrupts gesperrt.
Es war halt meine Hoffnung, daß es irgendwo die Möglichkeit gibt, die interne uhr einfach zu trimmen.
Das werde ich mal versuchen. Das ist genau das, was ich gesucht habe.
Die sekündliche Ausgabe auf dem seriellen Monitor hab ich schon angepasst, so daß nur noch jede Minute die Zeit ausgegeben wird, aber an strip.Show hatte ich noch gar nicht gedacht. Wenn es daran liegen sollte, ist es zwar gut, den Grund zu wissen, aber für diese Anwendung natürlich kontroproduktiv.
Sollte es an strip.show(); liegen, kommt es auf Deine Animation an, wie stark das stört. Sekündlich die Zeit anzuzeigen ist weniger kritisch, als ein Faden.
Bei meiner LED-Uhr habe ich es ja auch hinbekommen
Wieso bastelst Du am Arduino herum? Auch wenn Du einen Quarz statt des Resonators einbaust oder ein Modell mit Quarz ( zB Leonardo oder Micro) verwendest ist der Ds3231 immer genauer weil dort eine Temperaturkorrektur vorgenommen wird.
Warum nmmst Du dieses Beispiel? Nimm ein Beispiel das mit der DS3231 Biblothek geliefert wird und hol dir die Zeit alle Sekunde oder besser über den 1Hz Ausgang der DS3231 .
Nur ne Meinung, nichts, was Dich wirklich weiterbringt.
Ich hab kurz in den Code geschaut, der bringt sicher einiges schönes mit, aber da werden auch ständig Aktualisierungen gefahren, ohne Not.
So könnte geprüft werden ob sich die Sekunde geändert hat und erst dann drawclock() aufgerufen werden. Dann gibts da nen delay(10) mittendrin.
Zudem ist mir vollkommen unverständlich, warum jemand die Timelib von Paul verwendet, die TZ von JChristensen, aber die RTC von noch jemand anderem, wo doch sowohl die 1307 als auch die 3232 bei Paul vorhanden sind.
Du solltest Dir überlegen ob Du nicht erstmal Codeverbesserungen vornimmst, bevor Du einen - zu welchem Zweck auch immer - Korrekturfaktor empirisch ermittelst....
Es liegt wohl wirklich an strip.show. ich habe es auskommentiert und seit dem geht die Uhr richtig.
Eine Animation gibt es nicht. Es werden lediglich die Stundenmarkierungen und die Zeiger angezeigt.
Das mache ich ja nicht. Es war nur die Überlegung, den Fehler der internen Uhr zu verringern. Da der aber nicht Hardwarebedingt ist, erübrigt sich diese Überlegung.
Dieses Beispiel war halt das erste, welches ich gefunden habe. Und da ich einfach nur mal eben eine Uhr bauen wollte, habe ich das eben übernommen und angepasst.
Das delay habe ich schon rausgeworfen. Das hat mich schon ganz am Anfang gestört.
Insgesamt habe ich den Ablauf des programms jetzt etwas angepasst und aktualisiere alle 500ms die Uhrzeit aus der RTC.
Die LEDs werden nur noch jede Sekunde aktualisiert.
/**
* NeoClock
*
* Clock using 60 WS2812B/Neopixel LEDs and DS3231 RTC
*
* Libraries needed:
* * Adafruit NeoPixel (Library Manager) - Phil Burgess / Paint Your Dragon for Adafruit Industries - LGPL3
* * Rtc by Makuna (Library Manager) - Michael C. Miller
* * Arduino Timezone Library (https://github.com/JChristensen/Timezone) - Jack Christensen - CC-BY-SA
* * Time Library (https://github.com/PaulStoffregen/Time) - Paul Stoffregen, Michael Margolis - LGPL2.1
*/
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#if defined(ESP8266)
#include <pgmspace.h>
#else
#include <avr/pgmspace.h>
#endif
#include <SoftwareWire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3231.h>
#include <Time.h> //http://www.arduino.cc/playground/Code/Time
#include <Timezone.h> //https://github.com/JChristensen/Timezone
#include <EEPROM.h>
//Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; //Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; //Central European Standard Time
Timezone CE(CEST, CET);
TimeChangeRule *tcr; //pointer to the time change rule, use to get the TZ abbrev
time_t utc;
SoftwareWire myWire(8, 9);
RtcDS3231<SoftwareWire> Rtc(myWire);
#define PIN 6
unsigned long lastMillis = millis();
unsigned long prevMillis_1000;
unsigned long prevMillis_500;
unsigned long prevMillis_10;
unsigned long actMillis;
byte dimmer = 136;
byte hmark = 0;
byte tick = 0;
byte over = 0;
byte rounds = 60;
byte ohour=0;
byte ominute=0;
byte osecond=0;
boolean fader=true;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
pinMode(10, OUTPUT);
Serial.begin(115200);
strip.begin();
strip.show(); // Initialize all pixels to 'off'
Rtc.Begin();
Rtc.Enable32kHzPin(false);
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone);
if (!Rtc.GetIsRunning())
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
if (!Rtc.IsDateTimeValid())
{
// Common Cuases:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
byte eechk = EEPROM.read(0);
if(eechk == 0xAA) { //Assume this is our config and not a fresh chip
dimmer = EEPROM.read(5);
hmark = EEPROM.read(2);
fader = EEPROM.read(3);
tick = EEPROM.read(4);
over = EEPROM.read(6);
}
timeSync();
}
void calcTime(void) {
utc = now();
CE.toLocal(utc, &tcr);
ohour = hour(utc);
ominute = minute(utc);
if(osecond != second(utc)) {
osecond = second(utc);
lastMillis = millis();
}
}
void addPixelColor(byte pixel, byte color, byte brightness) {
// color *= 8;
color = color * 8;
uint32_t acolor = brightness;
// acolor <<= color;
acolor = acolor << color;
uint32_t ocolor = strip.getPixelColor(pixel);
// ocolor |= acolor;
ocolor = ocolor | acolor;
if (over > 0 ) {
strip.setPixelColor(pixel, ocolor);
}
else {
strip.setPixelColor(pixel, acolor);
}
}
void drawClock(byte h, byte m, byte s) {
strip.clear();
// addPixelColor(m, 1, dimmer);
// ----- Stundenmarkierungen setzen
if(hmark > 0) {
for(byte i = 0; i<12; i++) {
addPixelColor((5*i), 2, hmark);
}
}
// ----- Stundenzeiger setzen
h %= 12;
h *= 5;
h += (m/12);
addPixelColor(h, 2, dimmer);
// 0x RR GG BB
// ----- Minutenzeiger setzen
addPixelColor(m, 1, dimmer);
// ----- Sekundenzeiger setzen
if(fader) {
byte dim_s1 = dimmer;
byte dim_s2 = 0;
byte px_s2 = s+1;
if(px_s2 >= 60) px_s2 = 0;
unsigned long curMillis = millis()-lastMillis;
if(curMillis < 250) {
dim_s2 = 0;
dim_s1 = dimmer;
}else{
dim_s2 = map(curMillis, 250, 1000, 0, dimmer);
dim_s1 = dimmer - map(curMillis, 250, 1000, 0, dimmer);
}
addPixelColor(s, 0, dim_s1);
addPixelColor(px_s2, 0, dim_s2);
}else{
addPixelColor(s, 0, dimmer);
}
strip.show();
}
void loop() { // -----------------------------------------------------------------------
actMillis = millis();
// ---- jede 500ms
if (actMillis >= (prevMillis_500 + 500)) {
prevMillis_500 = actMillis;
RtcDateTime dt = Rtc.GetDateTime();
setTime(dt.Hour(),dt.Minute(),dt.Second(),dt.Day(),dt.Month(),dt.Year());
calcTime(); // Zeit neu berechnen
}
// ---- jede Sekunde
if (actMillis >= (prevMillis_1000 + 1000)) {
prevMillis_1000 = actMillis;
drawClock(ohour,ominute,osecond); // Zeit auf LEDs anzeigen
Serial.print(ohour); // Zeit auf serieller Schnittstelle ausgeben
Serial.print(":");
Serial.print(ominute);
Serial.print(":");
Serial.println(osecond);
if (tick == 1) {
tone (10, 1000, 5);
}
}
// ---- jede 10ms
if (actMillis >= (prevMillis_10 + 10)) {
prevMillis_10 = actMillis;
chkSer(); // Serielle Schnittstelle prüfen
}
} // ----------------------------------------------------------------------------
void timeSync(void) {
RtcDateTime dt = Rtc.GetDateTime();
setTime(dt.Hour(),dt.Minute(),dt.Second(),dt.Day(),dt.Month(),dt.Year());
Serial.print("Synced to: ");
Serial.print(dt.Year());
Serial.print("-");
Serial.print(dt.Month());
Serial.print("-");
Serial.print(dt.Day());
Serial.print("-");
Serial.print(dt.Hour());
Serial.print("-");
Serial.print(dt.Minute());
Serial.print("-");
Serial.println(dt.Second());
}
void timeSave(void) {
utc = now();
RtcDateTime store = RtcDateTime(year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc));
Rtc.SetDateTime(store);
Serial.print("Synced to: ");
Serial.print(year(utc));
Serial.print("-");
Serial.print(month(utc));
Serial.print("-");
Serial.print(day(utc));
Serial.print("-");
Serial.print(hour(utc));
Serial.print("-");
Serial.print(minute(utc));
Serial.print("-");
Serial.println(second(utc));
}
void setBrightness(byte brightness) {
dimmer = brightness;
}
void chkSer(void) {
unsigned int iy;
byte im,id,iH,iM,iS;
if(!Serial.available()) return;
switch(Serial.read()) {
case 'b': // Helligkeit der Zeiger einstellen
setBrightness(Serial.parseInt());
Serial.print(F("Brightness changed to: "));
Serial.println(dimmer);
EEPROM.put(0, 0xAA);
EEPROM.put(5, dimmer);
break;
case 't': // Uhrzeit einstellen
iy = Serial.parseInt();
im = Serial.parseInt();
id = Serial.parseInt();
iH = Serial.parseInt();
iM = Serial.parseInt();
iS = Serial.parseInt();
setTime(iH,iM,iS,id,im,iy);
Serial.println(F("System time changed"));
break;
case 'f': // Fader für Sekundenzeiger aus
fader = false;
EEPROM.put(0, 0xAA);
EEPROM.put(3, 0);
Serial.println(F("Fader off"));
break;
case 'F': // Fader für Sekundenzeiger ein
fader = true;
EEPROM.put(0, 0xAA);
EEPROM.put(3, 1);
Serial.println(F("Fader on"));
break;
case 'm': // Helligkeit der Stundenmarkierungen setzen. 0 = aus
hmark = Serial.parseInt();
EEPROM.put(0, 0xAA);
EEPROM.put(2, hmark);
Serial.println(F("HMark changed"));
break;
case 's': // Uhrzeit von RTC in Uhr
timeSync();
Serial.println(F("Synced RTC to System"));
break;
case 'S': // Uhrzeit von Uhr in RTC
timeSave();
Serial.println(F("Synced System to RTC"));
break;
case 'c': // Sekundentick
EEPROM.put(0, 0xAA);
EEPROM.put(4, 0);
Serial.println(F("Tick aus"));
tick = 0;
break;
case 'C':
EEPROM.put(0, 0xAA);
EEPROM.put(4, 1);
Serial.println(F("Tick ein"));
tick = 1;
break;
case 'o': // Überblenden
EEPROM.put(0, 0xAA);
EEPROM.put(6, 0);
over = 0;
Serial.println(F("ueberblenden aus"));
break;
case 'O':
EEPROM.put(0, 0xAA);
EEPROM.put(6, 1);
over = 1;
Serial.println(F("ueberblenden ein"));
break;
default:
Serial.println(F("???"));
}
}
@ TO:
mein Senf. Ich muss mich bei sowas immer sehr sehr sehr ... stark wundern. Da hat man schon eine genaue RTC am Controller hängen und dann nutzt man die nicht exklusiv für die Uhrzeit. Was soll das? Dein ganzes Software Uhr Gerassel kannste komplett vergessen. Lies die RTC pro Sekunde aus und fertig die Laube. Wenn es keine Fälschung ist kannste den DS3231 sogar per Aging Register noch genauer abgleichen wie der ohnehin schon ist.
Wie weiter oben schon geschrieben, ist der Code nicht von mir. Ich habe ihn so wie er war verwendet und nur einige kleinere Anpassungen vorgenommen, ohne lange darüber nachzudenken, bis ich die Abweichungen bemerkt habe.
Ich wollte halt eben nur mal schnell eine Uhr basteln.
Das hat bis jetzt einen ziemlichen Rattenschwanz hinter sich her gezogen. Im Endeffekt wäre es tatsächlich schneller gewesen, das Ganze komplett selber zu schreiben. Aber so habe ich wieder was gelernt und weiß jetzt auch, wie man die WS2812 ansteuert.
gut, dann haste jetzt mitbekommen das nicht alles was im Internet steht richtig ist. Von hinten durchs Auge und unten wieder rein kann eben nicht gut gehen.
Du benötigst rein die Daten von der RTC. Nicht mehr und nicht weniger. Erstell dir ein struct mir allen benötigen Uhrzeit und Datum Variablen. Diese aktualisierst du aller Sekunde oder halben Sekunde und dann arbeitet der komplette restliche Sketch nur noch mit den Variablen (Member) vom struct die ja ständig aktuell gehalten werden. Keine Software Uhr nichts. Kein 1Hz oder 32kHz Ausgang der RTC nur Daten von der RTC über I2C.
sorry, nicht nachgesehen.
Ein extra Quarz ist auf dem Modul nicht mit drauf. Da bin ich von den DS1307-Modulen ausgegangen, die ich hier auch noch herumliegen habe.
Auf dem Steckbrett liegt das Modul mit der Bestückungsseite nach unten, da hab ich nicht gesehen, daß gar keiner drauf ist und bin davon ausgegangen, es hätte auch einen.