TFT Display und SD Karte über SPI

Okay trotzdem vielen Dank für deine Mühe! :)

Korrektur zu 1: readStream schreibt einen Zeiger fort, der beim nächsten Tageswechsel möglicherweise zu weit vorne steht. Wenn das so wäre (ich habe nicht zwei Tage gewartet :) ) müßtest Du entweder den Zeiger wieder zurück setzen oder mit open/close arbeiten.

27sharp: 2. Wie du wahrscheinlich bemerkt hast benutze ich in der getRow() Methode den Befehl "strdup". Dies erzeugt ja dynamisch Speicher. Jetzt muss ich es ja wieder mit free() leeren, oder ?

Ja, unbedingt. Es ist ganz böse wenn man das nicht tut. Irgendwann ist der freie Speicher zu Ende und dein Programm stürzt ab.

Oder man verwendet die Funktion gar nicht erst (und es ist eine Funktion. Kein Befehl!). Ich wüsste nicht wozu das überhaupt nötig wäre, bzw. wozu du unbedingt alle Strings auf einmal speichern musst. Wie man das besser lösen kann habe ich glaube ich schon in einem anderen Thread gesagt.

Aber wenn du darauf bestehst, dann musst du am Ende über das Array aus Zeigern iterieren und free() machen. Und am besten danach den Zeiger auf NULL setzen! Dann macht es nichts wenn man aus irgendeinem Grund nochmal free() macht und man kann abfragen ob an der Stelle ein gültiger String steht.

Serenifly: Ja, unbedingt. Es ist ganz böse wenn man das nicht tut. Irgendwann ist der freie Speicher zu Ende und dein Programm stürzt ab.

Ich wüsste nicht wie ich ohne dieser Funktion auskommen könnte, denn sonst bekomme ich ja immer den letzten Eintrag der While-Schleife.

Könntest du mir vielleicht ein Beispiel zeigen, wie ich den Speicher leeren könnten?

Serenifly: Oder man verwendet die Funktion gar nicht erst (und es ist eine Funktion. Kein Befehl!). Ich wüsste nicht wozu das überhaupt nötig wäre, bzw. wozu du unbedingt alle Strings auf einmal speichern musst. Wie man das besser lösen kann habe ich glaube ich schon in einem anderen Thread gesagt.

Ich speichere doch gar nicht alle Strings ab, sondern nur immer die 7 Werte, oder habe ich das falsch verstanden?

Gruß

Wenn du nicht verstehst was malloc() und free() machen lass die Finger davon. Das ist eine ganz heikele Geschichte und einer der größten Fehlerquellen in C/C++. malloc() (und strdup() macht nichts anderes) legt Speicher dynamisch an, der dann nicht mehr für das übrigen Programm zu Verfügung steht. Dieser Speicher muss wieder mit free() freigeben werden, oder du hast irgendwann keinen Speicher mehr.

Ich speichere doch gar nicht alle Strings ab, sondern nur immer die 7 Werte

Eben das sollte nicht nötig sein. Zeile einlesen, diese Zeile auswerten und/oder anzeigen, nächste Zeile einlesen...

Wenn man wirklich Werte speichern muss, dann ist es wahrscheinlich einfacher man speichert die Stunden und Minuten ab. z.B. in einem Array aus struct das zwei Bytes enthält. Aber nicht die Strings.

Selbst wenn du 7 Strings kopieren möchtest, geht das sicherer mit strncpy() in ein zwei-dimensionales Array! Dynamischen Speicher braucht man wenn die Länge zur Laufzeit nicht bekannt ist. Hier weißt du eigentlich dass ein Zeit-String maximal 5 Bytes + Terminator hat. Wieso also dynamischer Speicher?

So habe jetzt deinen Rat mit strcpy() befolgt und wie folgt gelöst:

char str [7][15];

....

