Problema con el guardado de datos en una estación meteorológica

Muy buenas. Necesito ayuda, porqué después de realizar el código de programado de una estación meteorológica (incluye sensores de temperatura, presión, una placa fotovoltaica, y un anemómetro que mide a través de pulsos), me he encontrado con dos principales problemas a la hora de guardar los datos.
El código cada 10 segundos toma datos de cada una de las sondas, y a los 2 minutos, calcula un promedio, que escribe en la tarjeta SD junto con la hora y fecha de la toma de los datos, o eso es lo que debería hacer.
A través del monitor serial puedo ver como todas las sondas están recopilando información correctamente, pero cuando toca hacer el guardado de datos, se presenta un problema: que en la tarjeta solo hay impreso el nombre del fichero (que es el mes, día, hora y minuto.txt), y nada más, ninguno de los datos que he mandado que se guardaran.
El segundo problema es que de vez en cuando, el reloj no toma bien la hora, y marca que nos encontramos en el día 1 de Enero del 2000, y a pesar de que reviso que el cableado esté dispuesto correctamente, se corrige aparentemente de manera aleatoria.
Hay que tener en cuenta que en el código cada 50 guardados de información en la tarjeta SD, hago que la placa Arduino se resetee y cree un nuevo archivo de datos, como medida desesperada para ver si puedo salvar algo...
Alguien podría ayudarme, ¿por favor?
Muchísimas gracias,

El código es el siguiente:

#include <SD.h> //Incluimos la librería para trabajar con tarjetas SD
#include <SPI.h> //Incluimos la librería para la comunicación SPI
#include <Wire.h>
#include <RTClib.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_BMP085.h>

RTC_DS3231 rtc;
Adafruit_BMP085 bmp;

const int csPin = 53; //Pin 53 en MEGA2560. 4 o 9 en Arduino UNO. ¡No conectar NADA al pin 10!!!
const int misoPin = 50; //MISO pin para MEGA256
const int placaPin = A1; //Pin analógico para la lectura de la tensión de la placa
const int tempPin = 2;
const int anemPin = 3; //Pin digital para contar los pulsos que envía el anemometro
const int greenLEDPin=A3;
const int redLEDPin=A2;
  //Rojo: 5 seg encendido si no inicia la SD en el setup;  
  //Rojo o Verde: 3 parpadeos al iniciar la tarjeta en el setup y crear el fichero de txtFilename.txt; 
  //Verde: Un parpadeo de 150ms después de cada lectura a los 10 segundos
  //Rojo o Verde: 1 parpadeo de 2 segundos depués de grabar el promedio en la tarjeta; 
  //Rojo y Verde 2 segundos ON después de xx iteraciones, antes de resetear la tarjeta y crear un nuevo TXT
int txtLinesCounter = 0;

///////PARÁMETROS AJUSTABLES POR EL PROGRAMADOR///////
const int recordTime = 10000; //Tiempo para que se repitan las mediciones en milisegundos (10 segundos = 10000)
const int averageTime = 120000; //Tiempo para que se hagan los promedios en milisegundos (2 minutos = 120000)
const int anemTime = 5; //Intérvalo de muestreo del anemómetro en segundos
//const int delayTime = 2000; //Delay de descanso después de las repeticiones if (Esto ha sido puesto para que cuando no se cumplan los tiempos de mediciones la placa 'descanse' antes de continuar)
const int txtMaxLines = 50;

//Variable para almacenar los datos como string
String dataStringHora;
String dataStringPlaca;
String dataStringTemp;
String dataStringPres;
String dataStringTemp2;
String dataStringAnem;
String fecha;
String hora;

char txtFilename[13];  // Nombre del fichero TXT que se crea después de xx lecturas.
char dateString[22];  // Variable para almacenar los datos de fecha como string

//Variables Placa Fotovoltaica
float v1 = 0;
float mA = 0;
float totalmVolt = 0;
float totalmAmp = 0;
float averagemVolt = 0;
float averagemAmp = 0;
//Variables Sensor Temperatura
OneWire oneWire(tempPin);
DallasTemperature sensors(&oneWire);
float tempCelsius = 0;
float totalTemp = 0;
float averageTemp = 0;
//Variables sonda barométrica
float presion = 0;
float totalPres = 0;
float averagePres = 0;
//Temperatura sonda bar
float temp2 = 0;
float totalTemp2 = 0;
float averageTemp2 = 0;

