Daten auf SD nur einmal schreiben

Hallo in die Runde,
baue gerade ein altes Projekt um wo mehrere Daten werden gespeichert auf SD.
Alt --> Mega, LCD2004, SD und so weiter.
Neu ESP32, SPI TFT...
Gespeichert wurde ganzer Satz daten jede 20 Minuten, was auch funktionierte 1A.
Jetzt habe ich das Problem das die daten werden nicht einmal nur 45 mall geschrieben.
Mit dellay (1000) funktioniert, aber ist nicht Sinn der Sache, irgend wie drehe mich im Kreis.

void SDKarte()
{
  DateTime now = rtc.now();
  bmp280.getMeasurements(temperature, pressure, altitude);
  //float ret = AHT.getSensor(&humi, &temp);
  // if ((now.minute() == 20 && now.second() == 59 ) || (now.minute() == 40
  //     && now.second() == 59) || (now.minute() == 59 && now.second() == 59))
  if ( now.second() == 59)
  {
    myFile = SD.open("esp32.txt", File_Write); // if the file opened okay, write to it:
    if (myFile) {
      Serial.println("Schreibe in esp32.txt...");

      char buf2[40];
      sprintf(buf2, "%.2d.%.2d.%d %.2d:%.2d:%.2d ",
              now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());
      String dataString = (buf2);
      myFile.print(dataString);
      myFile.print(" BMP Temp. ");
      myFile.println(temperature - 0.8, 1);
      Serial.println("Uhrzeit geschrieben  ");
      // Serial.println(i);
      /* myFile.print(" °C   AHT Temp. ");
        myFile.print(temp - 1.1, 1);
        myFile.print(" °C   DS18B20  Temp. ");
        ds.selectNext();
        myFile.print(ds.getTempC() - 0.7, 1);
        myFile.println("  °C");
      */
      //delay(1000);
      myFile.close();  // close the file:
    } else Serial.println("Kann  esp32.txt nicht Öffnen ");
  }
}

Das if ( now.second() == 59) wurde eingebaut um nicht 20 min warten :wink:

if ( now.second() == 59)

Diese Aussage gilt für eine ganze Sekunde, was ausreichend Zeit ist, um mehrere Datensätze auf SD zu schreiben.

In der Sekunde soll der Datensatz nur einmal geschrieben werden, nicht 45 mal.

&& currentSecond != previousSecond

oder eben mit magic numbers

&& previousSecond != 59

Mal sehen ob ich das richtig "aussortiert" habe...:
Du schreibst so lange immer wieder auf die Karte wie ( now.second() == 59) wahr ist. Der Esp ist schnell genug, dass er den ganzen Sketch in einer Minute 45 mal abarbeitet.
Spiel den Sketch auf einen noch schnelleren MC und er schreibt noch häufiger. :wink:

Ich setze das schreiben auf SD oder Display immer in eine eigene Funtion, das finde ich übersichtlicher. Das Anspringen der Funktion erfolgt über eine Millis Abfrage.

void SDKarte()
{
  static bool isWrite = false;
  DateTime now = rtc.now();
  bmp280.getMeasurements(temperature, pressure, altitude);
  //float ret = AHT.getSensor(&humi, &temp);
  // if ((now.minute() == 20 && now.second() == 59 ) || (now.minute() == 40
  //     && now.second() == 59) || (now.minute() == 59 && now.second() == 59))
  if (now.second() == 59)
  {
    if (!isWrite)
    {
      isWrite = true;
      myFile = SD.open("esp32.txt", File_Write); // if the file opened okay, write to it:
      if (myFile)
      {
        Serial.println("Schreibe in esp32.txt...");
        char buf2[40];
        sprintf(buf2, "%.2d.%.2d.%d %.2d:%.2d:%.2d ",
                now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());
        String dataString = (buf2);
        myFile.print(dataString);
        myFile.print(" BMP Temp. ");
        myFile.println(temperature - 0.8, 1);
        Serial.println("Uhrzeit geschrieben  ");
        // Serial.println(i);
        /* myFile.print(" °C   AHT Temp. ");
          myFile.print(temp - 1.1, 1);
          myFile.print(" °C   DS18B20  Temp. ");
          ds.selectNext();
          myFile.print(ds.getTempC() - 0.7, 1);
          myFile.println("  °C");
        */
        myFile.close();  // close the file:
      }
      else
      {
        Serial.println("Kann  esp32.txt nicht Öffnen ");
      }
    }
  }
  else
  { isWrite = false; }
}

Ja, zu zeit dauert ein loop 8 Millisekunden. Wen ich die Webseite aufrufe springt auf 30 und zurück auf 8 ~ 9 milli

