Schreiben auf SD Karte klappt plötzlich nicht mehr

Hi,
ich habe mir einen Datenlogger gebaut, der auch ein paar Tage erfolgreich im Einsatz war. Dann habe ich ein ssd1306 I²C oled Display hinzugefügt um die Messwerte auch fortlaufend anzuzeigen, und seitdem funktioniert das Schreiben der Messwerte auf die SD Karte nicht mehr. Am Code der mit der SD Karte zu tun hat, habe ich dabei nichts geändert. Dateien von der Karte zu lesen klappt auch noch. Beim Versuch Dateien zum Schreiben zu öffnen wird die Datei erstellt, falls nicht vorhanden, aber das Öffnen der Datei schlägt fehlt. Oder anders gesagt, die Funktion SD.open() liefert immer 0 zurück, selbst wenn die nicht existierende Datei erfolgreich erstellt wurde.

int writeToSD(String dataString) {
	File dataFile = SD.open("data8.txt", FILE_WRITE);

// if the file is available, write to it:
	if (dataFile) {
		Serial.println("file opened");
		dataFile.println(dataString);
		dataFile.close();
		Serial.println("file closed");
		return 0;
		logs++;
	}
// if the file isn't open, pop up an error:
	else {
		Serial.println("can't open file");
		return 1;
	}
}

"can't open file" wird ausgegeben. Danach ist eine leere Datei auf der Karte vorhanden.

Ich dachte schon dass das Display vielleicht Störungen verursacht, weil es recht nahe im SD Slot angebracht war, aber es ein paar Zentimeter zu entfernen brachte leider auch nichts.

Hallo,

mit welchem Arduino arbeitest du den?

Gruss Temcuin

Ich tippe auf Speichermangel, und wie immer ist der Fehler in dem Teil des Sketches, der nicht gezeigt wird, bzw. In der Hardware, die auch verschwiegen wird

Das Fragment allein sagt nichts aus. Dazu wäre der ganze Sketch sinnvoll.
Was meldet denn Der Kompiler am Ende?

Gruß Tommy

Sorry, ebenso wenig wie Infos vorenthalten will ich das Forum mit elend langen Beiträgen nerven. Ich poste immer lieber erstmal nur den Ausschnitt in dem ich den Fehler vermute, und liefe der Rest bei Bedarf nach, als eingangs lange Beiträge zu posten die sich sowieso niemand mehr durchlesen will. Angesichts der 9000 Charakter Begrenzung und 5min Postingsperre scheint mit das hier im Forum auch nicht so erwünscht zu sein.

Es handelt sich um einen originalen Arduino UNO. Ein Datalogging Shield von ebay, mit RTC1307 und SD Slot und ein I²C OLED Display (SSD1306). Über digitale Ausgänge steuere ich zudem zwei LEDs und Relais. Und über die analogen Eingänge messe ich zwei Spannungen über Spannungsteiler. (Eines davon ist ein analoges Signal eines Hall-Sensors ACS712 der einen Strom misst.)

Kurze Erklärung was das ganze tun soll:
Ein Akku soll mit einer Last entladen werden. Dabei soll in einem wählbarem Intervall Spannung und Stromstärke aufgezeichnet und angezeigt werden. Unterschreitet die Spannung ein wählbares Niveau soll ein Relais die Last vom Akku trennen. Intervall und Abschaltspannung sollen von der SD Karte gelesen werden. Starten des Arduino mit Pin9 auf Masse soll die Konfigurationsdatei mit Default Werten auf die SD Karte schreiben.

Hier ist der gesamte Code.
Spannungslogger.h

#ifndef _Spannungslogger_H_
#define _Spannungslogger_H_
#include "Arduino.h"
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include <ssd1306.h>

#define R1 196.1 //kohm - Spannungsmessung
#define R2 9.82 //kohm - Spannungsmessung
#define R3 514.4 //kohm - Strommessung
#define R4 150.4 // kohm - Strommessung
#define VREF 1.1 // interne Spannungsreferenz
#define VNULL 2478 //Strommessung Spannung bei 0A in mV

#define CSPIN 10 //SD Karte
#define VOLTAGEPIN A1
#define CURRENTPIN A3
#define LED1PIN 3
#define LED2PIN 4
#define RELAISPIN1 6
#define RELAISPIN2 7
#define RESETPIN 9



