RTC-DS3231 & NEOPIXEL => String-Problem im seriellen Monitor

Hallo zusammen,

ich hab ein kleines Problem bei der Ausgabe eines Strings (result) auf dem seriellen Monitor.

Das Programm, mit dem ich angefangen habe, habe ich von dieser Seite und es funktioniert auch wunderbar: Datum, Uhrzeit und Wochentag werden im seriellen Monitor als zusammengesetzter String (in “void printRTCDateTime()”) ausgegeben.

Ich habe dann den Rest meines Programms (Umwandlung der Werte => Neopixel-Darstellung) dazu gesetzt, und plötzlich war die Anzeige des Strings (result) im Monitor weg!

Also alles wieder auf Anfang und das Ausgangsprogramm Stück für Stück ergänzt bis zu der Stelle, wo das Problem beginnt: sobald ich in Zeile 15 den Neopixel “strip” definiere, spinnt die Ausgabe des Strings und ich bekomme nichts mehr angezeigt…

EDIT: “nichts” stimmt nicht ganz… “Freitag, 2.April 0::0” -also ohne Uhrzeit- bekommt das Programm in diesem Stadium noch hin… wenn ich das Programm dann noch weiter ergänze (Neopixel ansteuern & Aktionen im EEPROM), steht dann wirklich nichts mehr da, kein Wochentag oder Datum…

// RTC D3231
#include <Wire.h>
#define RTC_I2C_ADDRESS 0x68 // I2C Adresse des RTC  DS3231

//Membervariablen
int jahr,monat,tag,stunde,minute,sekunde, wochentag;
int daysInMonth[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
String daysInWeek[7] = {"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"};
String monthInYear[12] = {"Januar","Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"};
String outputFormat = "%s, %02d.%s %04d %02d:%02d:%02d Uhr";

// NEOPIXEL
#include <Adafruit_NeoPixel.h>
#define PIN 6
//Adafruit_NeoPixel strip = Adafruit_NeoPixel(196, PIN, NEO_GRB + NEO_KHZ800);


//EEPROM
//#include <EEPROM.h>
//int adr = 102;


byte digits[10][7] = {{0,1,1,1,1,1,1},  // Digit 0
                     {0,1,0,0,0,0,1},   // Digit 1
                     {1,1,1,0,1,1,0},   // Digit 2
                     {1,1,1,0,0,1,1},   // Digit 3
                     {1,1,0,1,0,0,1},   // Digit 4
                     {1,0,1,1,0,1,1},   // Digit 5
                     {1,0,1,1,1,1,1},   // Digit 6
                     {0,1,1,0,0,0,1},   // Digit 7
                     {1,1,1,1,1,1,1},   // Digit 8
                     {1,1,1,1,0,1,1}};  // Digit 9 | 2D Array for numbers on 7 segment


//Setup Methode
void setup(){
  Wire.begin(); //Kommunikation über die Wire.h bibliothek beginnen.       
  Serial.begin(9600);  //Übertragungsgeschwindigkeit 9600 Baud
  Serial.println("Mit dem Befehl kann das Datum und die Uhrzeit gesetzt oder veraendert werden.");
  Serial.println("Beispiel: set 28.08.2013 10:54");
}



void loop(){ 
 rtcReadTime();
 printRTCDateTime();
 setRTCTime();
 delay(1000);
}


// ==================  RTC-Sub  =========================

//Ließt den aktuellen Zeitstempel aus dem RTC Modul.
void rtcReadTime(){
  Wire.beginTransmission(RTC_I2C_ADDRESS); //Aufbau der Verbindung zur Adresse 0x68
  Wire.write(0);
  Wire.endTransmission();
  Wire.requestFrom(RTC_I2C_ADDRESS, 7);
  sekunde    = bcdToDec(Wire.read() & 0x7f);
  minute     = bcdToDec(Wire.read()); 
  stunde     = bcdToDec(Wire.read() & 0x3f); 
  //Der Wochentag wird hier nicht ausgelesen da dieses mit 
  //dem Modul RTC DS3231 nicht über die Wire.h zuverlässig funktioniert.
  /* wochentag  =*/ bcdToDec(Wire.read());
  tag        = bcdToDec(Wire.read());
  monat      = bcdToDec(Wire.read());
  jahr       = bcdToDec(Wire.read())+2000;    
}

//Funktion zum schreiben / setzen der Uhrzeit.
void rtcWriteTime(int jahr, int monat, int tag, int stunde, int minute, int sekunde){
  Wire.beginTransmission(RTC_I2C_ADDRESS);
  Wire.write(0); // Der Wert 0 aktiviert das RTC Modul.
  Wire.write(decToBcd(sekunde));    
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(stunde));                                  
  Wire.write(decToBcd(0)); // Wochentag unberücksichtigt
  Wire.write(decToBcd(tag));
  Wire.write(decToBcd(monat));
  Wire.write(decToBcd(jahr-2000));  
  Wire.endTransmission();  
}

