Nahezu gleiche zeitungenauigkeit auf zwei verschiedenen Arduinos

Also gut :stuck_out_tongue: - wenn du stur auf eine RTC verzichten willst, musst du deine Uhr eichen.

Dazu benötigst du eine externe Quelle, die über eine Echtzeituhr verfügt - wie etwa einen Computer.

Wenn du einen Button benutzt, um per knopfdruck eine Verbindung zwischen Computer und Arduino herzustellen, kannst du die (Arduino-)Systemzeit mit der Echtzeit abgleichen und hast deann den Kompensationsfaktor. Eigentlich ähnlich wie in deinem Processingsketch. Diese Eichung musst du allerdings mehrmals über einen bestimmten Zeitraum machen um eine Schnittmenge zu bekommen.

Diese Zahl würde ich anschließend im EEPROM des Arduinos ablegen um bei bedarf darauf zurückzugreifen.

ja, die idee hatte ich auch schon und werde das wohl auch so verfolgen... das mit der statistik bekomme ich schon hin, das physikstudium soll ja auch nicht ganz für umsonst gewesen sein :wink:

Wie MarcusW bereits deutlich ausgedrückt hat, wirst Du so nie zu einer sauberen Lösung kommen.

Selbst wenn Du durch softwaremässige Korrekturen eine ausreichende Genauigkeit erreichen solltest, sind diese spätestens bei späteren Programmerweiterungen bzw. Änderungen schnell wieder hinfällig, die Ungenauigkeit nimmt wieder zu.

Hardwaremässig würde der Atmega168/328 für solche Aufgaben den Anschluß eines zweiten Quarzes vorsehen, was vom Arduino aber nicht unterstützt wird. Deshalb ist auch die Lösung, einen Uhren- bzw. dcf77-Baustein zu verwenden, eigentlich unvermeidlich - selbst wenn man voraussetzen kann, daß die verwendete Timerbibliothek bereits optimal programmiert ist.

Glaub' uns - diese Erfahrung wirst Du früher oder später machen. :wink:

Gruß. mmi.

Vielleicht ein genaueres Quarz an einen Arduino-Pin hängen und den mit einem Interrupt versehen?

kduin:
Vielleicht ein genaueres Quarz an einen Arduino-Pin hängen und den mit einem Interrupt versehen?

es scheint ja nicht an der genauigkeit des quarz selbst zu hängen, aber ja, ein zweiter quarz wäre natürlich ein traum, hat jemand einen kurzen workaround wie sich das bewerkstelligen ließe?

Die hardwaremässige Erweiterung mit dem Quarz (und 2x C) ist relativ einfach, die Problematik liegt bei der Software, insbesondere der Nutzung eines der atmega-spezifischen Hardwaretimer, um das normale processing nicht mit übermässig vielen Interrupts auszubremsen. Dazu kommen aber evtl. Konflikte mit anderen Arduino libs, die diese timer benötigen, etc.

Das Fazit bleibt: Eine saubere und effiziente Lösung ist ein RTC Modul. :wink:

Auf eine RTC fuer einen Uhr-gebrauch zu verzichten, ist mir nicht verstaendlich. Die Kosten fuer eine DS3231 sind so niedrig und die DS3231 ist so genau... und vergleichsweise preiswerte Module wie die Chronodot, unter anderen, machen das Tuefteln einfach.

Aber fuer diese Anwendung ist es vielleicht noch wichtiger, dass die DS3231 einen programmierbaren SQW output hat, mit dem die Zeit-ablauefe zwischen zwei Arduinos genaustens geeicht werden koennen (per interrupt, z.B.). Ich benutzte meine DS3231 z.B. um zwei Arduinos fuer Strom-messungen zu koordinieren. Funktionierte perfekt.

Aber gut, jeder muss seine Zeit / Geld Entscheidungen selber machen...

ich halte es aber eigentlich für sinnlos eine zweite zeitmessvorrichtung zu integrieren, wenn ich eigentlich schon eine, in form des verbauten quarzes (mit für mich ausreichender genauigkeit) habe und das ganze einfach nur genau eine uhr darstellen soll.

Markus W hat mit seinen Anmerkungen schon recht. Wenn Du Pech hast liegst Du mal locker 100ppm nebendran. Weiterhin ist die Abweichung in der Regel auch nicht stabil sondern hängt auch von der Temperatur ab. RTCs sind in der Regel temperaturkompensiert.

Dann ist da noch das Problem, daß Du unter Umständen auch mal Interrupts sperrt und dann hauen millis() und andere eingebaute Funktionen immer nebendran. Die Erkenntnis ist:

Entweder man kennt sich ganz außerordentlich gut aus (dann muß man aber nicht mehr hier fragen), oder man nimmt einen RTC. Wenn man sich super auskennt und nicht gerade für eine Serie entwickelt, dann nimmt man auch einen RTC. Ohne RTC arbeitet man eigentlich nur dann wenn man so gut wie gar keine Genauigkeit braucht oder wenn man so hohe Stückzahlen produziert, daß sich der zusätzliche Entwicklungsaufwand lohnt.

Hallo,

kann mich meinen Vorrednern nur anschließen was auch noch zu erwähnen wäre . Bei Temperaturschwankungen wird es zusätzlich auch noch Abweichungen ohne Ende geben. Deshalb wäre noch zu erwähnen das die DS3231 zusätzlich über eine Temperatur Compensation verfügt.

big-maec:
Bei Temperaturschwankungen wird es zusätzlich auch noch Abweichungen ohne Ende geben. Deshalb wäre noch zu erwähnen das die DS3231 zusätzlich über eine Temperatur Compensation verfügt.

Sehr richtig, und auch einer der Gruende, warum ich mittlerweile nur die DS3231 auf meinen Platinen fuer Zeitaufgaben einsetze. Auch schoen ist, wie einfach die DS321 zu programmieren ist. Nur zwei Leitungen werden fuer den I2C bus gebraucht, dazu kommen noch zwei 2k2 Wiederstaende und ein 0.1uF Kondensator, und das Ding laeuft.

Nachtrag: oft braucht man nicht wirklich so hohe Genauigkeit wie man meint zu brauchen. Also immer erst nachdenken. Auf der anderen Seite will man oft hohe Genauigkeit nicht weil man sie braucht sondern weil man das einfach haben will.

Wenn sehr genau ausreicht, dann ist DS3231 eine sehr gute Wahl.

Wenn haben will der Punkt ist, dann ist meine Empfehlung "Trimble Thunderbolt" :wink: Die Dinger sind bei Ebay gebraucht sehr günstig zu bekommen. Wem das nicht reicht, der hat ein echtes Problem :wink:

also zum einen: genauigkeit und "haben will" ist so ne sache, wenn meine uhr in einem tag um mintuen gestellt werden muss dann ist für diesen anwendungsfall eindeutig zu ungenau ;). was die temperaturabweichungen angeht: man bekommt bei bekannten elektronikversandhäusern quarze, die mit +-30ppm ungenauigkeit +-30ppm thermische ungenauigkeit spezifiziert sind, diese liegen bei mir auch rum, was mich zum anderen führt: nachdem ich durch softwareseitige kompensation auf eine ungenauigkeit von unter 40ppm gekommen bin (das macht unter 4 sekunden pro tag) habe ich das ganze zu weiteren testzwecken komplett aufs breadboard übertragen, also mit batteriebetriebenem ATmega-chip, eigener quarz (16MHz +-30ppm +-30ppm thermisch) etc. und siehe da, es läuft in etwa um die softwareseitig kompensierte zeit zu schnell, sprich: es würde ohne diese exakt (im rahmen von noch zu bestimmenden ungenauigkeiten) laufen, sofern man von exaktheit reden kann, naja, immerhin um größenordnungen genauer als die "original"-arduinos

korrigiert mich wenn ich falsch liege, aber das lässt doch vermuten das einfach nur die quarz-frequenz meiner beiden arduinos einen gehörigen offset hat. ist eigentlich irgendwo dokumentiert, was für bauteile in da italien verwendet werden?

Nein ist nicht dokumentiert. Und die spezifizierten Toleranzen gelten nur wenn Du auch die passenden Kapazitäten verwendest. Da dort aber von Picofarad ausgegangen wird musst Du auch die Kapazitäten von Platinen / Breadboards / Pins mit berücksichtigen. Und ich würde wetten, daß Deine Softwarekompensation keine Temperaturkompensation beinhaltet. Früher oder später landen alle bei RTCs. Mal sehen wie lange Du dafür brauchst :wink:

also aus reinem eigeninteresse werde ich (vermutlich erst nächsten monat) mal die temperaturabweichung nachmessen, aber ich vertraue da der herstellerangabe erstmal... ansonsten halte ich es für... naja... unnötig genau eine temperaturkompensation (die sicher mit etwas messaufwand auch softwareseitig machbar wäre) für eine zimmeruhr anzustreben, die 99% ihrer zeit bei zimmertemperatur betrieben wird... für spätere projekte behalte ich das klar im hinterkopf, aber bei diesem projekt, was einfach nur eine uhr mit sich selbst als selbstzweck sein soll, halte ich es immernoch für unpassend... und da ich gerne etwas rumtüftele und die zeit nicht so arg drängt sind ein paar kompensationsrechnungen für mich nicht so das problem... auf dauer würde mich nur interessieren ob es nicht einfach wirklich an dem arduino-quarz liegt... bei gelegenheit werde ich den vielleicht mal auslöten und ersetzen