RTC_DS1307 rtc;
uint32_t lastLog = 0;
uint32_t lastSecond = 0;
uint32_t lastDisplayUpdate = 0;
uint32_t lastBarUpdate = 0;
uint32_t intervall = 1;
uint32_t logs = 0;
float cutOutVoltage = 11.4;

void readConfig(int a);
float readVoltage(int pin, float r1, float r2);
float readCurrent();


String createString(DateTime now, float voltage, int current);
int writeToSD(String dataString);

class Led {
 public:
 Led(int pin) : pin(pin){};
 void set(int count, int onTime, int intervall);
 void execute();

 private:
 int pin;
 uint32_t interval = 0;
 int onTime = 0;
 int count = 0;
 bool action = 0;
 uint32_t nextAction = 0;
 bool status = 0;

};

Led led[] = {Led(LED1PIN), Led(LED2PIN)};

#endif /* _Spannungslogger_H_ */

Der Rest kommt kommt dann in 5min.

Spannungslogger.cpp

#include "Spannungslogger.h"
#include <font6x8.h>

void setup() {
 analogReference(INTERNAL);
 analogRead(VOLTAGEPIN);
 analogRead(CURRENTPIN);
 pinMode(LED1PIN, OUTPUT);
 pinMode(LED2PIN, OUTPUT);
 pinMode(RELAISPIN1, OUTPUT);
 pinMode(RELAISPIN2, OUTPUT);
 pinMode(RESETPIN, INPUT_PULLUP);
 digitalWrite(LED1PIN, 0);
 digitalWrite(LED2PIN, 1);
 digitalWrite(RELAISPIN1, 255);
 digitalWrite(RELAISPIN2, 255);
 Wire.begin();
 ssd1306_128x64_i2c_init();
 ssd1306_fillScreen(0x00);
 ssd1306_setFixedFont(ssd1306xled_font6x8);
 rtc.begin();
 Serial.begin(115200);
 if (!rtc.isrunning()) {
 Serial.println("RTC is NOT running!");
 // following line sets the RTC to the date & time this sketch was compiled
 rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
 }
 rtc.now();
 readConfig(digitalRead(RESETPIN));
 //delay(1000);
 digitalWrite(LED2PIN, 0);
 //delay(3000);
 digitalWrite(RELAISPIN2, 0);
}

void loop() {

 int current = 0;
 float voltage = 0.0;
 if (millis() > lastDisplayUpdate + 1000) {
 lastDisplayUpdate = millis();
 voltage = readVoltage(VOLTAGEPIN, R1, R2);
 current = readCurrent();
 int i_voltage = int(voltage);
 int id_voltage = int((voltage) * 100.0) - (i_voltage * 100);
 char t_text[21];
 sprintf(t_text, "Spannung:      %2i.%02iV", i_voltage, id_voltage);
 ssd1306_printFixed(0, 0, t_text, STYLE_NORMAL);
 sprintf(t_text, "Strom:        %5imA", current);
 ssd1306_printFixed(0, 8, t_text, STYLE_NORMAL);
 uint32_t t_intervall = intervall;
 char e = 's';
 if (t_intervall > 7200) {
 t_intervall = t_intervall / 60;
 e = 'h';
 }else if (t_intervall > 120) {
 t_intervall = t_intervall / 60;
 e = 'm';
 }
 sprintf(t_text, "Intervall:    %6li%c", t_intervall, e);
 ssd1306_printFixed(0, 16, t_text, STYLE_NORMAL);
 i_voltage = int(cutOutVoltage);
 id_voltage = int((cutOutVoltage) * 100.0) - (i_voltage * 100);
 sprintf(t_text, "Ende:          %2i.%02iV", i_voltage, id_voltage);
 ssd1306_printFixed(0, 24, t_text, STYLE_NORMAL);
 uint32_t time = millis() / 1000;
 if (time > 7200) {
 time = time / 60;
 e = 'h';
 } else if (time > 120) {
 time = time / 60;
 e = 'm';
 }
 sprintf(t_text, "Laufzeit:     %6li%c", time, e);
 ssd1306_printFixed(0, 32, t_text, STYLE_NORMAL);
 sprintf(t_text, "Datenpunkte: %8li", logs);
 ssd1306_printFixed(0, 40, t_text, STYLE_NORMAL);

 DateTime now = rtc.now();
 sprintf(t_text, "%02i.%02i.%04i   %02i:%02i:%02i", now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());
 ssd1306_printFixed(0, 48, t_text, STYLE_NORMAL);
 ssd1306_printFixed(0, 56, "                      ", STYLE_NORMAL);

 uint32_t a = (intervall *1000 - (millis()-lastLog));
 a = map(a, 0, intervall*1000, 127, 0);
 ssd1306_drawHLine(0, 63, a);

 if (millis() - lastLog >= intervall * 1000) {
 lastLog = millis();
 if (voltage < cutOutVoltage)
 digitalWrite(RELAISPIN2, 255);
 else if (voltage > cutOutVoltage + 0.5) digitalWrite(RELAISPIN2, 0);
 String dataString = createString(rtc.now(), voltage, current);
 if (writeToSD(dataString)) {
 Serial.print("nicht geschrieben:");
 led[0].set(-1, -1, -1);
 } else {
 Serial.print("geschrieben: ");
 led[0].set(1, 20, 0);
 }
 Serial.println(dataString);
 }
 }




 for (int i = 0; i < 2; ++i)
 led[i].execute();

}
float readVoltage(int pin, float r1, float r2) {
 float a = 0.0;
 int sum = 0;
 int sample_count = 0;
 while (sample_count < 10) {
 sum += analogRead(pin);
 sample_count++;
 delay(10);
 }

 sum = sum / 10;
 a = (float) sum * VREF / 1024.0;
 a = a / (r2 / (r1 + r2));
 return a;
}