//Berechnet den Tag der Woche aus dem übergebenen Datumswerten.
byte calcDayOfWeek(int jahr, byte monat, byte tag) {
  static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
  jahr -= monat < 3;
  return ((jahr + jahr/4 - jahr/100 + jahr/400 + t[monat-1] + tag) % 7); 
}

//Convertiert Dezimalzeichen in binäre Zeichen.
byte decToBcd(byte val){
  return ( (val/10*16) + (val%10) );
}

//Convertiert binäre Zeichen in Dezimal Zeichen.
byte bcdToDec(byte val){
  return ( (val/16*10) + (val%16) );
}

//Ließt einen String und liefert einen Integer Wert von einer 
//definierten Stelle (byte num) des Stringwertes.
int getIntFromString (char *stringWithInt, byte num){
  char *tail; 
  while (num>0){
    num--;
    //Bei Kommanseparierten Listen werden die Kommata 
    //übersprungen und geben damit die Unterteilung des Strings an.
    while ((!isdigit (*stringWithInt))&&(*stringWithInt!=0)){
      stringWithInt++;
    }
    tail=stringWithInt;
    //Schleife solange ausführen bis eine Zahl gefunden wurde
    //welche größer 0 ist.
    while ((isdigit(*tail))&&(*tail!=0)){
      tail++;
    }
   
    if (num>0){
      stringWithInt=tail;
    }
  }  
  return(strtol(stringWithInt, &tail, 10));
} 

//Gibt einen Zeitstempel auf der Seriellen Schnittstelle aus.
void printRTCDateTime(){
  char linebuf[60];
  int dOW = calcDayOfWeek(jahr, monat, tag);
  String wochentagC = daysInWeek[dOW];
  String monatC = monthInYear[monat];

  String result = "";
  result += wochentagC;
  result += ", ";
  result += tag;
  result += ".";
  result += monatC;
  result += " ";  
  result += jahr;  
  result += " "; 
  if(stunde<10) { result += "0"; }
  result += stunde;  
  result += ":";  
  if(minute<10) { result += "0"; }
  result += minute;  
  result += ":"; 
  if(sekunde<10) { result += "0"; }
  result += sekunde;  
  Serial.println(result); 
}

