Ungenauigkeit zwischen RTC und millis()

Hallo,
habe Problem mit millis(), in einer " sekunde " fehlen mir zwei Einheiten.

will ein stromzehler bauen mit Arduino im Garten. Angeschlossen sind RTC ,DHT und zwei LCD über I2C.
Sehr warschanlich durch die interrupts dauern die millis() 1000 länger als sekunde

Was soll uns das jetzt sagen? Dass wir uns die Ursache erwürfeln sollen?
Wie meinst Du, wie wir Dir helfen können, ohne Deinen Sketch zu kennen.
Setze Deinen Code bitte in Codetags. (</>-Button oben links im Forumseditor oder [code] davor und [/code] dahinter ohne *).
Dann ist er auch auf mobilen Geräten besser lesbar.

Gruß Tommy

Hi

Naja - da der Arduino nicht als Uhr konzipiert ist, wundert Das wenig.
Wenn Du eine Uhr haben willst, nimmst Du einen Chip, Der auf Genauigkeit getrimmt ist.
Das braucht der Arduino aber nicht, Der ist ein Rechenknecht.
Auch sind 1000 zu 1002 'Ticks' pro Sekunde ein sehr guter Wert - wird sich aber wohl schwer ändern, wenn der Arduino Mal wärmer/kälter ist oder Interrupts gesperrt werden (z.B. durch WS2812B).

Was hast Du vor und wofür brauchst Du Zeiten kleiner als eine Sekunde, Die Dir die RTC ebenfalls ausgeben kann.

MfG

PS: Ich habe eine Uhr laufen, in Der ich noch ca. 700 'Ticks' pro Sekunde bekomme - da der Sekundenzeiger animiert ist und nach einer Sekunde damit halt fertig sein sollte (die nächste Sekunde startet ja dann), nehme ich halt diese 700 (wird laufend angepasst wenn der Wert weiter sinkt) als Sekundenmaß.

*code

#include <Wire.h >
#include <LiquidCrystal_I2C.h> //LCD
LiquidCrystal_I2C LCD1(0x27, 20,4);
LiquidCrystal_I2C LCD2(0x3f, 16,2);
//#include <DS3232RTC.h>
//#include <Time.h>
//#include <TimeLib.h>
#include "RTClib.h"
#include "DHT.h" //DHT Bibliothek laden
#define DHTPIN 2 //Der Sensor wird an PIN 2 angeschlossen 
#define DHTTYPE DHT11    // Es handelt sich um den DHT11 Sensor
DHT dht(DHTPIN, DHTTYPE); //Der Sensor wird ab jetzt mit 
                         //„dth“ angesprochen
#define PV_U A0 //  PV_Modul Spannung max 21v
#define PV_I A1 // Strom vom PV_Modul max 16A
#define IN_U A2 // Inverter Spannung 12V auf 230V, max 1500W
#define IN_I A3 // Inverter Strom 
#define REF_VOLTAGE 5.0  
#define PIN_STEPS 1024.0
RTC_DS3231 rtc;

float pvout = 0.0, pvvin = 0.0;    //Spannung am PV Modul
float piout = 0.0, pviin = 0.0;    // Strom am PV Modul
float PVvolt = 0;          // Eingang spannung vom PV Modul

float invout = 0.0, invvin = 0.0;  //Spannung am Inwerter
float iniout = 0.0, iniin = 0.0; // Strom am Inwerter
float INVvolt = 0;             // Eingang spannung vom Inwerter

float R1 = 100000.0;        // resistance R1 (= 100 KOhm)// Spanungsteiler
float R2 =  10000.0;         // resistance R2 (= 10 KOhm)

float walt =0, wneu = 0 ;      // Wattzahl 
float sekunde =0; 

float minuten = 0 ;
float stunden = 0; 
float tag = 0;

float wm = 0 ;
float wm1 = 0 ;
float kwh = 0 ;
float kwm =0;
int taster1 = 8;        // Variable für Pin 8 Taster
int taster2 = 9;   
int taster1Status;   
int taster2Status; 
unsigned long previousMillis = 0; // speichert den Zeitpunkt an dem zuletzt geschalten wurde
unsigned long interval = 945; // Länge der Pause in ms
int s =0;
int a=0;
int b=0;
void setup()
   