#include <WiFi.h>

WiFiServer server(80);
const char * ssid = "FRITZ";
const char * password = "019xxxxxxxxxxxx";
#include <ArduinoOTA.h>
#include <OneWire.h>

#include <SPI.h>
#include <mySD.h>          //SPI "https://github.com/nhatuan84/esp32-micro-sdcard"
ext::File myFile;
//const byte chipSelect = 5;//SD CS

#include <TFT_eSPI.h> // Hardware-specific library
TFT_eSPI tft = TFT_eSPI();
//#include "Free_Fonts.h"

#include <RTClib.h>       //I2C https://github.com/adafruit/RTClib
RTC_DS3231 rtc;
const char wochenTag[7][4] = {"So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."};

#include <BMP280_DEV.h>  //I2C https://github.com/MartinL1/BMP280_DEV
float temperature, pressure, altitude;
BMP280_DEV bmp280;


#include "AHT20.h"       //I2C https://github.com/Seeed-Studio/Seeed_Arduino_AHT20
AHT20 AHT;
float humi, temp;


#include <DS18B20.h>      //https://github.com/matmunk/DS18B20
DS18B20 ds(13);

//const byte tftLicht = 2;

unsigned long previousMillis = 0;
const long intervall = 2000;
unsigned long startMilli;
unsigned long endMilli;
byte auswahl;


/*
#include <ModbusMaster.h>
#define MAX485_DE 2 
#define MAX485_RE_NEG  2 
// instantiate ModbusMaster object
ModbusMaster node;
void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1);
  digitalWrite(MAX485_DE, 1);
}
void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
}
*/


void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  delay(500);
  server.begin();
  ArduinoOTA.begin();
//  pinMode(tftLicht, OUTPUT);
//  digitalWrite(tftLicht, HIGH);

  bmp280.begin(BMP280_I2C_ALT_ADDR);
  bmp280.setIIRFilter(IIR_FILTER_OFF);              // Set the IIR filter to setting 4
  bmp280.setTimeStandby(TIME_STANDBY_500MS);      // Set the standby time to 2 seconds
  bmp280.startNormalConversion();
  
  AHT.begin();
  // Serial.println("AHT");


  pinMode(SS, OUTPUT);
  if (!SD.begin(26, 14, 13, 27)) {
    Serial.println("initialization failed!");
    // return;
  }
  Serial.println("initialization done.");

  tft.begin();
  tft.setRotation(1);
  //tft.clear();
  tft.fillScreen(0xBDF7);
  //tft.fillScreen(0x867D);
  //tft.drawRect(0, 0, 309, 60, TFT_BLUE); 
  //tft.drawRect(1, 1, 308, 61, TFT_BLUE);
  tft.fillRoundRect(6, 10, 307, 49,8, TFT_YELLOW);//x,y,B,H,R
  rtc.begin();
}

void loop() {
  startMilli = millis();
  ArduinoOTA.handle();
  WiFiClient client = server.available();
  datum();
  SDKarte();
  web();
  temperatur();
  endMilli = millis();
  //Serial.println(endMilli - startMilli);

}

Ok, ich werd nicht so richtig schlau draus.
Wie oft möchtest Du auf die SD-Karte wann schreiben?
1x sekunde? 1x Minute oder 1x Stunde?

Wie ich es verstehe : er will alle 20 Minuten 1x schreiben, hat den Sketch aber verändert damit wir nicht 20 Minuten warten müssen.

so wie Auskommenteit ein mall alle 20min, also 3mall in der Stunde nur ein Satz mit mehreren Daten , werde Nachmittag dein Vorschlag testen.
Muss zugeben das mir die Möglichkeit mit Status habe vergessen, na ja was solls :wink:
Muss jetzt dringend weg.

void SDKarte()
{
  static bool isWrite = false;
  DateTime now = rtc.now();
  bmp280.getMeasurements(temperature, pressure, altitude);
  //float ret = AHT.getSensor(&humi, &temp);
  if (now.minute() == 0 ||
      now.minute() == 20 ||
      now.minute() == 40)
  {
    if (!isWrite)
    {
      isWrite = true;
      myFile = SD.open("esp32.txt", File_Write); // if the file opened okay, write to it:
      if (myFile)
      {
        Serial.println("Schreibe in esp32.txt...");
        char buf2[40];
        sprintf(buf2, "%.2d.%.2d.%d %.2d:%.2d:%.2d ",
                now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());
        String dataString = (buf2);
        myFile.print(dataString);
        myFile.print(" BMP Temp. ");
        myFile.println(temperature - 0.8, 1);
        Serial.println("Uhrzeit geschrieben  ");
        // Serial.println(i);
        /* myFile.print(" °C   AHT Temp. ");
          myFile.print(temp - 1.1, 1);
          myFile.print(" °C   DS18B20  Temp. ");
          ds.selectNext();
          myFile.print(ds.getTempC() - 0.7, 1);
          myFile.println("  °C");
        */
        myFile.close();  // close the file:
      }
      else
      {
        Serial.println("Kann  esp32.txt nicht Öffnen ");
      }
    }
  }
  else
  { isWrite = false; }
}