float readCurrent() {
 int32_t a = 1000 * readVoltage(CURRENTPIN, R3, R4);
 a = a - VNULL;
 a = a * 1000 / 66;
 return a;
}

String createString(DateTime now, float voltage, int current) {
 String dataString = String(now.unixtime());
 dataString += ",";
 dataString += String(voltage);
 dataString += ",";
 dataString += String(current);
 dataString += ";";
 return dataString;
}

void readConfig(int a) {
 Serial.print("Initialisiere SD Karte...");
 if (!SD.begin(CSPIN)) {
 Serial.println("ERROR");
 return;
 } else {
 Serial.println("OK");
 File iniFile;
 if (!a) {
 Serial.print("Schreibe Konfiguration...");
 iniFile = SD.open("config.ini", O_WRITE | O_CREAT | O_TRUNC);
 if (iniFile) {
 iniFile.println("1     #Intervall in Sekunden");
 iniFile.println("12.0  #Abschaltspannung");
 iniFile.close();
 Serial.println("OK");
 } else Serial.println("ERROR");
 } else {
 Serial.print("Lade Konfiguration...");
 if (SD.exists("config.ini")) {
 iniFile = SD.open("config.ini", FILE_READ);
 String text = "";
 if (iniFile) {
 text = iniFile.readStringUntil(' ');
 intervall = text.toInt();
 iniFile.readStringUntil('\n');
 text = iniFile.readStringUntil(' ');
 cutOutVoltage = text.toFloat();
 iniFile.close();
 Serial.println("OK");
 Serial.print("Intervall = ");
 Serial.println(intervall);
 Serial.print("Abschaltspannung = ");
 Serial.println(cutOutVoltage);
 }
 } else Serial.println("ERROR");

 }
 }
}

int writeToSD(String dataString) {
 File dataFile = SD.open("data8.txt", FILE_WRITE);

// if the file is available, write to it:
 if (dataFile) {
 Serial.println("file opened");
 dataFile.println(dataString);
 dataFile.close();
 Serial.println("file closed");
 return 0;
 logs++;
 }
// if the file isn't open, pop up an error:
 else {
 Serial.println("can't open file");
 return 1;
 }
}

void Led::set(int count, int onTime, int intervall) {

 this->count = count;
 this->onTime = onTime;
 this->interval = intervall;
 action = 1;
 nextAction = 0;

}

void Led::execute() {
 if ((action) & (millis() >= nextAction)) {
 if ((status) & (count > 0)) {
 count--;
 status = 0;
 if (count > 0)
 nextAction = millis() + interval - onTime;
 else action = 0;
 } else if ((!status) & (count > 0)) {
 status = 1;
 nextAction = millis() + onTime;
 } else if ((!status) & (count < 0)) {
 status = 1;
 action = 0;
 }
 digitalWrite(pin, status);
 }
}

Der Kompiler endet mit:

