Escribir nombre archivo según los anteriores en la SD

Buenas, soy nuevo en el foro y en el mundo de Arduino, ya que llevo un par de meses nada más, bueno, la cosa es que quiero hacer que cuando se genere un archivo .txt en la SD que primero detecte si hay uno anterior y si lo hay que genere el siguiente nombre seguido del número superior, por ejemplo; si una archivo que se llama test0001.txt, que el siguiente sea test0002.txt, me he pasado dos días dándole vueltas a la programación y a Internet y no encuentro nada o no he sabido buscar bien.

void sd_card_create(){
  Serial.println("Opened");
  Serial.println("SES(num).txt");

  
  while(exist ==0){
    if (SD.exists("SES(num).txt")){
      Serial.println("SES(num).txt exists");
      num++;
    }
    else{
      exist = 1;
    }
  }
  if(exist == 1){
    myFile = SD.open("SES(num).txt", FILE_WRITE);
    if(myFile){
    Serial.println("Writing File...");
    myFile.println("Probando...");
    myFile.close();
    Serial.println("Done");
    }    
  }
}

Y otra pregunta es, si cuando se empieza a escribir en el bloc de notas, se puede sobrescribir sobre un determinado comando, por ejemplo,estoy haciendo un lap timer y cuando se actualice tanto el mejor tiempo, el tiempo total y/o el tiempo parcial, se actualicen.

Mejor vuelta = 00:00:000 Tiempo total = 00:00:00:000 Tiempo medio: 00:00:000

Vuelta 1 = 00:00:000

Vuelta 2 = 00:00:000

Y así sucesivamente

No se si me habré explicado bien, espero que me hayáis podido entender.

Saludos y gracias de antemano.

Bueno, la primera parte ya lo he solucionado con esto:

void sd_card_create(){
  char SES[12];
  exist = 0;
  num = 1;
   
  while(exist ==0){
      sprintf(SES,"ses%d.txt",num);
      if(SD.exists(SES)){
      num++;
    }
    else{
      exist = 1;
    }
  }

  if(exist == 1){
    myFile = SD.open(SES, FILE_WRITE);
    if(myFile){
    myFile.println("Probando...");
    myFile.close();
    }       
  }
}

Sólo me falta saber la segunda parte.

Saludos a todos.

orenes: Y otra pregunta es, si cuando se empieza a escribir en el bloc de notas, se puede sobrescribir sobre un determinado comando, por ejemplo,estoy haciendo un lap timer y cuando se actualice tanto el mejor tiempo, el tiempo total y/o el tiempo parcial, se actualicen.

Dices tú algo así como sobrescribir porciones de texto en el programa? Es posible siempre y cuando esas porciones no se muevan de donde están, y no se altere el tamaño.

Se hace con la función seek, sin embargo necesito que adjuntes el archivo de texto resultante. Adjúntalo tal y como está, no le alteres nada. La idea es ayudarte a determinar las posiciones necesarias para sobrescribir los datos cuando se requiera.

Lucario448: Dices tú algo así como sobrescribir porciones de texto en el programa? Es posible siempre y cuando esas porciones no se muevan de donde están, y no se altere el tamaño.

Se hace con la función seek, sin embargo necesito que adjuntes el archivo de texto resultante. Adjúntalo tal y como está, no le alteres nada. La idea es ayudarte a determinar las posiciones necesarias para sobrescribir los datos cuando se requiera.

Sí, en la muestra que pongo a continuación sería como quedaría, desde Total laps hasta Average, sería lo que se quedaría siempre en el mismo sitio, luego las vueltas van escribiéndose una a continuación de la otra.

Sería así para poder leerlo desde una lcd 16x2, creo que es posible pasarlo al lcd, ¿no?;

Total laps: 000

Total time: 00:00:00:000

Best lap: 00:00:000

Average: 00:00:000

Lap 1: 00:00:000

Lap 2: 00:00:000

Y así sucesivamente con las vueltas.

Es posible también que si estoy leyendo en la parte de arriba del todo si le doy a subir, muestre la última vuelta y viceversa, ¿no?. Con los ficheros txt no me aclaro mucho. Gracias y saludos.

El concepto de "posiciones" se debe al hecho de que la clase File trata los archivos de texto como si fuera de una sola línea.

Cómo sabe cuando termina una línea? Cuando se encuentra con el conjunto de caracteres \r y \n. No son cuatro sino dos; como los editores de texto lo interpretan como un cambio de línea (no como una simple 'r' seguido por una 'n'); en código se representan de esa manera.

El trozo de texto que acabas de postear, así lo ve la clase File:

"Total laps:\r\n000\r\n\r\nTotal time:\r\n00:00:00:000\r\n\r\nBest lap:\r\n00:00:000\r\n\r\nAverage:\r\n00:00:000\r\n\r\nLap 1:\r\n00:00:000\r\n\r\nLap 2:\r\n00:00:000\r\n"