Ich hab den Kommentar übersehen gehabt....

@my_xy_projekt
Sicher ? Halbierst Du damit die Anzahl der Schreibvorgänge nicht nur ?

Ablauf:

Sprung in die Funktion
isWrite ist false, also schreiben und iswrite auf true setzen
Funktion verlassen
Sprung in die Funktion
isWrite ist true, nicht schreiben und iswrite auf false setzen
Funktion verlassen

dann beginnt das wieder von vorne

Oder habe ich was übersehen ? (Muss jetzt aber auch erst mal los...)

Die Variable verliert den Inhalt nicht beim verlassen der Funktion.
Aber wenn die Zeit != 0, 20 oder 40 ist:

1 Like

... ist Mist, wenn du erreichen willst, dass etwas nur 1 Mal passiert.
Warum machst du es nicht wie das bewährte BlinkWithoutDelay und merkst dir, wann du das letzte Mal geschrieben hast? Frühestens x Sekunden später UND zur Synchronisierzeit gehts wieder los...

Die 59 ist ja nur damit ich nicht 20 min warten muss, nicht nur vorletzten Beitrag lesen :wink:
und ja so schlau war ich auch habe die zeit auf 100milisek. gesetzt hat nur statt 45, 10 mall geschrieben. Die loop dauert zu zeit ~8 milli, wen Modbus komme vielleicht auf 20 wegen Serial.
Die Lib für TFT ist sau schnell 10 mall schneller als die vom Oli Kraus, bei der dauert die loop 220 milli , bei Mega dauert es fast 0,7 sek ein durchlauf mit LCD als anzeige.
Dadurch wurde immer nur ein mall auf die SD geschrieben

Danke!!! einziges was musste ändern ist

 if (now.minute() == 0 ||
      now.minute() == 20 ||
      now.minute() == 40)
  {
    if (!isWrite)
    {
      isWrite = true;
      myFile = SD.open("esp32.txt", File_Write); // if the file opened okay, write to it:
      if (myFile)
      {
        Serial.println("Schreibe in esp32.txt...");
        char buf2[40];

in

 if (now.minute() == 0 ||
      now.minute() == 20 ||
      now.minute() == 40)
  {
   myFile = SD.open("esp32.txt", File_Write); // if the file opened okay, write to it:
    if (!isWrite)
    {
      isWrite = true;
   
      if (myFile)
      {
        Serial.println("Schreibe in esp32.txt...");
        char buf2[40];
type or paste code here

Die Datei muss offen sein vor der status Abfrage, sonst kann die nicht geöffnet werden.
Nur für die Änderung braucht nicht ganzen Tag :wink:
Hatte erst mall das Problem die SD zum laufen bringen, hat sich nicht vertragen mit Display auf dem gleichem SPI Port(VSPI), bin ausgeweicht auf zweiten SPI (HSPI).
Komischer weise mit der TFT Lib vom Oli hat es funktioniert auf dem gleichem Port, ist egal es 'Läuft'.
Danke!!!

PS. Bevor jemand fragt, die CS Pins wahren und sind andere :wink:

1 Like

Irgendwo sollte isWrite auch wieder false gesetzt werden :stuck_out_tongue:
z.B. in einem else-Zweig zu if (now.minute() == 0 ...

Ob du SD.open wirklich so oft machen willst, weiß ich nicht.
myFile.close() sollte möglichst auch irgendwo vorkommen.

Also alle 20min einmal öffnen sehe ich nicht als viel, und noch mall

if ((now.minute() == 20 && now.second() == 59 ) || (now.minute() == 40
       && now.second() == 59) || (now.minute() == 59 && now.second() == 59))

und nicht jede Minute.
Also wenn funktioniert ohne das der ESP32 rummotzt ist doch OK oder?
Und wen du das myFile.close() in #11 nicht siechst kann ich nix da für

Wird bei Dir der loop nur 1 Mal pro Sekunde abgearbeitet?

Gruß Tommy

Alle 20 Minuten versuchst du unendlich oft die Datei zu öffnen, bis die jeweilige Minute (in #16) rum ist.
Das gelingt dir zwar nicht, ist aber unschön, finde ich.