{
    
   LCD1.init(); //20x4
   LCD2.init(); //16x2

    pinMode(taster1, INPUT);   // definiere Pin 8 als Eingang
    pinMode(taster2, INPUT);
    digitalWrite(taster1, HIGH);  // aktiviert den Pull- Up- Widerstand
    digitalWrite(taster2, HIGH);
   //  pinMode(PV_U , INPUT);
   
    dht.begin();   //DHT11 Sensor starten  (Temp +Feuchtigkeit)
}

void loop()
{

        //Taster 
 taster1Status = digitalRead(taster1); // prüfe ob Taster
 taster2Status = digitalRead(taster2);  //gedrückt oder nicht und  
DateTime now = rtc.now();
      s= now.second();


     //Strom Anfang
  
    
unsigned long currentMillis = millis();     
 
    if (currentMillis - previousMillis >= interval) 
    { b= previousMillis;
    previousMillis = currentMillis; // Zeitpunkt der letzten Schaltung wird festgehalten 
   
     // PV Modul
    PVvolt = analogRead(PV_U);
    pvout = (PVvolt * REF_VOLTAGE) / PIN_STEPS;
    pvvin = pvout / (R2 / (R1 + R2));
    PVvolt = analogRead(PV_I);
    piout = (PVvolt * REF_VOLTAGE) / PIN_STEPS;
    pviin = piout / (R2 / (R1 + R2));

      
    if (pvvin < 0.09)
    {  pvvin = 0.00000001;  }  //damit man nicht durch "0" dvidiert ;)
    
   if ( pviin < 0.09 )
    { pviin=  0.0000001; }

    //Inverter
    INVvolt = analogRead(IN_U);
    invout = (INVvolt * REF_VOLTAGE) / PIN_STEPS;
    invvin = invout / (R2 / (R1 + R2));
    INVvolt = analogRead(IN_I); //Falsch?
    iniout = (INVvolt * REF_VOLTAGE) / PIN_STEPS;
    iniin = iniout / (R2 / (R1 + R2));

   if (invvin < 0.09)
    {  invvin = 0.00000001;  } //damit man nicht durch "0" dvidiert ;)
    
   if ( iniin < 0.09 )
    { iniin =  0.0000001; }

   sekunde=s;
     // walt =  (pvvin * pviin) + (invvin * iniin);
     walt=1;
      wneu = walt+wneu ; 
    
      sekunde = sekunde +1; 
 
               
        
      if (sekunde == 60)  
        {
          sekunde = 0;
          minuten= minuten + 1;
          wm = wneu/60;
          wneu = 0 ;
          wm1 = wm + wm1 ;
          kwm= wm1/1000;
          walt=1;
           }
           
    if (minuten >= 60)
     {
         minuten = 0;
         stunden = stunden + 1;
         
        }  
    if (stunden >= 24)
     {    
        stunden =0 ;      
       tag = tag+1 ;
      }
      
       kwh = (wm1/60)/1000 ;
     

    }
   LCD1.setCursor(0,0);
   
   LCD1.print("PV " + String(pvvin,1) + "V ");// pvvin,1 eine nachkomma stelle azeigen
   LCD1.print( String((pviin),1) + "A ");
   LCD1.print("  S ");
   if (sekunde < 10)
   {LCD1.print (0);}
   LCD1.print(sekunde,0);
       
   LCD1.setCursor (0,1); 
    LCD1.print("IN " + String(invvin,1) + "V ");
    LCD1.print( String((iniin),1) + "A ");
    LCD1.print("  M ");
     if (minuten < 10)
    {LCD1.print (0);}
    LCD1.print(minuten,0);
    
  
   
   LCD1.setCursor (0,2);
     LCD1.print ("KWh ");
     LCD1.print (kwh,6);
     LCD1.print ("   St ");
     LCD1.print (stunden,0);
          
   LCD1.setCursor(0,3);
     if (wm1> 1000)
     {
       LCD1.print ("KWmin ");
      LCD1.print (kwm);
         }
         
     else 
   {
       LCD1.print ("W/min ");
       LCD1.print (wm1); 
       LCD1.print ("  T ");
      LCD1.print (wneu,1 );
    }
     // Ende mit Strom
   
  
   
     // LCD Hintergrundbeleuchtung  ein -- aus  
        //   19.00 aus  8.00 ein    
     if( now.hour() >= 19)
      { LCD1.noBacklight(); LCD2.noBacklight(); }
    else
      if( now.hour() >=8)
      { LCD1.backlight() ; LCD2.backlight();   } 
    else
      { LCD1.noBacklight();   LCD2.noBacklight(); }
     if(taster1Status == LOW)    
      { LCD1.backlight(); } 
     if(taster2Status == LOW)    
      { LCD2.backlight(); } 
  
       // Ende Hintergrundbeleuchtung  ein -- aus


 
   // RTC Uhr mit Uhr + Temp und Luftfeuchtigkeit
     
        
 float Luftfeuchtigkeit = dht.readHumidity(); // Die Luftfeuchtigkeit auslesen und unter „Luftfeutchtigkeit“ speichern
 float Temperatur = dht.readTemperature();//die Temperatur auslesen und unter „Temperatur“ speichern
                                         // Z.B 22.30 °C 

   LCD2.setCursor(0,0); //Zeit anzeigen   
   LCD2.print("   " );
      if (now.hour() <10)
       {  LCD2.print("0");  }
   LCD2.print(now.hour(), DEC); 
   
   LCD2.print(":");    
      if( now.minute() <10) 
       {  LCD2.print ("0");}     
   LCD2.print(now.minute(), DEC);
   
   LCD2.print (":");
      if(now.second() <10 )
      {  LCD2.print("0"); }
    LCD2.print(now.second(), DEC);
 
int auswahl ; // Für case

   if(now.second() <= 5 ) // Umschalt zeit für LCD
    { auswahl = 1; }
   if(now.second() >=10 )
    { auswahl = 2; }  
   if(now.second() >25)
    { auswahl = 1; } 
   if(now.second() >30 )
     { auswahl = 2; }
   if(now.second() >45)
     { auswahl = 1; }  
   if(now.second() >50 )
     { auswahl = 2; } 

 switch (auswahl) //Anweisung für umschalten
   {
case 1:
    //("Anweisung1") //Datum anzeigen

 
  LCD2.setCursor(0,1);//Hier wird die Position des etrsten Zeichens festgelegt. In diesem Fall bedeutet     
    
    if(now.day()  < 10)
    { LCD2.print (" ");}
  LCD2.print (now.day(), DEC);// Tag anzeigen
  LCD2.print (" ");
  
   if(now.month() == 1)      // Monat anzeigen
   { LCD2.print("Januar"); }
    if(now.month() == 2) 
   { LCD2.print("Februar"); }
   if(now.month() == 3) 
   { LCD2.print("Maerz");  }
   if(now.month() == 4)
   { LCD2.print("April");  }
   if(now.month() == 5)
   { LCD2.print("May");   }
   if(now.month() == 6)
   { LCD2.print("Juni");  }
   if(now.month() == 7)
   { LCD2.print("Juli"); }
   if(now.month() == 8)
   { LCD2.print("August"); }
   if(now.month() == 9)
   { LCD2.print("Septem."); }
   if(now.month() == 10)
   { LCD2.print("Oktober");  }
   if(now.month() == 11)
   { LCD2.print("Nov.");  }
   if(now.month() == 12)
   { LCD2.print("Dez."); }
   LCD2.print (" ");
   
   LCD2.print (now.year(), DEC); //Jahr anzeigen
   LCD2.print("   "); // Wegen "&F "
  break;

case 2:

   //Anweisung 2  Temperatur und Feuchtigkeit Anzeigen

  LCD2.setCursor(0, 1);       
  LCD2.print(Temperatur,1);//Eine Stelle nach Kommma
  LCD2.print (" ");
  LCD2.print((char)223);  // ° Zeichen (°C )
  LCD2.print("C");
  LCD2.print("  ");
  LCD2.print(Luftfeuchtigkeit,0);// Nur Ganzzahl ochne Kommastellen
  LCD2.print ("% ");         
  LCD2.print("&F "); 
 break;
    }
   // Ende mit Uhr + Temp
   
  }
    //  PRG ende //
