0

I am using a script on an Arduino to record BME280 data onto an SD card and to display it on a lcd display. To have a valid time stamp I am also using a DS3231 RTC module. I set the time succesfully and can manually read it and write it to the serial monitor. Writing the sensor data to the SD card works fine as well. But as soon as I try to include the time from the RTC into the file onto the SD card the script jumps back to the else statement in the setup() where I try to open the file for writing the header line.

Without using the RTC time it works fine, as soon as I put it in using the corresponding function, it fails.

Here is the script I am using:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h> 
#include <SPI.h>
#include <SD.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define BME_SCK 8
#define BME_MISO 6
#define BME_MOSI 7
#define BME_CS 5

#define SEALEVELPRESSURE_HPA (1013.25)
#define RTC_I2C_ADDRESS 0x68 // I2C Adresse des RTC  DS3231

Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI

unsigned long lastReadRTC = -1;
boolean sd_ok = 0, sensor_ok = 0;
File dataLog;
String dat_time;

LiquidCrystal_I2C lcd(0x27, 20, 4);  //Hier wird das Display benannt (Adresse/Zeichen pro Zeile/Anzahl Zeilen)

void setup()
{
  Wire.begin(); //Kommunikation über die Wire.h bibliothek beginnen.
  Serial.begin(9600); // open serial communications and wait for port to open  
  // SD Card Initialization
  if (SD.begin())
  {
    Serial.println("SD card is ready.");
    sd_ok = true;
  } else
  {
    Serial.println("SD card initialization failed");
    return;
  }
  //if( SD.exists("test.txt") == 1 ){
  dataLog = SD.open("test.txt", FILE_WRITE);
  if(dataLog) {                               // if the file opened okay, write to it:
     Serial.println("OK, writing header");
     // write some texts to 'test.txt' file
     dataLog.println("DATE | TEMPERATURE | HUMIDITY | PRESSURE");
     dataLog.close();   // close the file
  }
  else {
    Serial.println("error opening testfile.txt");
  }
  unsigned status;
  status = bme.begin();  
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
    Serial.print("SensorID was: 0x"); 
    Serial.println(bme.sensorID(),16);
    Serial.print("        ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
    Serial.print("   ID of 0x56-0x58 represents a BMP 280,\n");
    Serial.print("        ID of 0x60 represents a BME 280.\n");
    Serial.print("        ID of 0x61 represents a BME 680.\n");
    while (1) delay(10);
  }else{
    sensor_ok = true;
    Serial.println("OK, sensor recognized");
  }
  lcd.init(); 
  lcd.backlight(); 
  lcd.setCursor(0,0); 
  lcd.print("Temp. = ");
  lcd.print(bme.readTemperature());
  lcd.print(" *C");
  lcd.setCursor(0,1); 
  lcd.print("Press. = ");
  lcd.print(bme.readPressure() / 100.0F);
  lcd.print(" hPa");
  lcd.setCursor(0,2);
  lcd.print("Alti. = ");
  lcd.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
  lcd.print(" m");
  lcd.setCursor(0,3);
  lcd.print("Hum. = ");
  lcd.print(bme.readHumidity());
  lcd.print(" %");
  
  delay(1000);
}

//Reads the current datetime from rtc module
String rtcReadTime(){
  Wire.beginTransmission(RTC_I2C_ADDRESS); //Connect to address 0x68
  Wire.write(0);
  Wire.endTransmission();
  Wire.requestFrom(RTC_I2C_ADDRESS, 7);
  int sekunde    = bcdToDec(Wire.read() & 0x7f);
  int minute     = bcdToDec(Wire.read()); 
  int stunde     = bcdToDec(Wire.read() & 0x3f); 
  //weekday not included
  /* wochentag  =*/ bcdToDec(Wire.read());
  int tag        = bcdToDec(Wire.read());
  int monat      = bcdToDec(Wire.read());
  int jahr       = bcdToDec(Wire.read())+2000;  
 
  char timestamp[30];
  sprintf(timestamp,"%02d.%02d.%4d %02d:%02d:%02d",tag,monat,jahr,stunde,minute,sekunde);
  return timestamp;
}

//Converts binary to numeric
byte bcdToDec(byte val){
  return ( (val/16*10) + (val%16) );
}

void loop()
{
  unsigned long currentMilliseconds = millis(); 
  if((lastReadRTC + 1000) < currentMilliseconds){
    lastReadRTC = currentMilliseconds;
    lcd.setCursor(0,0);
    lcd.print("Temp. = ");
    lcd.print(bme.readTemperature());
    lcd.print(" *C");
    lcd.setCursor(0,1);
    lcd.print("Press. = ");
    lcd.print(bme.readPressure() / 100.0F);
    lcd.print(" hPa");
    lcd.setCursor(0,2);
    lcd.print("Alti. = ");
    lcd.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    lcd.print(" m");
    lcd.setCursor(0,3);
    lcd.print("Hum. = ");
    lcd.print(bme.readHumidity());
    lcd.print(" %");
    //Serial.println(rtcReadTime());
    if(sd_ok){ 
      dataLog = SD.open("test.txt", FILE_WRITE);
      //dataLog.print(rtcReadTime());
      dataLog.print(" |  ");
      if(sensor_ok) {
        dataLog.print(bme.readTemperature());
        dataLog.print("°C   |  ");
        dataLog.print(bme.readHumidity());
        dataLog.print("  | ");
        dataLog.println(bme.readPressure() / 100.0F);
        dataLog.close(); 
      }
      else{
        dataLog.println("  Error    |  Error   |  Error");
        dataLog.close(); 
      }
    }
  }
  delay(1000);
}