//Variables Anemómetro
int interruptCounter = 0;
float windSpeed = 0;
float totalWindSpeed = 0;
float averageWindSpeed = 0;

//Variables de la función millis()
unsigned long currentMillis = 0;
int measures = 0;
unsigned long lastMeasurementTime = 0;
unsigned long lastAverageTime = 0;

File dataFile = SD.open(txtFilename, FILE_WRITE);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  sensors.begin();
  Wire.begin();

  //Definición de pines:
  pinMode(placaPin, INPUT);
  pinMode(greenLEDPin,OUTPUT);
  pinMode(anemPin, INPUT_PULLUP);
  pinMode(redLEDPin,OUTPUT);
  pinMode(csPin, OUTPUT); //Para evitar problemas en la SD lo definimos como output
  pinMode(misoPin, OUTPUT); //Para evitar problemas en la SD lo definimos como output
  pinMode(10, OUTPUT); //Para evitar problemas en la SD lo definimos como output y no conectamos nada a el PIN 10 (Paul McWorther Lesson 21)

  //Comunicación con el reloj RTC
  if (!rtc.begin()) {
    Serial.println (F("Error al iniciar el reloj RTC_DS3231"));
    showErrorAndReset();
  }
  Serial.println("Reloj RTC inicializado");

  //Comunicación con la SD
  if (!SD.begin(csPin)) {
    Serial.println (F("Error al iniciar la tarjeta SD"));
    showErrorAndReset();
  } else {
    Serial.println("Tarjeta SD inicializada");
    createFilename();
    createDataFile();
  }

  //Comunicación con la sonda barométrica
  if (!bmp.begin()) {
	  Serial.println (F("Error al iniciar la sonda barométrica"));
    showErrorAndReset();
	}
  Serial.println("Sonda barométrica inicializada");
}

void loop() {
  // put your main code here, to run repeatedly:
  currentMillis = millis(); //Cada vez que se resetee el loop, currentMillis será igual a millis(), por lo tanto, igual al tiempo desde que se inició el código.
  //Cuando llegue a 50 líneas escritas en la SD, la tarjeta Arduino se reiniciará.
  if (txtLinesCounter >= txtMaxLines) {
    resetBoard();

  } else {
    //Cada 10 segundos se realizan mediciones de todas las sondas:
    if (currentMillis - lastMeasurementTime >= recordTime) {
      lastMeasurementTime = currentMillis;

      //Medición de la placa solar:
      meassureVolt();
      //Impresión de los resultados en el Serial Monitor
      Serial.print("Tensión en mV: ");
      Serial.println(v1);
      Serial.print("Corriente en mA: ");
      Serial.println(mA);
      //Guardado de la información en una variable
      totalmAmp += mA;
      totalmVolt += v1;

      //Medición del sensor de temperatura:
      meassureTemp();
      //Impresión de los resultados en el Serial Monitor
      //SE IMPRIME EN EL VOID...
      //Guardado de la información en una variable
      totalTemp += tempCelsius;

      //Medición de la sonda barométrica:
      meassurePres();
      //Impresión de los resultados en el Serial Monitor
      Serial.print("Presión sonda: ");
      Serial.print(presion);
      Serial.println(" Pa");

      Serial.print("Temperatura sonda bar: ");
      Serial.print(temp2);
      Serial.println(" *C");
      //Guardado de la información en una variable
      totalPres += presion;
      totalTemp2 += temp2;

      //Medición del anemómetro:
      meassureWindSpeed();
      //Impresión de los resultados en el Serial Monitor
      Serial.print("Velocidad del viento en km/h: ");
      Serial.println(windSpeed);
      Serial.print("Velocidad del viento en m/S: ");
      Serial.println((float)windSpeed / 3.6);
      //Guardado de la información en una variable
      totalWindSpeed += windSpeed;
      Serial.println(" ");
      //Al acabar de medir en todas las sondas, se suma +1 medición
      measures++;
    }
    //Cada 2 minutos se calcula un promedio de los datos que se han ido almacenando cada 10 segundos
    if (currentMillis - lastAverageTime >= averageTime) {
      lastAverageTime = currentMillis;

      //Cálculo de promedios:
      calcularPromedio();

      //Tomar la hora:
      reloj();

      //Imprimir datos (promedio + hora) en el monitor serial y SD:
      imprimirSD();

    }
  }
}