*/

Du solltest Dir das mit den Codetags in #1 nochmal genau durchlesen.
Der 1. Versuch ging daneben. Korrigiere das bitte.

Gruß Tommy

Als blutiger Anfänger mit 67 habe ich den Sketch geschrieben, hab nur ein wenig Ahnung mit Comodore basic

fony:
Hallo,
habe Problem mit millis(), in einer " sekunde " fehlen mir zwei Einheiten.

will ein stromzehler bauen mit Arduino im Garten. Angeschlossen sind RTC ,DHT und zwei LCD über I2C.
Sehr warschanlich durch die interrupts dauern die millis() 1000 länger als sekunde

Die zwei Einheiten werden sich aufsummieren.
Damit das nicht passiert:
combie erklärt die Verwendung von millis() ohne Summieren.

Ok - das mit dem Solarpanel hast Du im Ausgangspost vergessen.

Mit einem Sekundentakt wird das aber ein seehr ungenauer Leistungsmesser...

@ my_xy_projekt
wehre ja kein Problem jede µsek die Leistungmessen nur das Problem wurde dadurch nicht behoben,
als alter Aufzugsmonteur , gebe ich nicht auf der armer Arduino mit seinen 16MhZ wird Qualmen aber rechnen so wie ich will

Danke an ALLE