Se muy desordenado, pero así es como el programa lo maneja. Recuerda que cada conjunto "\r\n" marca el inicio de una nueva línea.

Verdad que ahora sí es más fácil entender ese concepto de "posición"? Cada caracter que ves en esa larga línea (excepto los '\' y comillas dobles) tiene una posición. Esta se da como "índice cero" (parte desde cero). En la posición 0 tenemos una 'T', y así sucesivamente.

Ahora sabiendo lo anterior; podré decir con certeza que las posiciones que necesitamos para sobrescribir los datos requeridos, son las siguientes:

#define TOT_LAP_POS 13
#define TOT_TIME_POS 33
#define BEST_LAP_POS 60
#define AVERAGE_POS 83

Se declaran como variables globales (fuera de cualquier función).

Se utilizan de la siguiente manera:

Necesitas cambiar las vueltas totales?

archivo.seek(TOT_LAP_POS);
archivo.print(lasVueltas);
// Debes respetar que se impriman tres digitos; de lo contrario,
// podría ocurrir un "corrimiento de porciones", lo cual puede acabar en un texto desastroso.

Necesitas cambiar el tiempo de la mejor vuelta?

archivo.seek(BEST_LAP_POS);
archivo.print(tiempo);
// Recuerda respetar el formato hh:mm:ss:msmsms ("msmsms" = tres dígitos para los milisegundos);
// de lo contrario, "corrimiento de porciones" y texto desastroso.

Necesitas volver a seguir registrando vueltas?

archivo.seek(archivo.size() - 1);
// El "puntero" del archivo busca el final de este; de lo contrario, sobrescribe cuando no debería;
// y de nuevo, texto desastroso.

orenes: Sería así para poder leerlo desde una lcd 16x2, creo que es posible pasarlo al lcd, ¿no?

Pero por supuesto que sí. Solo lo que me deja en duda es: qué se debe leer del archivo? Sólo los números o cada par de líneas?

Si la respuesta es la primera, creo que bastará con las constantes que te había definido antes. Si la respuesta es la segunda, habrá que definir más constantes.

Para enviar datos a una LCD, preferiblemente leo la línea y la guardo en un "búfer"; luego ese "búfer" se imprime al LCD. Ejemplo:

 // Lee el tiempo de la mejor vuelta, y lo imprime en una LCD
archivo.seek(BEST_LAP_POS);
char buffer[17]; // El LCD es de 16 caracteres de largo; por lo tanto, 16 + un espacio para el terminador.
buffer[archivo.readBytesUntil('\r', buffer, sizeof(buffer) - 1)] = 0;
// Aquí ocurre la lectura y su almacenamiento temporal. Lee hasta el fin de la línea, o a los 16 caracteres.

// Asumiendo que ya ajustaste el cursor en x,0
lcd.print(buffer);

orenes: Es posible también que si estoy leyendo en la parte de arriba del todo si le doy a subir, muestre la última vuelta y viceversa, ¿no?.

Mmmm no entendí la pregunta, pero espero habértela respondido de todas formas.

Lucario448, muchas gracias por tu ayuda, ahora si lo entiendo mejor el tema de la SD, a ver si mañana puedo ponerme un rato y a ver si me sale bien todo.

Lucario448: Pero por supuesto que sí. Solo lo que me deja en duda es: qué se debe leer del archivo? Sólo los números o cada par de líneas?

Si la respuesta es la primera, creo que bastará con las constantes que te había definido antes. Si la respuesta es la segunda, habrá que definir más constantes.

Pensaba que sería más fácil leer cada par de líneas porque no se va saber uno cuántas líneas escritas por el tema de la vueltas hechas.

Lucario448: Mmmm no entendí la pregunta, pero espero habértela respondido de todas formas.

La pregunta era,que me expliqué mal, es que si por ejemplo estoy en el primer par de líneas leyéndolas, si le doy al botón de subir por ejemplo, que pase a las últimas par de líneas escritas y viceversa.

Muchas gracias de verdad, me has ayudado bastante. Saludos.

orenes: Pensaba que sería más fácil leer cada par de líneas porque no se va saber uno cuántas líneas escritas por el tema de la vueltas hechas.

No es tan difícil, lo que ocurre es que habría hacer dos veces lo que te había propuesto; y que ahora hay que definir más constantes:

#define TOT_LAP_POS_T 0
#define TOT_TIME_POS_T 20
#define BEST_LAP_POS_T 49
#define AVERAGE_POS_T 73

// Las siguientes se usan en caso de leer el tiempo de una vuelta (número de un dígito)
#define FIRST_LAP_OFFS_T 96
#define LAP_ONE_DIGIT_SIZE 21 // Se deduce desde la 'L' hasta el segundo cambio de línea (segundo "\r\n") 

// Las siguientes se usan en caso de leer el tiempo de una vuelta (número de dos dígitos).
// Así se podrían leer hasta 100 vueltas registradas.
#define LAP_TEN_OFFS_T 285
#define LAP_TWO_DIGITS_SIZE 22 // Se deduce desde la 'L' hasta el segundo cambio de línea (segundo "\r\n")

Nótese que las cuatro primeras nuevas constantes tienen casi el mismo nombre a las anteriores, solo que estas de aquí terminan en "_T" (la letra 'T' hace alusión a la palabra "título" o "texto"; dicho en otras palabras, "título/nombre del dato"). Las que acaban en "_SIZE" son para fines "matemáticos".

Ahora el ejemplo sería:

// Lee un par de líneas y luego las imprime en una LCD. Mira por qué digo que "se hace dos veces"
char nombre[17];
char dato[17];
// Almacenamiento temporal para leer título (nombre) y dato

archivo.seek(BEST_LAP_POS_T); // Con _T para que sepa que vamos a leer el "título/nombre" del dato
nombre[archivo.readBytesUntil('\r', nombre, sizeof(nombre) - 1)] = 0;
// Aquí ocurre la lectura y almacenamiento temporal del nombre. Lee hasta el fin de la línea, o a los 16 caracteres.

archivo.seek(BEST_LAP_POS); // Sin _T para que sepa que vamos a leer el dato propiamente
dato[archivo.readBytesUntil('\r', dato, sizeof(dato) - 1)] = 0;
// Aquí ocurre la lectura y almacenamiento temporal del dato. Lee hasta el fin de la línea, o a los 16 caracteres.

// Dices que es de 16x2, así que lógicamente hay que limpiar la pantalla para luego imprimir el par de líneas
lcd.clear();

// Imprimir primera línea
lcd.print(nombre);

// Ajustar el cursor al principio de la siguiente fila; luego imprimir el dato
lcd.setCursor(0, 1);
lcd.print(dato);

orenes: La pregunta era,que me expliqué mal, es que si por ejemplo estoy en el primer par de líneas leyéndolas, si le doy al botón de subir por ejemplo, que pase a las últimas par de líneas escritas y viceversa.

Creo que eso lo acabo de responder aquí:

Lucario448: Necesitas volver a seguir registrando vueltas?

archivo.seek(archivo.size() - 1);
// El "puntero" del archivo busca el final de este; de lo contrario, sobrescribe cuando no debería;
// y de nuevo, texto desastroso.

La solución está en la función seek, recuerda.

Para regresar a leer algún dato... de nuevo; función seek:

archivo.seek(NOMBRE_DE_LA_CONSTANTE);

Claro ahora sí?

Creo que ya lo tengo claro, ahora tengo el problema de que cuando quiero registrar las vueltas, no escribe en la SD, cuando no tenía puesto para guardar las vueltas, todo perfecto, guarda todo, pero ahora no, sólo la parte por defecto de los 00.

void sd_w(){
  Serial.println("lap");
  int i = lap-1;
  myFile = SD.open(SES, FILE_WRITE);
      if(i<10){
      myFile.seek(TOT_LAP_POS);
      myFile.print("00");
      myFile.print(i);
      Serial.println(i);  
    }
  if(i>9 && i<100){
    myFile.seek(TOT_LAP_POS);
    myFile.print("0");
    myFile.print(i);
    Serial.println(i);
 }
    if(i>99){
      myFile.seek(TOT_LAP_POS);
      myFile.print(i);
      Serial.println(i);
    }
    best();
}
 //******************************************************
 void best(){
    
    Serial.println(mejor_vuelta);
     m=mejor_vuelta/60000;                     
     mu=m%10;                           
     md=(m-mu)/10;                       
     s=(mejor_vuelta/1000)-(m*60);            
     su=s%10;                            
     sd=(s-su)/10;                       
     l=mejor_vuelta-(s*1000)-(m*60000);        
     lu=l%10;                           
     ld=((l-lu)/10)%10;                  
     lc=(l-(ld*10)-lu)/100;
     myFile.seek(BEST_LAP_POS);                           
     myFile.print(md);                      
     myFile.print(mu);               
     myFile.print(":");
     myFile.print(sd);
     myFile.print(su);
     myFile.print(":");
     myFile.print(lc);
     myFile.print(ld);
     myFile.print(lu);
     Serial.println("Best written");
    average();
}
//************************************************************
void average(){
  Serial.println(media);
     m=mejor_vuelta/60000;                     
     mu=m%10;                           
     md=(m-mu)/10;                       
     s=(media/1000)-(m*60);            
     su=s%10;                            
     sd=(s-su)/10;                       
     l=media-(s*1000)-(m*60000);        
     lu=l%10;                           
     ld=((l-lu)/10)%10;                  
     lc=(l-(ld*10)-lu)/100;
     myFile.seek(AVERAGE_POS);                           
     myFile.print(md);                      
     myFile.print(mu);               
     myFile.print(":");
     myFile.print(sd);
     myFile.print(su);
     myFile.print(":");
     myFile.print(lc);
     myFile.print(ld);
     myFile.print(lu);
     Serial.println("Average written");
     lap_w();
}
//***********************************************
void lap_w(){
  myFile.seek(myFile.size() - 1);
  Serial.println(tiempo_actual);
     m=tiempo_actual/60000;                     
     mu=m%10;                           
     md=(m-mu)/10;                       
     s=(tiempo_actual/1000)-(m*60);            
     su=s%10;                            
     sd=(s-su)/10;                       
     l=tiempo_actual-(s*1000)-(m*60000);        
     lu=l%10;                           
     ld=((l-lu)/10)%10;                  
     lc=(l-(ld*10)-lu)/100;                           
     myFile.print(md);                      
     myFile.print(mu);               
     myFile.print(":");
     myFile.print(sd);
     myFile.print(su);
     myFile.print(":");
     myFile.print(lc);
     myFile.print(ld);
     myFile.print(lu);
     Serial.println("Lap written");
  myFile.close();
}

Ahora mismo lo tengo hecho así en bruto para comprobar fallos, luego lo iré gestionando para ocupar menos ROM.

Muchas gracias de nuevo y saludos.

orenes:
Creo que ya lo tengo claro, ahora tengo el problema de que cuando quiero registrar las vueltas, no escribe en la SD, cuando no tenía puesto para guardar las vueltas, todo perfecto, guarda todo, pero ahora no, sólo la parte por defecto de los 00.

Hablas de sd_w verdad? La única forma de que falle en sobrescribir los datos, es que el archivo no se haya podido abrir para escritura. Tendrás que colocar más datos de depuración:

void sd_w(){
  Serial.println("lap");
  int i = lap-1;
  myFile = SD.open(SES, FILE_WRITE);
      if(i<10){
      myFile.seek(TOT_LAP_POS);
      myFile.print("00");
      Serial.println(myFile.position()); // Valor esperado: 15
      myFile.print(i);
      Serial.println(myFile.position()); // Valor esperado: 16
      Serial.println(i);  
    }
  if(i>9 && i<100){
    myFile.seek(TOT_LAP_POS);
    myFile.print("0");
    Serial.println(myFile.position()); // Valor esperado: 14
    myFile.print(i);
    Serial.println(myFile.position()); // Valor esperado: 16
    Serial.println(i);
 }
    if(i>99){
      myFile.seek(TOT_LAP_POS);
      Serial.println(myFile.position()); // Valor esperado: 13
      myFile.print(i);
      Serial.println(myFile.position()); // Valor esperado: 16
      Serial.println(i);
    }
    best();
}

La verdad sería extraño que el puntero no se mueva después de una lectura/escritura (a menos que haya sido con peek).

void lap_w(){
  myFile.seek(myFile.size() - 1);
  Serial.println(tiempo_actual);
     m=tiempo_actual/60000;                     
     mu=m%10;                           
     md=(m-mu)/10;                       
     s=(tiempo_actual/1000)-(m*60);            
     su=s%10;                            
     sd=(s-su)/10;                       
     l=tiempo_actual-(s*1000)-(m*60000);        
     lu=l%10;                           
     ld=((l-lu)/10)%10;                  
     lc=(l-(ld*10)-lu)/100;                           
     myFile.print(md);                      
     myFile.print(mu);               
     myFile.print(":");
     myFile.print(sd);
     myFile.print(su);
     myFile.print(":");
     myFile.print(lc);
     myFile.print(ld);
     myFile.print(lu);
     Serial.println("Lap written");
  myFile.close();
}

Espera, estás intentado sobrescribir los datos de una vuelta, o crear una nueva?
La función tal y como está podría dejar las dos últimas líneas así:

Lap 1:
00:04:38500:32:64501:01:64301:42:839

Suponendo tres intentos.

Lucario448: Hablas de sd_w verdad? La única forma de que falle en sobrescribir los datos, es que el archivo no se haya podido abrir para escritura. Tendrás que colocar más datos de depuración:

Sin poner lap_w, todo funciona de maravilla, pero cuando pongo lap_w, ya no se sobrescribe nada y se queda los 00 de por defecto.

Lucario448: Espera, estás intentado sobrescribir los datos de una vuelta, o crear una nueva? La función tal y como está podría dejar las dos últimas líneas así:

No, quiero crearlas nuevas, pero hasta que no solucione el problema de por qué no funciona la opción de escribir las vueltas, no lo configuro para que salga bien. Soy de los que primero ven si funciona y luego ajusta.

Gracias y saludos.

Y que tal si antes de hacer el seek, hacer un flush?

Llamar a flush o a close garantiza que los datos sean físicamente escritos en la tarjeta SD.

El principio de la función lap_w deberia quedar así:

void lap_w(){
  Serial.println(myFile.size());
  // Si no se añadieron mas vueltas y el archivo acaba en la segunda, entonces el valor esperado es 134, 136 o 138
  // A este valor se le debería sumar 9 cada vez que se llame esta función.

  myfile.flush();
  Serial.println(myFile.size());
  // Si solo sobrescribe y no introduce nada nuevo, los valores esperados deberían ser los mismos: 134, 136 o 138
  // A este valor se le debería sumar 9 cada vez que se llame esta función.
  myFile.seek(myFile.size() - 1);
  Serial.println(myFile.position()); // Valor esperado: 104
  Serial.println(tiempo_actual);

Qué aparece en el monitor serie al intentar visualizar esos valores?

Lucario448: Qué aparece en el monitor serie al intentar visualizar esos valores?

Me devuelve sólo 0 en los size y 4294967295 en position.

No se si será por culpa de la SD o de su formateo, aunque no tendría sentido, ya que lo hace todo menos cuando pongo para que guarde las vueltas.

orenes: Me devuelve sólo 0 en los size y 4294967295 en position.

Que bueno que me lo reportas, porque eso quiere decir que programa o librería están teniendo errores de programación ("bugs").

orenes: No se si será por culpa de la SD o de su formateo

Formato es lo más probable. El único fallo que se puede evidenciar del medio físico, es que se corrompan archivos sin razón aparente (o la estructura de datos del sistema de archivos).

Si en esa tarjeta hay algo importante, respáldalo (hacer una copia fuera de la tarjeta) porque quiero que intentes reformatearla con los siguientes parámetros:

  • Sistema de archivos: FAT32
  • Tamaño del clúster: 4096 bytes

Asumo que sabes como formatear una unidad de almacenamiento externa.

orenes: aunque no tendría sentido, ya que lo hace todo menos cuando pongo para que guarde las vueltas.

Me sorprende que flush no haga bien su trabajo. Digo, si antes de llamar a lap_w ya deberían de haber datos físicamente escritos, por qué size (tamaño del archivo) sigue reportando cero? Definitivamente no tiene sentido (a menos que sea que size no cambia "al vuelo").

Por el momento te dejo dos posibilidades:

  • Formatear la tarjeta con los parámetros antes mencionados.
  • En vez del flush inicial en lap_w, reemplazarlo con un "cerrar y abrir"; posiblemente sea que size no se actualiza "al momento", y solo con reabrir.

PD (explicación opcional): El hecho de que en la parte de llamar a position devuelva un número tan grande; es porque si size reporta cero, entonces no me está llevando a la última posición, sino me dejó en la primera (por hacer myfile.seek(myFile.size()).

Debido a que size devuelve un valor unsigned (carente de negativos), entonces 0 - 1 = -1. Pero... es unsigned!!! Entonces qué va a ocurrir? Algo catastrófico? NO (en el sentido de que se vaya a acabar el mundo o algo por el estilo).

Dar la explicación técnica podría acabar en dolor de cabeza, así lo diré de forma simple: se devuelve al valor más alto posible (4294967295, el máximo propio de un unsigned long, en bytes eso equivale a 4 GB).

El archivo que trabajas... muy difícilmente va a quedar de 4 GB; así que al irse a una posición fuera del archivo, de seguro que se negaba a escribir. Y al sumar más alla del valor máximo ocurre lo contrario (regresa a cero), entonces no me extrañaría que con tanto error se sobrescriban los primeros caracteres del archivo.

Bueno, creo que el lector SD no está en buenas condiciones porque ahora cuando enciende y chequea que está la SD, lo hace bien, pero cuando intenta crear un txt, falla y me cuelga el Arduino o me lo reinicia. Por lo que acabo de pedir dos lectores SD nuevos, uno para SD y otro para microSD, y en cuanto me lleguen, probaré a hacer todo lo que me has dicho y espero que no haya problemas de nuevo.

Otra cosa, ¿no hay otra librería de SD a parte de la que lleva el IDE de Arduino?.

Gracias por tu tiempo y saludos.

SdFat

De hecho en esta es que se basa la librería SD, solo que en una versión más antigua.

Se puede usar como la libraría SD que viene en la IDE de Arduino, solo que con algunas diferencias:

Se debe instanciar la clase:

SdFat sd;

Así lo único que habrá que cambiar en el código, es "SD" por "sd" en cada ocurrencia.

La función name (clase File) está obsoleta ("deprecated"), esto se debe al soporte de LFN (nombres de archivo de hasta 255 caracteres; ya no el típico 8.3). Ahora se usa getName, pero necesita un "array" de char o byte para devolver el nombre del archivo. El resto de funciones de la clase File se siguen usando normalmente.

print y println están optimizados. En la librería SD, estas funciones llamaban implícitamente a flush; lo cual se volvía lento cuando el tiempo importaba. En realidad no afecta en cómo se usan; pero considero que es una mejora importante a destacar :)

Y quizá hayan más cambios, solo que estos son los que yo he notado...

Gracias por la información, pues entonces me esperaré a que me vengan los nuevos módulos SD y probaré si se me solucionan los fallos.

Saludos.

Bueno, después de que me hayan llegado hoy los nuevos módulos SD y seguir el problema, he decidido a toquetear un poco el código y resulta que es problema a la hora de generar el nombre por lo que fallaba la escritura de datos, porque ahora directamente genero un archivo, sin que busque si hay uno o no y haga un nombre más el número, y funciona bien, ahora escribe todos los datos correctamente. Pero ahora pierdo la opción de generar un archivo nuevo. ¿Habría otra forma de generar el nombre de los siguientes archivos?

void sd_card_create(){
  int num=1;
  byte exist=0;
  
  char SES[8];
  
exist = 0;
  num = 1;
  Serial.println("Opened");
 Serial.println("Testing");

 
/*  while(exist ==0){
      sprintf(SES,"ses%d.txt",num);
      if(SD.exists(SES)){
      num++;
      Serial.print(num);
      Serial.println(" Exists");
    }
    else{
      exist = 1;
      Serial.print(SES);
      Serial.println(" creating");
    }
  }
  if(exist == 1){
    Serial.println(SES);
    myFile = SD.open(SES, FILE_WRITE);*/
    myFile = SD.open("orenes1.txt", FILE_WRITE);
    if(myFile){
      myFile.print("Total laps:\r\n000\r\n\r\nTotal time:\r\n00:00:00:000\r\n\r\nBest lap:\r\n00:00:000\r\n\r\nAverage:\r\n00:00:000\r\n\r\n");
      Serial.println("Writing");
      myFile.close();  
    }
    else{
      Serial.println("Error");     
    }
  }
//}
//********************************************************** SD WRITING
  void sd_w(){
   i = lap-1;
  myFile = SD.open("orenes1.txt", FILE_WRITE);
      if(i<10){
      myFile.seek(TOT_LAP_POS);
      myFile.print("00");
      myFile.print(i);
     
    }
  if(i>9 && i<100){
    myFile.seek(TOT_LAP_POS);
    myFile.print("0");
    myFile.print(i);
 
 }
    if(i>99){
      myFile.seek(TOT_LAP_POS);
      myFile.print(i);

    }
    best();
}
 //******************************************************
 void best(){

     m=mejor_vuelta/60000;                     
     mu=m%10;                           
     md=(m-mu)/10;                       
     s=(mejor_vuelta/1000)-(m*60);            
     su=s%10;                            
     sd=(s-su)/10;                       
     l=mejor_vuelta-(s*1000)-(m*60000);        
     lu=l%10;                           
     ld=((l-lu)/10)%10;                  
     lc=(l-(ld*10)-lu)/100;
     myFile.seek(BEST_LAP_POS);                           
     myFile.print(md);                      
     myFile.print(mu);               
     myFile.print(":");
     myFile.print(sd);
     myFile.print(su);
     myFile.print(":");
     myFile.print(lc);
     myFile.print(ld);
     myFile.print(lu);
    average();
}
//************************************************************
void average(){
     m=mejor_vuelta/60000;                     
     mu=m%10;                           
     md=(m-mu)/10;                       
     s=(media/1000)-(m*60);            
     su=s%10;                            
     sd=(s-su)/10;                       
     l=media-(s*1000)-(m*60000);        
     lu=l%10;                           
     ld=((l-lu)/10)%10;                  
     lc=(l-(ld*10)-lu)/100;
     myFile.seek(AVERAGE_POS);                           
     myFile.print(md);                      
     myFile.print(mu);               
     myFile.print(":");
     myFile.print(sd);
     myFile.print(su);
     myFile.print(":");
     myFile.print(lc);
     myFile.print(ld);
     myFile.print(lu);
     myFile.close();
     lap_w();
}
//***********************************************
void lap_w(){
  myFile = SD.open("orenes1.txt", FILE_WRITE);
  if(myFile){
  Serial.println(tiempo_actual);
     m=tiempo_actual/60000;                     
     mu=m%10;                           
     md=(m-mu)/10;                       
     s=(tiempo_actual/1000)-(m*60);            
     su=s%10;                            
     sd=(s-su)/10;                       
     l=tiempo_actual-(s*1000)-(m*60000);        
     lu=l%10;                           
     ld=((l-lu)/10)%10;                  
     lc=(l-(ld*10)-lu)/100;
     myFile.println("");
     myFile.print("LAP:");   
     myFile.println(i);                        
     myFile.print(md);                      
     myFile.print(mu);               
     myFile.print(":");
     myFile.print(sd);
     myFile.print(su);
     myFile.print(":");
     myFile.print(lc);
     myFile.print(ld);
     myFile.println(lu);
     Serial.println("Lap written");
  myFile.close();
}
}

Modifiqué sd_card_create de la siguiente manera:

void sd_card_create() {
  unsigned int num = 1;

  char SES[13];
  Serial.println("Opened");
  Serial.println("Testing");

  sprintf(SES, "ses%d.txt", num);

  while (SD.exists(SES)) {
    Serial.print(num);
    Serial.println(" Exists");
    num++;
    sprintf(SES, "ses%d.txt", num);
  }
  Serial.println(SES);
  Serial.println(" creating");
  myFile = SD.open(SES, FILE_WRITE);
  //myFile = SD.open("orenes1.txt", FILE_WRITE);
  if (myFile) {
    myFile.print(F("Total laps:\r\n000\r\n\r\nTotal time:\r\n00:00:00:000\r\n\r\nBest lap:\r\n00:00:000\r\n\r\nAverage:\r\n00:00:000\r\n\r\n"));
    Serial.println("Writing");
    myFile.close();
  }
  else {
    Serial.println("Error");
  }
}

Reinicié el Arduino 10 veces (se me generaron 10 archivos); pero todos eran de 0 bytes (sin contenido). Al momento de leer la tarjeta en mi PC, noté que no podía eliminar nada; típico, se me había deslizado el interruptor de protección contra escritura a la hora de introducirla a la ranura. Una vez corregido el problema, borro los 10 archivos generados; y ahora me genera otro pero esta vez sí tenía contenido.

El archivo se llamaba "SES1.TXT" y este fue su contenido:

Total laps:
000

Total time:
00:00:00:000

Best lap:
00:00:000

Average:
00:00:000

Supongo que el cronometrado se hace basado en millis, cierto? De ser así pondré a prueba el resto de funciones para ver si las sobrescrituras funcionan correctamente; enviando comandos mediante el monitor serie.

PD: para evitar posibles problemas, el "array" SES debe declarase como global.

Actualizo: utilizando el siguiente código:

#include <SD.h>

#define TOT_LAP_POS 13
#define TOT_TIME_POS 33
#define BEST_LAP_POS 60
#define AVERAGE_POS 83

// Las siguientes se usan en caso de leer el tiempo de una vuelta (número de un dígito)
#define FIRST_LAP_OFFS_T 96
#define LAP_ONE_DIGIT_SIZE 21 // Se deduce desde la 'L' hasta el segundo cambio de línea (segundo "\r\n") 

// Las siguientes se usan en caso de leer el tiempo de una vuelta (número de dos dígitos).
// Así se podrían leer hasta 100 vueltas registradas.
#define LAP_TEN_OFFS_T 285
#define LAP_TWO_DIGITS_SIZE 22 // Se deduce desde la 'L' hasta el segundo cambio de línea (segundo "\r\n")

File myFile;

char SES[13];
unsigned long mejor_vuelta = 4294967295UL;
unsigned long tiempo_actual;
unsigned long tiempo_anterior;
unsigned long tiempo_vuelta;
unsigned int lap = 1;

void setup() {
  Serial.begin(9600);
  if (!SD.begin(4)) {
    Serial.println("SD fail");
    return;
  }
  sd_card_create();
  tiempo_anterior = millis();
}

void loop() {
  if (Serial.available()) {
    switch (Serial.read()) {
      case 'w':
        tiempo_actual = millis();
        sd_w();
        tiempo_anterior = tiempo_actual;
        break;

      case 'n':
        mejor_vuelta = 4294967295UL;
        lap = 1;
        Serial.println("Crear nuevo archivo.");
        sd_card_create();
        tiempo_anterior = millis();
        break;

      default:
        Serial.println("Comando desconocido");
    }
    Serial.flush();
  }

}

void sd_card_create() {
  unsigned int num = 1;
  Serial.println("Preparando...");

  sprintf(SES, "ses%d.txt", num);

  while (SD.exists(SES)) {
    Serial.print(SES);
    Serial.println(" ya existe");
    num++;
    sprintf(SES, "ses%d.txt", num);
  }
  Serial.print(SES);
  Serial.println(" creado");
  myFile = SD.open(SES, FILE_WRITE);
  //myFile = SD.open("orenes1.txt", FILE_WRITE);
  if (myFile) {
    myFile.print(F("Total laps:\r\n000\r\n\r\nTotal time:\r\n00:00:00:000\r\n\r\nBest lap:\r\n00:00:000\r\n\r\nAverage:\r\n00:00:000\r\n\r\n"));
    Serial.println("Listo. Introduce comando.");
    myFile.close();
  }
  else {
    Serial.println("Error");
  }
}
//********************************************************** SD WRITING
void sd_w() {
  myFile = SD.open(SES, FILE_WRITE);
  if (lap < 10) {
    myFile.seek(TOT_LAP_POS);
    myFile.print("00");
    myFile.print(lap);

  }
  if (lap > 9 && lap < 100) {
    myFile.seek(TOT_LAP_POS);
    myFile.print("0");
    myFile.print(lap);

  }
  if (lap > 99) {
    myFile.seek(TOT_LAP_POS);
    myFile.print(lap);

  }
  // "Total time" nunca se actualiza?
  best();
}
//******************************************************
void best() {
  tiempo_vuelta = tiempo_actual - tiempo_anterior;

  if (tiempo_vuelta < mejor_vuelta) {
    mejor_vuelta = tiempo_vuelta;
    unsigned long m, mu, md, s, su, sd, l, lu, ld, lc;
    m = mejor_vuelta / 60000;
    mu = m % 10;
    md = (m - mu) / 10;
    s = (mejor_vuelta / 1000) - (m * 60);
    su = s % 10;
    sd = (s - su) / 10;
    l = mejor_vuelta - (s * 1000) - (m * 60000);
    lu = l % 10;
    ld = ((l - lu) / 10) % 10;
    lc = (l - (ld * 10) - lu) / 100;
    myFile.seek(BEST_LAP_POS);
    myFile.print(md);
    myFile.print(mu);
    myFile.print(":");
    myFile.print(sd);
    myFile.print(su);
    myFile.print(":");
    myFile.print(lc);
    myFile.print(ld);
    myFile.print(lu);
  }
  //average();
  myFile.close();
  lap_w();
}
//************************************************************
/*void average() {
  unsigned long m, mu, md, s, su, sd, l, lu, ld, lc;
  // No uso esta función porque no sé como se obtiene "media"
  m = mejor_vuelta / 60000;
  mu = m % 10;
  md = (m - mu) / 10;
  s = (media / 1000) - (m * 60);
  su = s % 10;
  sd = (s - su) / 10;
  l = media - (s * 1000) - (m * 60000);
  lu = l % 10;
  ld = ((l - lu) / 10) % 10;
  lc = (l - (ld * 10) - lu) / 100;
  myFile.seek(AVERAGE_POS);
  myFile.print(md);
  myFile.print(mu);
  myFile.print(":");
  myFile.print(sd);
  myFile.print(su);
  myFile.print(":");
  myFile.print(lc);
  myFile.print(ld);
  myFile.print(lu);
  myFile.close();
  lap_w();
  }*/
//***********************************************
void lap_w() {
  unsigned long m, mu, md, s, su, sd, l, lu, ld, lc;
  myFile = SD.open(SES, FILE_WRITE);
  if (myFile) {
    Serial.print("Tiempo de la vuelta (ms): ");
    Serial.println(tiempo_vuelta);
    m = tiempo_vuelta / 60000;
    mu = m % 10;
    md = (m - mu) / 10;
    s = (tiempo_actual / 1000) - (m * 60);
    su = s % 10;
    sd = (s - su) / 10;
    l = tiempo_actual - (s * 1000) - (m * 60000);
    lu = l % 10;
    ld = ((l - lu) / 10) % 10;
    lc = (l - (ld * 10) - lu) / 100;

    /*if (lap < 10)
      myFile.seek(FIRST_LAP_OFFS_T + (LAP_ONE_DIGIT_SIZE * (lap - 1)));
      else if (lap < 100)
      myFile.seek(LAP_TEN_OFFS_T + (LAP_TWO_DIGIT_SIZE * (lap - 1)));
      else
      Serial.println("No se ha programado para leer más de 100 vueltas");*/

    myFile.print("LAP ");
    myFile.print(lap);
    myFile.println(':');
    myFile.print(md);
    myFile.print(mu);
    myFile.print(":");
    myFile.print(sd);
    myFile.print(su);
    myFile.print(":");
    myFile.print(lc);
    myFile.print(ld);
    myFile.println(lu);
    myFile.println();
    Serial.println("Lap written");
    myFile.close();
    lap++;
  }
}

Puedo crear vueltas, registrar el mejor tiempo, registrar cantidad de vueltas; todo sin ningún problema.
Aquí lo único que no actualiza es el promedio y tiempo total. Promedio no sé cómo piensas calcularlo, y el tiempo total me falta implementarlo.
Y la mejor parte de todas: PUEDO CREAR ARCHIVOS NUEVOS QUE LA ACTUALIZACIÓN DE DATOS SIGUE FUNCIONANDO!!!

Aquí los resultados:

SES2.TXT

Total laps:
005

Total time:
00:00:00:000

Best lap:
00:02:312

Average:
00:00:000

LAP 1:
00:07:432

LAP 2:
00:13:501

LAP 3:
00:15:813

LAP 4:
00:19:953

LAP 5:
00:25:241

SES3.TXT

Total laps:
005

Total time:
00:00:00:000

Best lap:
00:01:824

Average:
00:00:000

LAP 1:
00:35:653

LAP 2:
00:37:477

LAP 3:
00:40:612

LAP 4:
00:44:766

LAP 5:
00:47:143

PD: creo que estoy calculando mal el tiempo de vuelta; no es que a propósito me tarde cada vez más en registrar una vuelta.
Pareciera que en cada vuelta lo que imprimo es el tiempo total, no uno parcial :confused:

El promedio lo hago con el tiempo total entre el número de vuelta.

Los tiempo los registro con millis.

Probé a poner el código anterior a este último que has puesto pero muchas veces tiraba error y cuando no, la parte por defecto no se escribía bien y las vueltas si lo hacía bien.

Para el tiempo por vuelta, cada vez que pulses el botón para registra el tiempo, tiene que tomar otra vez el millis que está en ese momento como inicio, y luego restar millis con el tiempo de inicio tomado como cero.

Saludos y gracias