So when I run the script without the RTC time included it gives me the output:

SD card is ready.

OK, writing header

If I include the line dataLog.print(rtcReadTime()); towards the end (in the script commented out) it jumps to the beginning of the setup section into the else statement giving me:

error opening testfile.txt

and nothing is written into the file on the SD card.

I tried to find the mistake for multiple hours yesterday and was hoping to find it today after a good night of sleep, but nope. It is probably an easy fix that i can't see. The code is probably far from perfect or good. I am a beginner in c++, so keep that in mind. :)

EDIT: As I was not able to find a solution using rtcReadTime(), I used the RTClib.h directly and created the time manually in the writing process for the SD card:

void loop()
{
  unsigned long currentMilliseconds = millis(); 
  if((lastReadRTC + 1000) < currentMilliseconds){
    lastReadRTC = currentMilliseconds;
    lcd.setCursor(0,0);
    lcd.print("Temp. = ");
    lcd.print(bme.readTemperature());
    lcd.print(" *C");
    lcd.setCursor(0,1);
    lcd.print("Press. = ");
    lcd.print(bme.readPressure() / 100.0F);
    lcd.print(" hPa");
    lcd.setCursor(0,2);
    lcd.print("Alti. = ");
    lcd.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    lcd.print(" m");
    lcd.setCursor(0,3);
    lcd.print("Hum. = ");
    lcd.print(bme.readHumidity());
    lcd.print(" %");
    if(sd_ok){ 
      dataLog = SD.open("test.txt", FILE_WRITE);
      DateTime now = rtc.now();
      dataLog.print(now.day(), DEC);
      dataLog.print(' ');
      dataLog.print(now.month(), DEC);
      dataLog.print(' ');
      dataLog.print(now.year(), DEC);
      dataLog.print(" ");
      dataLog.print(now.hour(), DEC);
      dataLog.print(':');
      dataLog.print(now.minute(), DEC);
      dataLog.print(':');
      dataLog.print(now.second(), DEC);
      dataLog.print(" |  ");
      if(sensor_ok) {
        dataLog.print(bme.readTemperature());
        dataLog.print("°C   |  ");
        dataLog.print(bme.readHumidity());
        dataLog.print("  | ");
        dataLog.println(bme.readPressure() / 100.0F);
        dataLog.close(); 
      }
      else{
        dataLog.println("  Error    |  Error   |  Error");
        dataLog.close(); 
      }
    }
  }
}

Probably far from ideal, but enough for me.

1 Answer 1

1

timestamp in rtcReadTime() is a local variable. When you return from that function it ceases to exist - so what you have returned is nothing. You can't print nothing, so the Arduino crashes.

You either need to make the timestamp variable static so that it's not lost when you leave the function, make it a global variable, which has a similar effect, or pass a buffer variable to rtcReadTime() which is then filled instead of returning a pointer to a char.

The simplest of these is just to make timestamp static. Also you should use snprintf instead of sprintf to avoid the possibility of buffer overflows:

String rtcReadTime(){
  Wire.beginTransmission(RTC_I2C_ADDRESS); //Connect to address 0x68
  Wire.write(0);
  Wire.endTransmission();
  Wire.requestFrom(RTC_I2C_ADDRESS, 7);
  int sekunde    = bcdToDec(Wire.read() & 0x7f);
  int minute     = bcdToDec(Wire.read()); 
  int stunde     = bcdToDec(Wire.read() & 0x3f); 
  //weekday not included
  /* wochentag  =*/ bcdToDec(Wire.read());
  int tag        = bcdToDec(Wire.read());
  int monat      = bcdToDec(Wire.read());
  int jahr       = bcdToDec(Wire.read())+2000;  
 
  static char timestamp[30];
  snprintf(timestamp, 30, "%02d.%02d.%4d %02d:%02d:%02d",tag,monat,jahr,stunde,minute,sekunde);
  return timestamp;
}
4
  • Thx for the input, sadly that doesn't work. Don't ask me why though^^
    – Kj Ell
    Apr 4, 2021 at 11:22
  • Even taking the content out of the function and put it directly into the if(sd_ok)-statement gives me the same result
    – Kj Ell
    Apr 4, 2021 at 11:36
  • Btw. I can print the time to the serial monitor by using Serial.println(rtcReadTime());. I would have thought that means returning the variable from the function does work?
    – Kj Ell
    Apr 4, 2021 at 12:06
  • 1
    It "works" because you have other variables in the function's stack frame acting as a buffer to the timestamp variable - subsequent functions that you call happen to not quite encroach their stack frames far enough through that buffer of old variables in memory to corrupt the string. Purely by chance.
    – Majenko
    Apr 4, 2021 at 14:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.