//Manuelles setzen der Uhrzeit über den Seriellen Monitor der IDE.
void setRTCTime(){
  char linebuf[30];
  byte counter;
  if (Serial.available()){
    delay(100); // Warte auf das Eintreffen aller Zeichen vom seriellen Monitor
    memset(linebuf,0,sizeof(linebuf)); // Zeilenpuffer löschen
    counter=0; // Zähler auf Null
    while (Serial.available()){
      linebuf[counter]=Serial.read(); // Zeichen in den Zeilenpuffer einfügen
      if (counter<sizeof(linebuf)-1) counter++; // Zeichenzähler erhöhen
    }
    // Wenn in der gelesenen Zeile das Wort 'set' vorkommt dann...
    //(Hier muss man bedenken das die Suche nach 'set' auch nach x Zeichen ein positives Ergebnis liefern wird, zbsp. 123set 09.01.2016 12:00:00)
    if (strstr(linebuf,"set")==linebuf){ 
      tag=getIntFromString (linebuf,1);
      monat=getIntFromString (linebuf,2);
      jahr=getIntFromString (linebuf,3);
      stunde=getIntFromString (linebuf,4);
      minute=getIntFromString (linebuf,5);
      sekunde=getIntFromString (linebuf,6);
    } else {
      Serial.println("Befehl unbekannt.");
      return;
    }
    // Ausgelesene Werte einer groben Plausibilitätsprüfung unterziehen:
    if (!checkDateTime(jahr, monat, tag, stunde, minute, sekunde)){
      Serial.println(linebuf);
      Serial.println("Fehlerhafte Zeitangabe im 'set' Befehl");
      Serial.println("Beispiel: set 28.08.2013 10:54");
      return;
    }
    rtcWriteTime(jahr, monat, tag, stunde, minute, sekunde);
    Serial.println("Zeit und Datum wurden auf neue Werte gesetzt.");
  }
}

//Prüft das eingegebene Datum auf korrektheit.
boolean checkDateTime(int jahr, int monat, int tag, int stunde, int minute, int sekunde){
   boolean result = false;
   if(jahr>2000){
     result = true;  
   } else {
     return false;
   }
   // Schaltjahr prüfen
   if(jahr % 400 == 0 || (jahr % 100 != 0 && jahr % 4 == 0)){
     //Wenn es ein Schaltjahr ist dann den Wert 29 in das Array an der Stelle 1 für den Monat Februar schreiben.
     daysInMonth[1]=29;
   }
   
   //Monat muss kleiner 13 sein.
   if (monat<13){
      if( tag <= daysInMonth[monat-1] ){
        result = true;
      }
   } else {
     return false;
   }
    
  //Wert für Stunde muss zwischen 0 und 24 liegen,
  //Wert für Minute und sekunde muss zwischen 0 und 59 liegen
  if(stunde <24 && minute <60 && sekunde <60 && stunde >= 0 && minute >=0 && sekunde >=0){
        result = true;
  } else {
     return false;
   }
   
   return result;
}

Hilft mir jemand, das zu verstehen? :slight_smile:

Das könnte ein Speicherplatzproblem sein, auf welcher Hardware soll das laufen?

Reduziere die 196 auf einen deutlich kleineren Wert, was passiert?

In der Tat!!

Mal die Pixel auf 5 STück reduziert.. und es läuft!

Die Hardware, auf der ich es momentan teste, ist ein Duemilanove(UNO-baugleich?) mit MEGA328P-Chip.

Leider brauche ich die 196 Neopixel, aber kann evtl. noch andere Boards testen..

Sollte das dann aber nicht den gewünschten Effekt haben, dann muss ich mich wohl von diesem String verabschieden und die Ausgabe selbst aus den einzelnen Variablen (tag, monat, etc) zusammensetzen.

Es sei denn, jmd hat noch eine andere Idee! :wink:

Aber schonmal besten Dank für die Erklärung, woher dieser Fehler denn nun kommt :)

Hi

void printRTCDateTime() wird ein char-Array erzeugt, aber nicht benutzt. Statt Dessen wird mit Strings rumgespielt, Welche mit der Länge 0 initiiert werden, dann kommt 'was dran' - wenn Das nicht mehr auf den aktuellen Platz passt, wird ein neuer Platz dafür gesucht - 'weiter hinten'. Dort kommt dann wieder 'was dran' ... irgendwann hast Du einen total zerpflückten Speicher, Der zwar 'Platz ohne Ende' hat, aber leider nicht in der benötigten Menge am Stück - denke, hier wird Dein Problem angesiedelt sein.