Hi,

na ist doch alles geklärt. Jetzt können auch richtige Anfänger die das hier lesen wenigstens abschätzen auf was sie sich einlassen wenn Sie ein Uhren Projekt realisieren.

So ganz kann ich Euer Problem nicht nachvollziehen.
Meine Uhr läuft nun seit einer Woche ohne Synchronisation vom Rechner und die Abweichung beträgt gerade mal 4 Sekunden.
Als Hardware komm ein Atmega8 mit 8MHz-Quarz auf dem Steckbrett zum Einsatz, es sind 20 Igittipfui-50ms-Delays, die obligatorischen Millis und noch etwas Spielkram für gedimmte Hintergrundbeleuchtung und Thermoneter in der Software und der Quarz ist ein ganz normaler (verm. 25ppm) ohne Abgleichkapazität.
Kann das nur Glück sein und mein Quarz ist gerade soviel zu schnell, wie meine "Sünden" Zeit und Interupts verschlucken, oder liegt das Problem der geschilderten Ungenauigkeiten doch woanders.
Entschuldigt meine dumme Frage, aber ist es sicher, daß wirklich der Quarz und nicht versehentlich der unkalibrierte Oszillator verwendet wird?
Für die, die es interessiert hier mein Sketch:

/* 
 * TimeSerialDateStrings.pde
 * example code illustrating Time library date strings
 *
 * This sketch adds date string functionality to TimeSerial.pde
 * 
 */
#include <Time.h>  
#include <LiquidCrystal.h>
const int led_heartbeat_Pin = 2;  // pin that the blink LED is attached to
const int lightSensorPin = A0;    // pin that the sensor is attached to (vcc>ldr>A0>4.7k>gnd)
const int tempSensorPin = A1;    // pin that the sensor is attached to (vcc>3,3k>tsens+>10k>A1>10k>gnd tempsens->gnd)
const int led_dim_Pin = 9;        // pin that the dimmed LED is attached to

boolean heartbeat = true;
int mode = 0;    // switch telling what to display

const int sample = 20;  // number of samples for temperatur integration
int sumRaw;      // sum of temperature samples
float sumAverage;  //  sum over 16 loops fetching 20 samples
float celsius;    // result in degree celsius
float fahrenheit;    // result in degree celsius
const float tempCorrection = 4;

int rawLight = 0;         // the lightsensor value
int lightValue = 0;         // remapped light value
const int lightMin = 50;        // minimum sensor value
const int lightMax = 850;           // maximum sensor value

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

#define TIME_MSG_LEN  11   // time sync to PC is HEADER followed by unix time_t as ten ascii digits
#define TIME_HEADER  'T'   // Header tag for serial time sync message
#define TIME_REQUEST  7    // ASCII bell character requests a time sync message 

void setup()  {
  pinMode(led_heartbeat_Pin, OUTPUT);  //  define as digital output
  pinMode(lightSensorPin, INPUT);    //  define as analog input
  pinMode(tempSensorPin, INPUT);    //  define as analog input
  analogReference(INTERNAL);    //  use the 2.56V internal referece at atmega8
  lcd.begin(8, 2);          //  DEM16101 16*1 display is organized as 8*2
  Serial.begin(9600);
  setSyncProvider( requestSync);  //set function to call when sync required
  Serial.println("Waiting for sync message");
}