Melde mich wenn "das Ding rechnet" wie ich will

MfG

Bernhard

PS leider kann ich kein Englisch --- großer nachteil

fony:
@ my_xy_projekt
wehre ja kein Problem jede µsek die Leistungmessen nur das Problem wurde dadurch nicht behoben,
als alter Aufzugsmonteur , gebe ich nicht auf der armer Arduino mit seinen 16MhZ wird Qualmen aber rechnen so wie ich will

Danke an ALLE

Melde mich wenn “das Ding rechnet” wie ich will

Ich versteh noch nicht ganz das Problem. Ich bin geneigt zu behaupten, das Du einen Denkfehler hast.

Deinen Sketch hast Du noch immer nicht in Codetags gesetzt.
Du kannst das auch jetzt noch machen. Klicke auf Deiner Ursprungsnachricht auf “modify Message”; markiere den Text, der der Code ist und dann klicke oben auf der linken Seite im Editorfenster auf </>

Und jetzt zu Deiner Logik:
Du gehst das falsch an.
bei jedem Durchlauf schreibst Du den Display neu - vollkommen unsinnig, da Dein Auge das sowieso nicht schafft.

Die Aufgabe ist also:

In jedem Durchlauf von loop() U und I einlesen, P errechnen und aufsummieren.

Und nur jede Sekunde machst Du die Ausgabe auf dem Display.

Schreibe Deinen Code nicht komplett in loop() ; das macht keinen Sinn, ist unproduktiv und erschwert nur das debugen

PS leider kann ich kein Englisch — großer nachteil

da auch ich nur deutsch kann, sehe ich darin keinen Nachteil.

Na dann…

my_xy_projekt:
da auch ich nur deutsch kann, sehe ich darin keinen Nachteil.

Echt? Mir würden meine Englischkenntnisse sehr fehlen.

Chinesisch wäre heutzutage zusätzlich auch sehr nützlich finde ich.

Hi

Texte in Englisch lassen sich mit deepl.com übersetzen.
Wenn die Sprache exotischer wird, kann man sich auch von Google helfen lassen, deepl ist halt weniger datenhungrig ...

Zum Glück hat mein Schul-Englisch zumindest etwas gebracht - ohne deepl wäre ich aber ebenfalls aufgeschmissen - von Chinesisch ganz zu schweigen - hatte Mal einen chinesischen Elektro-Plan (Siebdruck-Presse ... wenn ich mich recht erinnere) - wenn man die Symbole gerafft hat, war's ganz einfach :slight_smile:

MfG

Deepl kann mittlerweile auch Chinesisch und Japanisch.

Gruß Tommy

Tommy56:
Deepl kann mittlerweile auch Chinesisch und Japanisch.

Gruß Tommy

Ich hab copy&paste aus China Seiten noch nicht probiert, aber wenn .pdf noch unterstützt wird, dann ist das eine echte Alternative.

Ich finde es sehr bemerkenswert, das da tatsächlich steht:

Bitte beachten Sie, dass Sie unseren Übersetzungsservice nicht für Texte mit personenbezogenen Daten jeglicher Art nutzen dürfen.

https://www.deepl.com/privacy.html (Nr.4 - Satz 1 / Stand 4.4.2020)
Danke dafür!

fony:
*code

Danke das der jetzt in tags steht.

Keine Kritik! Nur ein Hinweis. Wenn Du den Code vorher einmal mit STRG-T bearbeitest, werden die Einrückungen formatiert. Damit lässt sich der code dann auch besser lesen.

2 LCD’s…

Und dann:

  if (now.second() <= 5) // Umschalt zeit für LCD
    {
    auswahl = 1;
    }
  if (now.second() >= 10)
    {
    auswahl = 2;
    }
  if (now.second() > 25)
    {
    auswahl = 1;
    }
  if (now.second() > 30)
    {
    auswahl = 2;
    }
  if (now.second() > 45)
    {
    auswahl = 1;
    }
  if (now.second() > 50)
    {
    auswahl = 2;
    }
  switch (auswahl) //Anweisung für umschalten