Bekommst Du gar keine Anzeige, sobald Du die Neopixel einbindest? Wenn der Speicher schon knapp wird, sollte die IDE beim Hochladen dazu eine Warnung ausgeben. Spätestens jetzt ist es an der Zeit, sich die String-Geschichten noch Mal anzuschauen und durch 'was Gescheites' zu ersetzen.

Das oben bereits erwähnte char-Array belegt 60 Zeichen Platz (für 59 Zeichen + /0 Terminierung). Diese 60 Zeichen sind am Stück, egal, was Du Da rein schreibst - musst halt drauf achten, daß Du 'im Array' bleibst. Dein String wächst und wächst, mit jedem 'Zusammenkleben' brauchst Du die jetzige komplette Länge 'am Stück' für diesen String - und Du klebst bis zu 16x!! Gib die Zeilen DIREKT mit Serial.print aus und Du bist zumindest hier die Möglichkeit los, daß der Fehler genau dort liegt.

MfG

Die Klasse String verwaltet Speicher zur Laufzeit dynamisch. Das ist auf Systemen mit wenig RAM ungünstig, da dadurch der Speicher sehr schnell fragmentiert wird. Der Kompiler kann das wegen der Dynamik nicht unbedingt feststellen.

Wirf [u]alle[/u] Vorkommen von String aus dem Sketch raus und benutze dafür char-Arrays. Hier gibt es einige Grundlagen dazu.

Gruß Tommy

postmaster-ino:
Wenn der Speicher schon knapp wird, sollte die IDE beim Hochladen dazu eine Warnung ausgeben.

Nein, das wäre zu einfach. Die NeoPixel Bibliothek versteckt den notwendigen Speicher (anders als FastLED). Das String-Objekt ist auch immer ein Quell der Probleme. Aber mit einer Bibliothek wie MemoryFree kann man sich die dynamische Entwicklung zur Laufzeit ansehen.

Wieso überhaupt Strings konkatenieren? Wenn nur println() macht ist das unnötig. Man kann auch mehrmals print()/println() aufrufen

Hi

Serenifly: konkatenieren

:o siehe Wikipedia - wow, hier wird Deutsch gesprochen :) (kannte ich noch gar nicht - wird sich aber wohl auch nicht in meinen aktiven Wortschatz vertiefen - aber n2k (nice to know) )

MfG

Wir müssen ja auch Dir gegenüber den Bildungsauftrag erfüllen ;)

Gruß Tommy

Hast du gedacht dass strcat() was mit Katzen zu tun hat? :)

Der war gut.

Gruß Tommy

Wow!

Vielen Dank an alle für die vielen neuen Erkentnisse!

Hätte nicht gedacht, daß es bei einem so (relativ) übersichtlichen Programm bereits ein Speicherproblem gibt, wenn man aus Bequemlichkeit mit so einer Stringvariablen arbeitet. Zu meiner Rechtfertigung... wie gesagt: das ganze Listing bzgl. RTC ist ja von mir per copy&paste übernommen worden.

Aber nun werde ich wohl diesen bis zum Exzess konkat.. katkong.. zusammengeführten String komplett streichen :grinning:

Ihr seid mir wieder mal eine große Hilfe gewesen!

Wenn du Bequemlichkeit willst verwende das: http://arduiniana.org/libraries/streaming/

Wo man hier auch ein klein wenig Speicher sparen kann. Statt das:

Serial.print(",");

das:

Serial.print(',');

Hier byte statt int:

int daysInMonth[12]

Und für die Tages- und Monats-Namen kann man ein Array aus char* nehmen (oder besser const char* const)

Serenifly: ... (oder besser const char* const)

Oder Konstanten in den Programmspeicher verlagern, siehe Progmem. Besonders schön das "Arrays of strings".

oder Suchbegriff - F-Makro

Serial.println("Fehlerhafte Zeitangabe im 'set' Befehl"); Serial.println("Beispiel: set 28.08.2013 10:54");

zu

Serial.println(F("Fehlerhafte Zeitangabe im 'set' Befehl\n"
                       "Beispiel: set 28.08.2013 10:54"));