Проблема с SD-картой Arduino RTC

Я использую скрипт на Arduino для записи данных BME280 на SD-карту и отображения их на ЖК-дисплее. Чтобы иметь действительную отметку времени, я также использую модуль DS3231 RTC. Я успешно установил время и могу вручную прочитать его и записать на последовательный монитор. Запись данных датчика на SD - карту также работает нормально. Но как только я пытаюсь включить время из RTC в файл на SD-карту, скрипт возвращается к оператору else в setup (), где я пытаюсь открыть файл для записи строки заголовка.

Без использования времени RTC он работает нормально, как только я вставляю его с помощью соответствующей функции, он терпит неудачу.

Вот сценарий, который я использую:

#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); // программное обеспечение 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-карта готова.");
    sd_ok = true;
  } else
  {
    Serial.println("Не удалось инициализировать SD-картуd");
    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);
}

Поэтому, когда я запускаю скрипт без учета времени RTC, он выдает следующий результат:

SD-карта готова.

Хорошо, пишу заголовок

Если я включаю строку dataLog.print(rtcReadTime()); ближе к концу (в закомментированном скрипте) она переходит к началу раздела setup в оператор else, дающий мне:

ошибка открытия testfile.txt

и ничего не записывается в файл на SD-карте.

Вчера я несколько часов пытался найти эту ошибку и надеялся найти ее сегодня после хорошего ночного сна, но нет. Вероятно, это простое решение, которое я не вижу. Код, вероятно, далек от совершенства или хорошего. Я новичок в c++, так что имейте это в виду. :)

ПРАВКА: Поскольку я не смог найти решение с помощью rtcReadTime(), я использовал RTClib.h напрямую и создал время вручную в процессе записи для SD-карты:

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(); 
      }
    }
  }
}

Вероятно, далеко не идеал, но для меня достаточно.

, 👍0


1 ответ


1

метка времени в rtcReadTime () - это локальная переменная. Когда вы возвращаетесь из этой функции, она перестает существовать - так что то, что вы вернули, есть ничто. Вы не можете ничего напечатать, поэтому Arduino падает.

Вам нужно либо сделать переменную timestamp статической, чтобы она не терялась при выходе из функции, либо сделать ее глобальной переменной, которая имеет аналогичный эффект, либо передать переменную буфера в rtcReadTime (), которая затем заполняется вместо того, чтобы возвращать указатель на символ.

Самый простой из них-просто сделать временную метку статичной. Кроме того, вы должны использовать snprintf вместо sprintf, чтобы избежать возможности переполнения буфера:

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;
}
,

Спасибо за ввод, к сожалению, это не работает. Не спрашивай меня, почему, хотя^^, @Kj Ell

Даже вынимание содержимого из функции и помещение его непосредственно в оператор "if(sd_ok)" дает мне тот же результат, @Kj Ell

Кстати. Я могу распечатать время на последовательном мониторе, используя Serial.println(rtcReadTime());. Я бы подумал, что это означает, что возврат переменной из функции работает?, @Kj Ell

Это "работает", потому что у вас есть другие переменные во фрейме стека функции, действующие в качестве буфера для переменной метки времени - последующие функции, которые вы вызываете, не совсем пересекают свои фреймы стека достаточно далеко через этот буфер старых переменных в памяти, чтобы повредить строку. Чисто случайно., @Majenko