void meassureVolt(){
  v1 = analogRead(placaPin) * 5.0 / 1023.0 * 1000.0;
  mA = v1 / 100;
}

void meassureTemp(){
  sensors.requestTemperatures(); //Request temperature readings
  tempCelsius = sensors.getTempCByIndex(0); //Get temperature from the sensor in Celsius
  //Check if temperature reading is valid (DS18B20 returns -127°C when reading is not available)
  if (tempCelsius != -127.00) {
    Serial.print("TemperaturaPanel: ");
    Serial.print(tempCelsius);
    Serial.println(" *C");
  } else {
    Serial.println("Error reading temperature!");
  }
}

void meassurePres(){
  presion = bmp.readPressure();
  temp2 = bmp.readTemperature();
}

void meassureWindSpeed(){
  interruptCounter = 0;
  attachInterrupt(digitalPinToInterrupt(anemPin), countup, RISING);
  delay(anemTime * 1000); //El muestreo de velocidad del viento se realiza durante 5 segundos
  detachInterrupt(digitalPinToInterrupt(anemPin));
  windSpeed = (float)interruptCounter / (float)anemTime * 1.2; //Factor 1.2 calculat empíricament
}

void countup() {
  interruptCounter++;
}

void calcularPromedio() {
  //Cálculo promedio de mVolt y mAmp
  averagemAmp = totalmAmp / measures;
  averagemVolt = totalmVolt / measures;
  //Cálculo promedio de temperatura:
  averageTemp = totalTemp/ measures;
  //Cálculo promedio de presión:
  averagePres = totalPres/ measures;
  averageTemp2 = totalTemp2/ measures;
  //Cálculo promedio de la velocidad del viento:
  averageWindSpeed = totalWindSpeed/ measures;
}

void imprimirSD () {
  //Creación de los strings que irán en la SD:
  dataStringHora = fecha + hora;
  dataStringPlaca = "mVolt promedio: ;" + String(averagemVolt) + "; mAmp promedio: ;" + String(averagemAmp) +";";
  dataStringTemp = "Temperatura promedio placa: ;" + String(averageTemp) + "; *C;";
  dataStringPres = "Presion promedio: ;" + String(averagePres) + "; Pa;"; 
  dataStringTemp2 = "Temperatura promedio tejado: ;" + String(averageTemp2) + "; *C;";
  dataStringAnem = "Velocidad promedio viento: ;" + String(averageWindSpeed) + "; km/h;";

  //Almacenamiento del string creado dentro de la SD:
  File dataFile = SD.open(txtFilename, FILE_WRITE);
  if (dataFile) {
    dataFile.print(dataStringHora);
    dataFile.print(dataStringPlaca);
    dataFile.print(dataStringTemp);
    dataFile.print(dataStringPres);
    dataFile.print(dataStringTemp2);
    dataFile.println(dataStringAnem);
    blinkLED(greenLEDPin, 3, 750);
    dataFile.close();
    
    //Impresión de lo guardado en la SD en el monitor serial
    Serial.println(dataStringHora);
    Serial.println(dataStringPlaca);
    Serial.println(dataStringTemp);
    Serial.println(dataStringPres);
    Serial.println(dataStringTemp2);
    Serial.println(dataStringAnem);
    Serial.println("Datos almacenados correctamente.");
    Serial.println(" ");

    txtLinesCounter++;
    Serial.print("Contador de lineas en Fichero TXT: ");
    Serial.println(txtLinesCounter);
    Serial.println(" ");

  } else {
    Serial.println("Error al abrir el archivo.");
    showErrorAndReset();
  }
}