section                      size      addr
.data                         606   8388864
.text                       26074         0
.bss                         1027   8389470
.comment                       17         0
.note.gnu.avr.deviceinfo       64         0
.debug_aranges                168         0
.debug_info                 57661         0
.debug_abbrev               10626         0
.debug_line                 15772         0
.debug_frame                 3744         0
.debug_str                  11050         0
.debug_loc                  29695         0
.debug_ranges                2192         0
Total                      158696


'Finished building target: Spannungslogger'
' '

Sorry, mein Code ist nicht sehr schön. Zu allem Übel verrutsch die Formatierung wenn ich das hier poste, weil Eclipse Tabulatoren zum einrücken verwendet. Da könnte man vieles sauberer Lösen, int statt float, usw. Ich habe das an einem Abend schnell zusammen geschrieben, mit der Prämisse das ganze schnell zum laufen zu bringen.

Was sagt der Compiler zum Speicherverbrauch?

Lass dir mal das freie RAM regelmäßig ausgeben.

RAM kann man sparen mit dem F() Makro und festen Texten im PROGMEM.

Bei dir dürfte sprintf_P etwas bringen...
Ausserdem musst du sicher sein, nie die 20 Zeichen von char t_text[21]; zu überschreiten.

UNO und String Objekte, warum wundert mich da nichts

Ich benutze die Sloeber IDE, deren Compiler den Speicherverbrauch des Sketch leider nicht so schön ausgibt wie die Arduino IDE.

Laut MemoryFree Lib sind es 259byte bevor er die config.ini in der setup() Funktion öffnet. Und 205byte wenn in der loop() die Log Datei zum schreiben geöffnet werden soll.

Soweit ich glaube zu Wissen, benötigt die Lib dafür 512 Bytes

Zum öffnen einer Datei? OK, gut zu wissen, das ist ja ne ganze Menge. Also wenn es ziemlich sicher am Speicher zu liegen scheint, werde ich mal versuchen zu optimieren. Ich denke ich könnte mir grundsätzlich auch die ganzen floats sparen. Wie gesagt, ich habe den Code sehr schnell zusammen geworfen, und eine float Variable, oder ein String Objekt macht nun mal viel weniger Arbeit es mit int und char arrays zu lösen.

Beim Schreiben wird mehr 'Saft' benötigt - liegts eventuell an der Stromversorgung (hatte solche Problem einmal mit einen Raspberry) zum testen einmal das Display abhängen ?!

Er versucht ja gar nicht erst zu schreiben, weil bereits das Öffnen der Datei nicht erfolgreich ist.

Ich denke auch nicht dass es an der Stromversorgung liegt. Das Display nutzt 3.3V, die SD Karte 5V. Das Display ist so ein winziges 0.5" OLED, so groß wie ein Daumennagel. Ich kenne ehrlich gesagt die Stromaufnahme des Displays nicht, aber in sämtlichen HowTos wurde es direkt vom Arduino versorgt.

Da könnte es helfen, die Stromaufnahme zu messen.
Klar wird es in den Beispielen immer direkt versorgt - da ist es ja auch allein an der Versorgung.

Gruß Tommy

Bei mir ist es ebenfalls alleine an der 3.3V Versorgung, daher sah ich keine Notwendigkeit es zu messen. 15mA zieht es wenn Text angezeigt wird.

Hallo,

das ist zu fiel für den uno. Das Ram reicht nicht. Eigentlich dürfte es gar nicht zusammen funktioniert haben. Ich finde es auch komisch das das Display 3.3 Volt hat und die SD 5V. Meistens erwischt man nämlich eine 3.3 Volt Variante für den Rasperi. Da braucht es mehr infos um das zu ermitteln.

Gruss Temucin

606 + 1027 + (512 für SD) ist mehr Speicher, als der UNO hat.
Dazu noch die dynamischen Strings.

No!


nextAction = millis() + onTime;
Fehler.

nextAction = millis() + interval - onTime;
Das selbe Problem.

Da die beiden vorherigen einen Fehler verursachen, funktioniert auch dieses nicht:

if ((action) & (millis() >= nextAction))

Und hier machst du es richtig:

if (millis() - lastLog >= intervall * 1000)
Mache es doch bitte überall so.

Ich werde versuchen den Speicherbedarf zu senken.
Auf die Stromversorgung komme ich ggf. später zurück.

nextAction = millis() + onTime;
Fehler.

Was ist denn hier der Fehler? Funktioniert hat das ganz prima.

Damit bekommst Du Probleme, wenn millis() überläuft.

Gruß Tommy