if(i>zeile && i<=(zeile+7) ){
    strcpy(str[z],serialBuffer);

Geht das so in Ordnung?

Meinen Vorschlag von #17 habe ich um die Funktion freeRam erweitert, um den freien Speicher zu überwachen. Tatsächlich wird der immer kleiner. Daher habe ich free ergänzt, wodurch der Speicher (“freier Speicher: 6767”) konstant bleibt. Den Tagesübergang habe ich mit einem Taster simuliert.

#include <Wire.h>
#include "SPI.h"
#include "Adafruit_GFX.h"
#include <Adafruit_TFTLCD.h> // Hardware-specific library 
#include "Sodaq_DS3231.h"
#include <SD.h>

#define SD_CS 53   // Set the chip select line for SD-card
#define LCD_CS 31  // Chip Select 
#define LCD_CD A15 // Command/Data 
#define LCD_WR A14 // LCD Write 
#define LCD_RD 30  // LCD Read 
#define LCD_RESET 42 // Can alternately just connect to Arduino's reset pin 

Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

// Color definitions
#define ILI9341_BLACK       0x0000      /*   0,   0,   0 */
#define ILI9341_NAVY        0x000F      /*   0,   0, 128 */
#define ILI9341_DARKGREEN   0x03E0      /*   0, 128,   0 */
#define ILI9341_DARKCYAN    0x03EF      /*   0, 128, 128 */
#define ILI9341_MAROON      0x7800      /* 128,   0,   0 */
#define ILI9341_PURPLE      0x780F      /* 128,   0, 128 */
#define ILI9341_OLIVE       0x7BE0      /* 128, 128,   0 */
#define ILI9341_LIGHTGREY   0xC618      /* 192, 192, 192 */
#define ILI9341_DARKGREY    0x7BEF      /* 128, 128, 128 */
#define ILI9341_BLUE        0x001F      /*   0,   0, 255 */
#define ILI9341_GREEN       0x07E0      /*   0, 255,   0 */
#define ILI9341_CYAN        0x07FF      /*   0, 255, 255 */
#define ILI9341_RED         0xF800      /* 255,   0,   0 */
#define ILI9341_MAGENTA     0xF81F      /* 255,   0, 255 */
#define ILI9341_YELLOW      0xFFE0      /* 255, 255,   0 */
#define ILI9341_WHITE       0xFFFF      /* 255, 255, 255 */
#define ILI9341_ORANGE      0xFD20      /* 255, 165,   0 */
#define ILI9341_GREENYELLOW 0xAFE5      /* 173, 255,  47 */
#define ILI9341_PINK        0xF81F

#define MAX_ZEILE 210

String dateString;
int minuteNow = 0;
int minutePrevious = 0;
int dateNow = 0;
int datePrevious = 0;
char timeChar[7] = {"00.00"};
char dateChar[15] = {"00.00.0000"};

DateTime now;
const int READ_BUFFER_SIZE = 40;
char serialBuffer[READ_BUFFER_SIZE];
File myFile;

char *str [7] = {"01234"};
uint32_t old_ts;

void setup() {
  Serial.begin(9600);
  Serial.println("Anfang");
  pinMode(2, INPUT_PULLUP);
  SPI.begin();
  SD.begin(SD_CS);
  Wire.begin();
  rtc.begin();
  // setRTCTime(); // nur zum Setzen der Zeit in der RTC
  tft.reset();
  tft.begin(0x9341);
  tft.invert();
  tft.setRotation(1);
  tft.fillScreen(ILI9341_BLACK);
  Serial.print("freier Speicher: ");
  Serial.println(freeRam());
}

void loop() {
  if (!digitalRead(2)) {  // nur zum Testen, um nicht 24 h warten zu müssen
    datePrevious = 0;
    Serial.print("freier Speicher: ");
    Serial.println(freeRam());
  }
  now = rtc.now(); //get the current date-time

  uint32_t ts = now.getEpoch();

  if (old_ts == 0 || old_ts != ts) {
    old_ts = ts;

    minuteNow = now.minute();
    if (minuteNow != minutePrevious) {
      minutePrevious = minuteNow;
      getTime();
      tft.fillRect(64, 0, 200, 55, ILI9341_BLACK);
      printText(timeChar, ILI9341_WHITE, 65, 5, 6);
    }
    dateNow = now.date();
    if (dateNow != datePrevious) {
      datePrevious = dateNow;
      getDate();
      tft.fillRect(44, 57, 240, 30, ILI9341_BLACK);
      printText(dateChar, ILI9341_YELLOW, 43, 56, 4);
      getRow(dateChar);
      printText(str[0], ILI9341_CYAN, 43, 100, 2);
      printText(str[1], ILI9341_CYAN, 43, 120, 2);
      printText(str[2], ILI9341_CYAN, 43, 140, 2);
      printText(str[3], ILI9341_CYAN, 43, 160, 2);
      printText(str[4], ILI9341_CYAN, 43, 180, 2);
      printText(str[5], ILI9341_CYAN, 43, 200, 2);
      free(str[0]);
      free(str[1]);
      free(str[2]);
      free(str[3]);
      free(str[4]);
      free(str[5]);
    }
  }
  delay(1000);
}

bool readStream(File& myFile)  // Zeile für Zeile einlesen
{
  static byte index;
  while (myFile.available())
  {
    char c = myFile.read();
    if (c >= 32 && index < READ_BUFFER_SIZE - 1)
    {
      serialBuffer[index++] = c;
    }
    else if (c == '\n' && index > 0)
    {
      serialBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;
}

void getRow(char *datum) { // Akt. Datum in der Text Datei finden und einordnen.
  myFile = SD.open("test.txt");
  int i = 0;
  int z = 0;
  int zeile = MAX_ZEILE;
  while (i < MAX_ZEILE && readStream(myFile)) {
    Serial.print(datum);
    Serial.print("   ");
    Serial.print(serialBuffer);
    Serial.print("   i: ");
    Serial.print(i);
    if (strcmp(datum, serialBuffer) == 0) {
      zeile = i;
    }
    if (i > zeile && i < (zeile + 7) ) {
      str[z] = strdup(serialBuffer);
      Serial.print("   z: ");
      Serial.print(z);
      z++;
    }
    if (i >= (zeile + 6)) {
      i = MAX_ZEILE;
    } else {
      i++;
    }
    Serial.println();
  }
  for (byte j = 0; j < 7; j++) {
    Serial.print(str[j]);
    Serial.print("  ");
  }
  Serial.println();
  myFile.close();
}

void setRTCTime()
{
  DateTime dt(2016, 6, 22, 20, 58, 00, 3); // Year, Month, Day, Hour, Minutes, Seconds, Day of Week
  rtc.setDateTime(dt); //Adjust date-time as defined 'dt' above
}
void printText(char *text, uint16_t color, int x, int y, int textSize)
{
  tft.setCursor(x, y);
  tft.setTextColor(color);
  tft.setTextSize(textSize);
  tft.setTextWrap(true);
  tft.print(text);
}
void getDate()
{
  now = rtc.now(); //get the current date-time

  if (now.date() < 10 && now.month() < 10)
  {
    dateString = "0" + String(now.date()) + "." + "0" + String(now.month()) + "." + String(now.year());
  }
  else if (now.date() < 10)
  {
    dateString = "0" + String(now.date()) + "." + String(now.month()) + "." + String(now.year());
  }
  else if (now.month() < 10)
  {
    dateString = String(now.date()) + "." + "0" + String(now.month()) + "." + String(now.year());
  }
  else
  {
    dateString = String(now.date()) + "." + String(now.month()) + "." + String(now.year());
  }

  dateString.toCharArray(dateChar, 15);

}
void getTime()
{
  String hours = String(now.hour());

  if (now.hour() < 10 && now.minute() < 10) {
    hours = "0" + hours + ":0" + String(now.minute());
  }
  else if (now.minute() < 10)
  {
    hours = hours + ":0" + String(now.minute());
  }
  else if (now.hour() < 10)
  {
    hours = "0" + hours + ":" + String(now.minute());
  }
  else
  {
    hours = hours + ":" + String(now.minute());
  }

  hours.toCharArray(timeChar, 7);
}

// Quelle: http://jeelabs.org/2011/05/22/atmega-memory-use/
int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

Geht auch das so in Ordnung? (abgesehen von der versteckten Speicherreservierung)

Ja, aber fällt dir nicht auf wie unnötig das ist? Wenn man die Zeit-Strings nur anzeigt besteht einfach kein Grund sie alle zu speichern. Man muss nur den aktuellen String speichern. Wenn er einmal auf der Anzeige ist braucht man ihn nicht mehr.

Und wenn man die Zeiten permanent braucht um irgendwas damit zu rechnen, dann nimmt man besser Integer.

Serenifly: Ja, aber fällt dir nicht auf wie unnötig das ist?

Doch schon und ich wäre auch nicht auf so eine Lösung gekommen. Aber ich habe was gelernt:

  • Es gibt versteckte Speicherreservierung, die man meiden sollte.
  • Eine Speicherreservierung kann dynamisch den verfügbaren Speicher zur Laufzeit reduzieren, muß also möglichst wieder freigegeben werden.
  • Es macht möglicherweise Sinn, den verfügbaren Speicher zur Laufzeit mit einer Funktion anzeigen zu lassen.
  • Die Verwendung von str (ein Feld aus Zeigern?) war mir anfänglich gänzlich unklar.

Hat sich doch gelohnt, bei diesem Thema mit sympathischem TO, wo es mal nicht um millis geht, einzusteigen :)

Wenn der Sketch dann mal tut, was er soll, ist eine gute Gelegenheit zur Optimierung, beispielsweise durch Reduzierung überflüssiger Variablen. Zumindest kann man Optimierungsideen zum nächsten Sketch mitnehmen.

Von mir Dank für die Unterstützung!

agmue: Es gibt versteckte Speicherreservierung, die man meiden sollte

Dass da dynamischer Speicher verwendet wird und man free() machen muss steht in der Doku sehr deutlich dabei: http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html#ga8569f20e38a030b5a28fd951abec2c9b

Warning: The strdup() function calls malloc() to allocate the memory for the duplicated string! The user is responsible for freeing the memory by calling free().

Mir war die Funktion bekannt. Das ist aber das erste mal wo ich gesehen habe dass sie jemand verwendet hat

Die Verwendung von str (ein Feld aus Zeigern?) war mir anfänglich gänzlich unklar.

Da C Strings Zeiger auf das erste Element sind, sind Arrays aus C Strings Arrays aus Zeigern.

Aber wie gesagt, ein zwei-dimensionales Arrays dass gerade groß genug für die Strings ist (und 15 ist hier schon wieder viel mehr als man braucht), ist hier wesentlich vernünftiger.

Also vielen Dank noch einmal für die super Hilfe von euch beiden. Ich habe aufjedenfall viel dazu gelernt und strdup() werde ich versuchen zu vermeiden! :smiley:

Ich habe vorher immer mit Java programmiert und für c habe ich mir nur die Grundlagen angeschaut, jedoch hat das wohl nicht gereicht.