void reloj() {
  DateTime now = rtc.now();
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  //rtc.adjust(DateTime(2023, 9, 11, 14, 58, 20));

  //Obtención del día de la semana
  String diaSemana;
  switch (now.dayOfTheWeek()) {
    case 1: diaSemana = "Lunes"; break;
    case 2: diaSemana = "Martes"; break;
    case 3: diaSemana = "Miercoles"; break;
    case 4: diaSemana = "Jueves"; break;
    case 5: diaSemana = "Viernes"; break;
    case 6: diaSemana = "Sabado"; break;
    case 7: diaSemana = "Domingo"; break;
    default: diaSemana = "Desconocido"; break;
  }

  //Formato de fecha: Día de la semana, Día/Mes/Año
  fecha = diaSemana + ";" + String(now.day()) + "/" + String(now.month()) + "/" + String(now.year()) + " ";

  // Formato de hora: Hora:Minuto:Segundo
  hora = String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second()) + ";";

}

void createFilename() {
  DateTime now = rtc.now();
  snprintf(txtFilename, sizeof(txtFilename), "%02d%02d%02d%02d.txt",
           now.month(), now.day(), now.hour(), now.minute());
  Serial.print("Filename: ");
  Serial.println(txtFilename);
} //cierre void createFilename

void createDataFile() {
  File dataFile = SD.open(txtFilename, FILE_WRITE);
  if (dataFile) {
    dataFile.println(txtFilename);
    dataFile.close();
    blinkLED(greenLEDPin, 3, 150);
    //Serial.println(txtFilename);
    //Serial.println("txtFilename grabado en TXT")); //(F()) guarda en la memoria flash en vez de la RAM y ahorra memoria. Solo sirve para MENSAJES DE TEXTO O VARIABLES FIJAS
  } else {
    blinkLED(redLEDPin, 3, 150);
    Serial.println(F("Error grabando txtFilename en TXT"));
  }
} //cierre createDataFile

void resetBoard() {
  Serial.println("Número máximo de líneas en TXT alcanzado. Reseteando tarjeta: ");
  blinkLED(greenLEDPin, 1, 2000);
  blinkLED(redLEDPin, 1, 2000);
  asm volatile ("  jmp 0");   // Software Reset
} //cierre void resetBoard

void showErrorAndReset() {
  blinkLED(redLEDPin, 1, 5000);
  asm volatile ("  jmp 0");
} //cierre void showErrorAndReset

void blinkLED(int pin, int count, int delayMs) {
  for (int i = 0; i < count; i++) {
    digitalWrite(pin, HIGH);
    delay(delayMs);
    digitalWrite(pin, LOW);
    delay(delayMs);
  }
} //corchete cierre blinkLED

A lo de la SD dejo a alguien con mas practica que yo que te conteste.
En cuanto a la inestabilidad del reloj:

¿De vez en cuando?. No, cada 2 minutos. Cada vez que llamas a la función " reloj () " · ejecutas la instrucción :

 rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

Que lo que hace es poner en hora al RTC a la hora en la que se efectuó la ultima compilación.
Esta instrucción Ha de ir en el "setup" para que se ejecute una sola vez, haces una primera compilación para poner al RTC en hora e inmediatamente comentas la línea y haces una segunda compilación.
De esta forma en la segunda compilación no hay nada que retoque la hora del RTC y el solo te mantendrá la hora correcta actualizada.

Gracias, eso es un error, ya que me dejé esa línea por comentar antes de subir el código, lo siento. Igualmente, te agradezco el comentario, ya que me ha dado una idea. Creo que en los momentos que daba error era porqué no había hecho la segunda compilación del código al aplicar los cambios que fuesen, y al desconectar la placa del ordenador, el reloj se reseteaba.

Actualización. Podría ser que cada 2 minutos no se calcularan promedios porque la variable está declarada como const int , y no como unsigned long?

unsigned long lastAverageTime = 0;

File dataFile = SD.open(txtFilename, FILE_WRITE); 

void setup() { 

debería ser

unsigned long lastAverageTime = 0;

File dataFile; 

void setup() { 

Otra cosita:
cambia el case de 7 a 0 en esta línea:

case 7: diaSemana = "Domingo"; break;

El domingo es 0 y no 7...

Has probado el básico de SD para descartar problemas de conexionado?
Saludos.

Gracias, pero realmente tengo que decir que me marcaba el nombre correcto de la semana cuando ponía el domingo en el 7 y no en el 0, ya lo puse así al principio, pero algo raro pasa que no lo identifica así...
De todos modos, el código básico de la SD lo he probado varias veces, y siempre funciona todo correctamente, el fallo ocurre más en el código grande, donde lo único que imprime es el título del documento, y a partir de ahí, lo demás no se guarda.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.