Warum machst Du sowas?
Switch/Case kann auch 31 … 45 bzw. 46 … 50 oder 51 … 59
Da benötigst Du keinen zusätzlichen Abfrageparameter.

Versuche Deinen Code in einzelne Bereiche zu unterteilen, jenachdem, was da passieren soll und lagere das aus dem loop() aus.

Hi

Würde in einer kleineren Einheit (und ohne float) rechnen.
Spannung und Strom sind bekannt oder in Erfahrung zu bekommen.
Beides multipliziert ergibt W (Watt).
Wenn ich Das jede Sekunde mache, habe ich Ws (Watt-Sekunden), Die ich wunderbar aufaddieren kann.
3600Ws=1Wh

Auch lassen sich Divisionen unterbinden, wenn man den Divisor zuvor auf true prüft:
if (Divisor) {
Ergebnis=Dividend/Divisor;
}
Wobei fraglich ist, wo man großartig mit unbekanntem Dividenden dividieren müsste.

MfG

Boach, ich glaubte nicht das ich so eine Hilfe bekomme.
WErde das mit alle ruhe mall bearbeiten.

ich denke nach, komplett auf RTC umsteigen dh. millis rauswerfen und mit Herz rechnen.

Den den Vergleich zwischen millis () 1000 und 1 "echte RTC" Sekunde hab ich nicht hin bekommen :frowning:

Danke nochmals

Hallo,

https://www.arduino.cc/reference/de/language/functions/time/millis/
Das heißt wenn du Sekunden haben möchtest den Rückgabewert durch 1000 teilen.
Wenn du die RTC verwendest, dann zähle die Hertztakte und mach deine Vergleiche mit denen.

Hi

Von der RTC bekommst Du ein Sekunden-Takt.
Intern guckst Du bei JEDEM Takt nach, wie viele millis() seit dem letzten Takt vergangen sind - DAS ist Deine aktuelle Sekunde (bzw. 110% davon - damit Du noch etwas Luft hast).

In Deinem Sketch rechnest Du weiterhin mit millis() - nur Du nimmst nicht die 1000 als 'Dauer einer Sekunde', sondern den ausgemessneen Wert - bei meiner Uhr irgendwas um 700.

Nach 70 0 millis() ist also 1/10tel Sekunde bei mir vorbei - Zeit, für den nächsten Schritt der Animation.
So bin ich mit der Animation fertig, bevor die nächste Sekunde 'zuschlägt' und mir ggf. die 'Zeit' - also meine 700 'Ticks' noch weiter reduziert - weil der Arduino irgend wofür länger brauchte, als vorher - dann sind's halt ab Jetzt nur noch 698 'Ticks', Die ich zur Verfügung habe - dann startet mein 2.ter Schritt eben nicht nach 70 'Ticks', sondern bereits nach 69(komma 8 ... eigentlich).

Die Sekunden sind nur, damit der Arduino 'im Takt' bleibt.
Zusätzlich zum 'Mitzählen' der Sekunden (damit Du die aktuelle Uhrzeit hast), kannst Du alle paar Stunden die Uhrzeit aus der RTC auslesen, um ggf. Ungenauigkeiten auszugleichen.

MfG

Edit
Nachgerechnet und entschieden, eine Null zu streichen - waagerecht

postmaster-ino:
In Deinem Sketch rechnest Du weiterhin mit millis() - nur Du nimmst nicht die 1000 als 'Dauer einer Sekunde', sondern den ausgemessneen Wert - bei meiner Uhr irgendwas um 700.

Nach 700 millis() ist also 1/10tel Sekunde bei mir vorbei

Willst Du da noch mal nachrechnen?

Gruß Tommy

Hallo,

er kann auch das komplette Datum der RTC zyklisch auslesen seine "Zeit" bilden, nach dem Unix Format.
Dann muss er sich nicht um den eingehenden 1s Takt kümmern.
Er muss die RTC Zeiten nicht mit millis vergleichen. Wofür auch?
millis wird nur für einen zyklisch gebremsten Aufruf zum RTC ansteuern benötigt.
Wenn ich etwas aller einer RTC genauen Minute machen möchte und frage die RTC aller ungenauen millis 100ms ab, dann pendelt der Abfragefehler konstant maximal um die 100ms jeder Minute herum. Was ja nun nicht stören sollte für ein Stromzähler.