void loop(){
  digitalWrite(led_heartbeat_Pin, heartbeat);
  heartbeat = !heartbeat;
  if (mode > 16){    //  reset modecounter if 10 sec time + 3 sec date + 3 sec temp  displayed
    mode = 0;
    sumAverage = 0;
  }
  mode += 1;    //  increment displaymode counter

  rawLight = analogRead(lightSensorPin);  // read the light sensor
  lightValue = map(rawLight, lightMin, lightMax, 255, 0);  // apply the calibration to the sensor reading
  lightValue = constrain(lightValue, 0, 255);  // in case the sensor value is outside the range seen during calibration

  analogWrite(led_dim_Pin, lightValue);  // fade the LED using the calibrated value:

  sumRaw = 0;    //  reset raw temperature sum
  for (int i = 0; i < sample; i++){    //  read 20 samples from temp in
    delay (50);     //  wait 50ms to give adc time and define heartbeat to 20*50ms
    sumRaw += analogRead(tempSensorPin);    //  summerize 20 readings
  }
  sumAverage += (sumRaw/sample);
  celsius = (((float(sumAverage) / mode)/2) - 273.15 + tempCorrection); // 
  fahrenheit = (( celsius * 9 ) / 5 ) + 32;

  if(Serial.available() ){
    processSyncMessage();
  }
  if(timeStatus()!= timeNotSet){
    digitalClockDisplay();  
    if (mode <= 10){
      timeLcdDisplay();
    }
    else if (mode <=13){
      dateLcdDisplay();
    }
    else if (mode <=16){
      tempLcdDisplay();
    }
  }
}

void timeLcdDisplay(){
  lcd.clear();
  lcd.setCursor(0,0);
  printLcdDigits(hour());
  lcd.print(":");
  printLcdDigits(minute());
  lcd.print(":");
  printLcdDigits(second());
  lcd.setCursor(0,1);
  lcd.print(" Uhr");
}

void dateLcdDisplay(){
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(dayShortStr(weekday()));
  lcd.print(" ");
  lcd.print(day());
  lcd.print(". ");
  lcd.setCursor(0,1);
  lcd.print(monthShortStr(month()));
  lcd.print(" ");
  lcd.print(year());
}

void tempLcdDisplay(){
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(celsius);
  lcd.print(" C");
  lcd.setCursor(0,1);
  lcd.print(fahrenheit);
  lcd.print(" F");
}

void printLcdDigits(int digits){
  // Utility function for digital clock display: prints leading 0
  if(digits < 10)
    lcd.print('0');
  lcd.print(digits);
}
void digitalClockDisplay(){
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(dayShortStr(weekday()));
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(monthShortStr(month()));
  Serial.print(" ");
  Serial.print(year()); 
  Serial.print(" ");
  Serial.print(celsius); 
  Serial.println(" C"); 
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void processSyncMessage() {
  // if time sync available from serial port, update time and return true
  while(Serial.available() >=  TIME_MSG_LEN ){  // time message consists of a header and ten ascii digits
    char c = Serial.read() ; 
    Serial.print(c);  
    if( c == TIME_HEADER ) {       
      time_t pctime = 0;
      for(int i=0; i < TIME_MSG_LEN -1; i++){   
        c = Serial.read();          
        if( c >= '0' && c <= '9'){   
          pctime = (10 * pctime) + (c - '0') ; // convert digits to a number    
        }
      }   
      setTime(pctime);   // Sync Arduino clock to the time received on the serial port
    }  
  }
}

time_t requestSync()
{
  //  Serial.print(TIME_REQUEST,BYTE);  
  Serial.write(TIME_REQUEST);
  return 0; // the time will be sent later in response to serial mesg
}

Bleiben verschwenderische 2 Byte(!) in einem Atmega8 ohne Bootloader frei ]:smiley:

Also es lohnt sich offensichtlich durchaus auch mal Versuche ohne RTC-Chip zu wagen, obgleich die Dinger wirklich klasse sind - keine Frage.

LG Lena

Lena, ich stimme dir voll und ganz zu, wie du sicher bei mir gelesen hast komme ich auch auf absolut normale abweichungen (innerhalb der vom hersteller angegeben maßstäbe), wenn ich einen ATmega328 auf einem steckbrett mit 16MHz quarz betreibe :wink: meine messungen weisen lediglich daraufhin das die in den käuflich erwerbbaren arduinos verbauten quarze ziemlich unpräzise sind.

Können die wirklich so grottenschlecht sein, so daß die von Dir beobachteten Abweichungen auftreten?
Das wäre ja normalerweise nur noch als Ausschuß zu betrachten!
Die Dinger werden ja von jedem Keramikresonator mit "Hallo LC-Kreis" begrüßt :smiley:
Na dann wühlt mal schön in Euren Grabbelkisten oder schaut bei Eurem Händler des Vertraues vorbei und holt Euch einen halbwegs brauchbaren Quarz, den so wird das ja auch irgendwann mal für den Baudrategenerator der UART eng.

LG Lena

da ich eigentlich ohnehin nie vor hatte die 20 euro platine irgendwo zu verbauen ist mir das eigentlich ziemlich egal, was am ende fertig irgendwo landet besteht dann eh aus nem atmega328 und selbstgekauftem quarz auf steckbrettern oder rasterplatinen...