Code optimization for arduino Uno

This project uses an MC522 RFID, RTC DS1302, sd reader/writer, buzzer, button and lcd i2c.

#include <MFRC522.h>
#include <Wire.h>
#include <ThreeWire.h>
#include <LiquidCrystal_I2C.h>
#include <RtcDS1302.h>
#include <SD.h>

// Definiciones de pines
#define RST_PIN     9
#define SS_PIN      10
#define button      2
#define buzzerPin   3
#define cspin       4

// Variables globales
byte valueButton = 1;
byte minutoA, minuto, mesA, mes, diaA, dia, horaA, hora;
int aA, a;
byte stateButton = 0;
unsigned long tx = 0;
unsigned long timepassed;
bool cardRead = false;
String cardValue;
char uidBuffer[4];
byte lengthU;
int toRun;
String copy = "";

// Objetos para periféricos
LiquidCrystal_I2C lcd(0x27, 16, 2);
ThreeWire myWire(6, 5, 7);
RtcDS1302<ThreeWire> Rtc(myWire);
MFRC522 mfrc522(SS_PIN, RST_PIN);

// Archivos
File usuarios, registro, entraron, file;
String fileF = "FILE.TXT";
String entraronF = "ENTRARON.TXT"; 
String usuariosF = "USUARIOS.TXT";          
String registroF = "REGISTRO.TXT"; 

void setup() {
  Serial.begin(9600);
  pinMode(cspin, OUTPUT);
  digitalWrite(cspin, HIGH);
  
  pinMode(buzzerPin, OUTPUT);
  pinMode(button, INPUT_PULLUP);
  lcd.begin(16, 2);
  lcd.backlight();
  Rtc.Begin();
  //Config for RTC
  /*RtcDateTime currentTime = RtcDateTime(__DATE__ , __TIME__);
  Rtc.SetDateTime(currentTime);
  */
  SPI.begin();
  mfrc522.PCD_Init();

  // Inicializar la tarjeta SD
  if (!SD.begin(cspin)) {
    Serial.println(F("Error initializing SD card."));
    displayMessage("Error iniciando", "tarjeta SD");
    while(1){}
  }
  Serial.println(F("SD card initialized successfully."));

  // Esperar para iniciar la comunicación Serial
  while (!Serial);
  mfrc522.PCD_DumpVersionToSerial();
  Serial.println(F("Comunicaión Serial iniciada"));

  if (!SD.exists(usuariosF)) {
    usuarios = SD.open(usuariosF, FILE_WRITE);
    usuarios.close();
    Serial.println(F("usuariosF ha sido creado"));
  } else {
    Serial.println(F("usuariosF ya existe"));
    } 
  // Verificar si el archivo "registroF" existe y crearlo si no existe
  if (!SD.exists(registroF)) {
    registro = SD.open(registroF, FILE_WRITE);
    registro.close();
    Serial.println(F("registroF ha sido creado"));
  } else {
    Serial.println(F("registroF ya existe"));
  }

  // Verificar si el archivo "entraronF" existe y crearlo si no existe
  if (!SD.exists(entraronF)) {
    entraron = SD.open(entraronF, FILE_WRITE);
    entraron.close();
    Serial.println(F("entraronF ha sido creado"));
  } else {
    Serial.println(F("entraronF ya existe"));
  }
}

void loop() {
  timepassed = millis();
  registerButton();
  clock();
  mainProcess();
}

void clock() {
  // Obtener la fecha y hora actual del RTC
  RtcDateTime now = Rtc.GetDateTime();
  
  // Obtener los componentes de fecha y hora
  aA = now.Year();
  mesA = now.Month();
  diaA = now.Day();
  horaA = now.Hour();
  minutoA = now.Minute();

  setMillis(100);

  // Actualizar los componentes de fecha y hora
  a = now.Year();
  mes = now.Month();
  dia = now.Day();
  hora = now.Hour();
  minuto = now.Minute();

  // Mostrar "Colocar tarjeta" en la primera línea del LCD
  lcd.setCursor(0, 0);
  lcd.print("Colocar tarjeta");

  // Mostrar la fecha y hora en la segunda línea del LCD
  lcd.setCursor(0, 1);
  printDoubleDigit(dia);
  lcd.print("/");
  printDoubleDigit(mes);
  lcd.print("/");
  lcd.print(a);
  lcd.print("|");
  printDoubleDigit(hora);
  lcd.print(":");
  printDoubleDigit(minuto);

  // Actualizar la pantalla solo si hay cambios en la fecha o hora
  if ((minutoA != minuto) || (horaA != hora) || (diaA != dia) || (mesA != mes) || (aA != a)) {
    lcd.setCursor(0, 1);
    printDoubleDigit(dia);
    lcd.print("/");
    printDoubleDigit(mes);
    lcd.print("/");
    lcd.print(a);
    lcd.print("|");
    printDoubleDigit(hora);
    lcd.print(":");
    printDoubleDigit(minuto);
  }
}

// Función para imprimir un número de dos dígitos
void printDoubleDigit(int number) {
  if (number < 10) {
    lcd.print("0");
  }
  lcd.print(number);
}

void mainProcess() {
  // Verificar si hay una nueva tarjeta presente en el lector
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Leer el serial de la tarjeta
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  // Inicializar la variable cardValue para almacenar el UID
  cardValue = "";

  // Leer el UID de la tarjeta y almacenarlo en cardValue
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    // Formatear el valor hexadecimal y almacenarlo en el búfer
    sprintf(uidBuffer, "%02X", mfrc522.uid.uidByte[i]);
    // Concatenar el valor del búfer en la variable cardValue
    cardValue += uidBuffer;
  }

  // Imprimir el valor del UID en el Serial Plotter
  serialP(cardValue);

  // Verificar si la tarjeta está registrada
  if (ifIsRegister(cardValue)) {
    ifIsAlreadyIn(cardValue); // Verificar si el usuario ya está adentro
    // Mostrar mensajes en la pantalla y hacer sonidos apropiados
    entryRegis(cardValue);
  } else {
    // Mostrar mensajes en la pantalla y hacer sonidos apropiados
    lcd.noDisplay();
    lcd.clear();
    setMillis(100);
    lcd.display();
    displayMessage("No registrado", "");
    buzzerTone(250, 1000, 0);
    lcd.noDisplay();
    lcd.clear();
    lcd.display();
    displayMessage("Para registrarte", "Apreta el boton");
    setMillis(1000);
    lcd.noDisplay();
    lcd.clear();
    setMillis(100);
    lcd.display();
    clock();
  }

  // Limpiar el valor de la tarjeta para la siguiente lectura
  cardValue = "";
  mfrc522.PICC_HaltA(); // Finalizar la comunicación con la tarjeta
}

void registerButton() {
  valueButton = digitalRead(button); // Leer si el botón está pulsado
  
  // Si el botón está presionado
  if (valueButton == LOW) {
    stateButton = 1;
    cardRead = false;
    
    // Mostrar mensaje en la pantalla para colocar la tarjeta
    displayMessage("Colocar tarjeta", "Para registrarse");
    
    tx = millis(); // Guardar el tiempo actual en 'tx'

    // Esperar hasta que se cumpla el tiempo de registro o se lea una tarjeta
    while (millis() - tx < 10000) {
      toRegis();
      
      // Si se leyó una tarjeta, actualizar la hora en la pantalla y salir del bucle
      if (cardRead == true) {
        clock();
        break;
      }
    }
  }
}

void toRegis() {
  if (cardRead) {
    return;
  }

  // Verificar si hay una nueva tarjeta presente en el lector
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Leer el serial de la tarjeta
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  // Inicializar la variable cardValue para almacenar el UID
  cardValue = "";

  // Leer el UID de la tarjeta y almacenarlo en cardValue
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    // Formatear el valor hexadecimal y almacenarlo en el búfer
    sprintf(uidBuffer, "%02X", mfrc522.uid.uidByte[i]);
    // Concatenar el valor del búfer en la variable cardValue
    cardValue += uidBuffer;
  }

  // Imprimir el valor del UID en el Serial Plotter
  serialP(cardValue);

  // Verificar si la tarjeta está registrada
  if (ifIsRegister(cardValue)) {
    displayMessage("Ya estás", "registrado");
    setMillis(1000);
    displayMessage("Inténtalo", "nuevamente");
    buzzerTone(250, 250, 0);
    cardRead = false;
    return; // Salir de la función si la tarjeta ya está registrada
  }
  
  // Mostrar mensaje en la pantalla y hacer sonidos apropiados
  displayMessage("Registrando", "...");
  toRegister(cardValue);
  setMillis(1000);
  displayMessage("Has sido", "registrado");
  buzzerTone(500, 1000, 1);
  cardRead = true;
  cardValue = "";
}
void setMillis(int wantedTime) {
  tx = millis(); // Guardar el tiempo actual en 'tx' 

  // Esperar hasta que haya pasado el tiempo deseado
  while (millis() - tx < wantedTime) {
    // No se hace nada, simplemente se espera
  }
}

void buzzerTone(int t1, int t2, int mode){
  digitalWrite(buzzerPin, HIGH);
  setMillis(t1);
  digitalWrite(buzzerPin, LOW);
  
  if (mode == 0) {
    setMillis(t1);
    digitalWrite(buzzerPin, HIGH);
    setMillis(t1);
    digitalWrite(buzzerPin, LOW);
  }
  
  setMillis(t2);
}

void displayMessage(const char* line1, const char* line2) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(line1);
  lcd.setCursor(0, 1);
  lcd.print(line2);
}

void toRegister(String uid) {
  // Cerrar el archivo antes de abrirlo nuevamente
  usuarios.close();
  setMillis(100);
  
  // Abre el archivo en modo escritura
  usuarios = SD.open(usuariosF, FILE_WRITE);
  setMillis(100);
  
  if (usuarios) {
    usuarios.print(uid); // Escribe el UID en el archivo
    usuarios.print(",");
    usuarios.flush(); // Verificar que ningún dato quedó a medio camino
    usuarios.close(); // Cierra el archivo después de escribirlo
    Serial.println(F("Datos escritos en el archivo correctamente."));
  } else {
    Serial.println(F("Error al abrir el archivo para escritura."));
  }
}

bool ifIsRegister(String user) {
  usuarios.close();
  setMillis(100);
  
  // Abre el archivo en modo lectura
  usuarios = SD.open(usuariosF, FILE_READ);
  setMillis(100);
  
  if (usuarios) {
    String verifyUser;
    char nextCharacter;

    while (usuarios.available()) {
      nextCharacter = usuarios.read();
      if (nextCharacter == ',') {
        if (verifyUser == user) {
          usuarios.close();
          return true;
        }
        verifyUser = ""; // Limpiar la cadena para el siguiente UID
      } else {
        verifyUser += nextCharacter; // Agregar el caracter leído al UID temporal
      }
    }
    
    // Verificar el último UID al final del archivo
    if (verifyUser == user) {
      usuarios.close();
      return true;
    }
    usuarios.close();
  }
  return false; // El archivo no se pudo abrir o el usuario no está registrado
}
void serialP(String message){
  Serial.println(message);
}

void entryRegis(String uid) {
  RtcDateTime now = Rtc.GetDateTime();
  a = now.Year();
  mes = now.Month();
  dia = now.Day();
  hora = now.Hour();
  minuto = now.Minute();
  
  String completeTime = String(hora) + ":" + String(minuto) + " " + String(dia) + "|" + String(mes) + "|" + String(a);
  
  Serial.println(completeTime);

  registro.close();
  setMillis(100);
  
  registro = SD.open(registroF, FILE_WRITE); 
  
  setMillis(100);
  
  if (registro) {
    registro.print(uid); // Escribe el UID en el archivo
    registro.print(" ");
    registro.print(completeTime); // Agrega la fecha y hora al registro
    registro.print(","); // Agrega una coma para separar los UID
    registro.flush(); // Verificar que ningún dato quedó a medio camino
    registro.close(); // Cierra el archivo después de escribirlo
    Serial.println(F("Datos escritos en el archivo correctamente."));
  } else {
    Serial.println(F("Error al abrir el archivo para escritura."));
  }
}
void ifIsAlreadyIn(String user) {
  entraron.close();
  setMillis(100);
  entraron = SD.open(entraronF, FILE_READ); 
  setMillis(100);
  if (entraron) {
    String verifyUser;
    char nextCharacter;
    lengthU = user.length()-1;
    while (entraron.available()) {
      nextCharacter = entraron.read();
      toRun = lengthU + entraron.position();
      if (nextCharacter == ',') {
        if (verifyUser == user) {
          entraron.flush();
          entraron.close();
          setMillis(100);
          removeText(lengthU, toRun);
          displayMessage("Hasta luego,", "nos vemos!!");
          buzzerTone(350, 750, 0);
          entraron.flush();
          entraron.close();
        }
        verifyUser = ""; 
      } else {
        verifyUser += nextCharacter; 
      }
    }
    // Verificar el último UID al final del archivo
    if (verifyUser == user) {
      entraron.close();
      removeText(lengthU, toRun);
      displayMessage("Hasta luego,", "nos vemos!!");
      buzzerTone(350, 750, 0);
    }
    entraron.close();
  }
  addUser(cardValue);
  displayMessage("Bienvenido!!", "de vuelta");
  buzzerTone(500, 1000, 1); 
}
void removeText(int num, int lenghtNum){
  if (!SD.exists(fileF)) {
    file = SD.open(fileF, FILE_WRITE);
    file.close();
    Serial.println(F("fileF ha sido creado"));
  } else {
    Serial.println(F("fileF ya existe"));
  }
  
  entraron.close();
  setMillis(100);
  entraron = SD.open(entraronF, FILE_READ); 
  file = SD.open(fileF, FILE_WRITE); 
  setMillis(100);
  entraron.seek(0);
  while (entraron.available()){
    int actualPos = entraron.position();
    entraron.seek(actualPos);
    if(actualPos < num || actualPos > lenghtNum+1){
      copy = entraron.read();
      file.print(copy);
    } else if(copy == " ") {
      return;
    } else {
      actualPos++;
    }
  }
  
  entraron.flush();
  entraron.close();
  file.flush();
  file.close();
  entraron = SD.open(entraronF, FILE_WRITE); 
  file = SD.open(fileF, FILE_READ); 
  while(file.available()){
    copy = file.read();
    entraron.print(copy);
    if(copy == " ") {
      return;
    }
  }
  
  entraron.flush();
  entraron.close();
  file.flush();
  file.close();
  SD.remove("FILE.TXT");
  Serial.print(F("Text removed"));
}

void addUser(String user){
  entraron.close();
  setMillis(100);
  entraron = SD.open(entraronF, FILE_WRITE); 
  setMillis(100);
  entraron.print(user);
  entraron.print(",");
  entraron.flush();
  entraron.close();
  Serial.println(user);
  Serial.print(F("User added"));
  }

The code appears to be flawless, but it consumes too many resources. Can you give me a hand, to improve its structure or optimization? I know that i can use an arduino mega, but as you note if i writing this i dont have one of this. Thanks a lot, if you need another thing please say it to me.

String objects cause unpredictable crashes on the Uno, are never necessary and thus should not be used. The associated code takes up valuable memory, as well.

Use the F macro to put C-strings like this in program memory:

  lcd.print("Colocar tarjeta");
1 Like

String objects

Thats the big takeaway.
Your program will use less memory, be more stable and a number of other benefits y using c-strings.

The UNO is a VERY small microcontroller by modern standards.
Wasting resources to make life easier is a false benefit.
I love good string libraries as much as anyone, but not with 4K of RAM

There’s an old saying…

if it’s easier for the programmer, it’s harder for the user.
if it’s easier for the user, it’s harder for the programmer.

Strings are like ‘interpreted’ languages, convenient but technically compromised. The closer you get to the hardware, the tighter and more efficient your code will be.

That doesn’t mean you can’t ‘cut corners’, but you must understand what you’re giving away.

1 Like

In what sense?

1 Like
void setMillis(int wantedTime) {
  tx = millis(); // Guardar el tiempo actual en 'tx'

  // Esperar hasta que haya pasado el tiempo deseado
  while (millis() - tx < wantedTime) {
    // No se hace nada, simplemente se espera
  }
}

Just use delay(), your setMillis() function does exactly the same thing.

void clock() {
  // Obtener la fecha y hora actual del RTC
  RtcDateTime now = Rtc.GetDateTime();

  // Obtener los componentes de fecha y hora
  aA = now.Year();
  mesA = now.Month();
  diaA = now.Day();
  horaA = now.Hour();
  minutoA = now.Minute();

  setMillis(100);

  // Actualizar los componentes de fecha y hora
  a = now.Year();
  mes = now.Month();
  dia = now.Day();
  hora = now.Hour();
  minuto = now.Minute();

  // Mostrar "Colocar tarjeta" en la primera línea del LCD
  lcd.setCursor(0, 0);
  lcd.print("Colocar tarjeta");

  // Mostrar la fecha y hora en la segunda línea del LCD
  lcd.setCursor(0, 1);
  printDoubleDigit(dia);
  lcd.print("/");
  printDoubleDigit(mes);
  lcd.print("/");
  lcd.print(a);
  lcd.print("|");
  printDoubleDigit(hora);
  lcd.print(":");
  printDoubleDigit(minuto);

  // Actualizar la pantalla solo si hay cambios en la fecha o hora
  if ((minutoA != minuto) || (horaA != hora) || (diaA != dia) || (mesA != mes) || (aA != a)) {
    lcd.setCursor(0, 1);
    printDoubleDigit(dia);
    lcd.print("/");
    printDoubleDigit(mes);
    lcd.print("/");
    lcd.print(a);
    lcd.print("|");
    printDoubleDigit(hora);
    lcd.print(":");
    printDoubleDigit(minuto);
  }
}

I do not see how the if statement could ever be true. The value of now will not change once it is initialized.

1 Like

Fixed that.

2 Likes

If you redefine your displayMessage() function like this:

void displayMessage(const __FlashStringHelper* line1, const __FlashStringHelper* line2) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(line1);
  lcd.setCursor(0, 1);
  lcd.print(line2);
}

Then you should be able to use the F() macro in the function call:

    displayMessage(F("Error iniciando"), F("tarjeta SD"));

(I have not had time to verify this on actual hardware).

1 Like

hehe - thanks !

That's quite a lot of stuff.
How much "too many"? How about an "Nano Every"? It has 50% more Flash and 3x more RAM...

1 Like

This is super cool idea, i use that for print in serial, but i dont knew thid method, such a cool. Thanks so much

This are two chunks of the same code:

If the if condition do the same as without condition - why do you need it at all?

1 Like

I know it doesn't seem to make sense, but if I don't put this on, the LCD screen flickers very badly.

That does not make much sense, because the IF condition will always be false. All you are doing is setting two sets of variables to the same values (by getting the individuals elements of the time stored in "now") and then checking to see if those identical values are different.

1 Like

The thing is that I did what I was told and the LCD starts to flicker ugly, I saw it as a solution. I already tried to remove the if but it looks really ugly....

Sketch uses 25718 bytes (79%) of program storage space. The maximum is 32256 bytes.
Global variables use 1313 bytes (64%) of dynamic memory, leaving 735 bytes for local variables. The maximum is 2048 bytes.

avrdude: Version 6.3-20190619
Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
Copyright (c) 2007-2014 Joerg Wunsch

     System wide configuration file is "C:\Users\ferna\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf"

     Using Port                    : COM3
     Using Programmer              : arduino
     Overriding Baud Rate          : 115200
     AVR Part                      : ATmega328P
     Chip Erase delay              : 9000 us
     PAGEL                         : PD7
     BS2                           : PC2
     RESET disposition             : dedicated
     RETRY pulse                   : SCK
     serial program mode           : yes
     parallel program mode         : yes
     Timeout                       : 200
     StabDelay                     : 100
     CmdexeDelay                   : 25
     SyncLoops                     : 32
     ByteDelay                     : 0
     PollIndex                     : 3
     PollValue                     : 0x53
     Memory Detail                 :

                              Block Poll               Page                       Polled
       Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
       ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
       eeprom        65    20     4    0 no       1024    4      0  3600  3600 0xff 0xff
       flash         65     6   128    0 yes     32768  128    256  4500  4500 0xff 0xff
       lfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
       hfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
       efuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
       lock           0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
       calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00
       signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00

     Programmer Type : Arduino
     Description     : Arduino
     Hardware Version: 3
     Firmware Version: 4.4
     Vtarget         : 0.3 V
     Varef           : 0.3 V
     Oscillator      : 28.800 kHz
     SCK period      : 3.3 us

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: reading input file "C:\Users\ferna\AppData\Local\Temp\arduino\sketches\7DD2EA11E40858403DCDF3E3E8A29C90/RFIDCODE.ino.hex"
avrdude: writing flash (25616 bytes):

Writing | ################################################## | 100% 4.82s

avrdude: 25616 bytes of flash written

avrdude done. Thank you.

// Verificar si la tarjeta está registrada
 if (ifIsRegister(cardValue)) {
   ifIsAlreadyIn(cardValue); // Verificar si el usuario ya está adentro
   // Mostrar mensajes en la pantalla y hacer sonidos apropiados
   entryRegis(cardValue);
 } else {
   // Mostrar mensajes en la pantalla y hacer sonidos apropiados
   lcd.noDisplay();
   lcd.clear();
   delay(100);
   lcd.display();
   displayMessage(F("No registrado"), F(""));
   buzzerTone(250, 1000, 0);
   lcd.noDisplay();
   lcd.clear();
   lcd.display();
   displayMessage(F("Para registrarte"), F("Apreta el boton"));
   delay(1000);
   lcd.noDisplay();
   lcd.clear();
   delay(100);
   lcd.display();
   clock();
 }

I took some of your recommendations, but it still doesn't work. Thank you very much for your comments and for your time, I'm thinking of borrowing a mega, to see if it's memory or my poor programming ability.

Fixed, thanks for the comment

void clock() {
  // Obtain the current date and time from the RTC
  RtcDateTime now = Rtc.GetDateTime();

  // Check if there's a change in date or time
  if ((minuto != now.Minute()) || (hora != now.Hour()) || (dia != now.Day()) || (mes != now.Month()) || (year != now.Year())) {
    // Update the stored values with the current date and time
    minuto = now.Minute();
    hora = now.Hour();
    dia = now.Day();
    mes = now.Month();
    year = now.Year();
    displayMessage(F("Colocar Tarjeta"), F(""));
    // Update the display with the new values
    lcd.setCursor(0, 1);
    printDoubleDigit(dia);
    lcd.print("/");
    printDoubleDigit(mes);
    lcd.print("/");
    lcd.print(year);
    lcd.print("|");
    printDoubleDigit(hora);
    lcd.print(":");
    printDoubleDigit(minuto);
  }
}

Can you give a good description of what that means? Can you also please provide your complete code as it stands now.

I did look at your original code yesterday and it consumed around 97% of flash and 75% for global variables.

Sketch uses 25770 bytes (79%) of program storage space. Maximum is 32256 bytes.
Global variables use 1549 bytes (75%) of dynamic memory, leaving 499 bytes for local variables. Maximum is 2048 bytes.

Changing below

String fileF = "FILE.TXT";
String entraronF = "ENTRARON.TXT"; 
String usuariosF = "USUARIOS.TXT";          
String registroF = "REGISTRO.TXT"; 

to

char fileF[] = "FILE.TXT";
char entraronF[] = "ENTRARON.TXT";
char usuariosF[] = "USUARIOS.TXT";
char registroF[] = "REGISTRO.TXT";

saves you a little RAM

Sketch uses 25658 bytes (79%) of program storage space. Maximum is 32256 bytes.
Global variables use 1535 bytes (74%) of dynamic memory, leaving 513 bytes for local variables. Maximum is 2048 bytes.

If your code behaves different from what you expect, I noticed some possible issues.

1
Closing files that might already have been closed.

E.g.

void toRegister(String uid)
{
  // Cerrar el archivo antes de abrirlo nuevamente
  usuarios.close();
  setMillis(100);

2
To my knowledge you can't have to files open at the same time as you do in the function removeText.

  entraron = SD.open(entraronF, FILE_READ);
  file = SD.open(fileF, FILE_WRITE);

3
Passing String objects to functions, e.g.

void toRegister(String uid)

This will result in a copy of uid being placed on the stack. You're better of just using a reference which will (on an Uno) only be 2 bytes).

void toRegister(String &uid)

Thanks for the comments, here is the full version:

#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <ThreeWire.h>
#include <LiquidCrystal_I2C.h>
#include <RtcDS1302.h>
#include <SD.h>

// Definiciones de pines
#define RST_PIN     9 //RFID
#define SS_PIN      10 //RFID
#define button      2
#define buzzerPin   3
#define cspin       4 //SD

// Variables globales
byte valueButton = 1;
byte minutoA, minuto, mes, dia, hora;
int year, lengthU, toRun;
byte stateButton = 0;
unsigned long tx = 0;
bool cardRead = false;
String cardValue;
char uidBuffer[4];
String copy = "";

// Objetos para componentes
LiquidCrystal_I2C lcd(0x27, 16, 2);
ThreeWire myWire(6, 5, 7);
RtcDS1302<ThreeWire> Rtc(myWire);
MFRC522 mfrc522(SS_PIN, RST_PIN);

// Archivos
File usuarios, registro, entraron, file;
char fileF[] = "FILE.TXT";
char entraronF[] = "ENTRARON.TXT";
char usuariosF[] = "USUARIOS.TXT";
char registroF[] = "REGISTRO.TXT";

void setup() {
  Serial.begin(9600);
  pinMode(cspin, OUTPUT);
  digitalWrite(cspin, HIGH);
  
  pinMode(buzzerPin, OUTPUT);
  pinMode(button, INPUT_PULLUP);
  lcd.begin(16, 2);
  lcd.backlight();
  Rtc.Begin();
  //Configuración para RTC
  /*RtcDateTime currentTime = RtcDateTime(__DATE__ , __TIME__);
  Rtc.SetDateTime(currentTime);
  */
  SPI.begin();
  mfrc522.PCD_Init();

  // Inicializar la tarjeta SD
  if (!SD.begin(cspin)) {
    Serial.println(F("Error initializing SD card."));
    displayMessage(F("Error iniciando"), F("tarjeta SD"));
    while(1){}
  }
  Serial.println(F("SD card initialized successfully."));

  // Esperar para iniciar la comunicación Serial
  while (!Serial);
  mfrc522.PCD_DumpVersionToSerial();
  Serial.println(F("Comunicaión Serial iniciada"));

  if (!SD.exists(usuariosF)) {
    usuarios = SD.open(usuariosF, FILE_WRITE);
    usuarios.close();
    Serial.println(F("usuariosF ha sido creado"));
  } else {
    Serial.println(F("usuariosF ya existe"));
    } 
  // Verificar si el archivo "registroF" existe y crearlo si no existe
  if (!SD.exists(registroF)) {
    registro = SD.open(registroF, FILE_WRITE);
    registro.close();
    Serial.println(F("registroF ha sido creado"));
  } else {
    Serial.println(F("registroF ya existe"));
  }

  // Verificar si el archivo "entraronF" existe y crearlo si no existe
  if (!SD.exists(entraronF)) {
    entraron = SD.open(entraronF, FILE_WRITE);
    entraron.close();
    Serial.println(F("entraronF ha sido creado"));
  } else {
    Serial.println(F("entraronF ya existe"));
  }
}

void loop() {
  registerButton();
  clock();
  mainProcess();
}

void clock() {
  // Obtener la fecha y hora actual del RTC
  RtcDateTime now = Rtc.GetDateTime();

  // Obtener los componentes de fecha y hora
  minutoA = now.Minute();

  delay(100);

  // Actualizar los componentes de fecha y hora
  year = now.Year();
  mes = now.Month();
  dia = now.Day();
  hora = now.Hour();
  minuto = now.Minute();

  // Mostrar "Colocar tarjeta" en la primera línea del LCD
  lcd.setCursor(0, 0);
  lcd.print("Colocar tarjeta");

  // Mostrar la fecha y hora en la segunda línea del LCD
  lcd.setCursor(0, 1);
  printDoubleDigit(dia);
  lcd.print("/");
  printDoubleDigit(mes);
  lcd.print("/");
  lcd.print(year);
  lcd.print("|");
  printDoubleDigit(hora);
  lcd.print(":");
  printDoubleDigit(minuto);

  // Actualizar la pantalla solo si hay cambios en la fecha o hora
  if ((minutoA != minuto)) {
    lcd.setCursor(0, 1);
    printDoubleDigit(dia);
    lcd.print("/");
    printDoubleDigit(mes);
    lcd.print("/");
    lcd.print(year);
    lcd.print("|");
    printDoubleDigit(hora);
    lcd.print(":");
    printDoubleDigit(minuto);
  }
}

// Función para imprimir un número de dos dígitos
void printDoubleDigit(int number) {
  if (number < 10) {
    lcd.print("0");
  }
  lcd.print(number);
}

void mainProcess() {
  // Verificar si hay una nueva tarjeta presente en el lector
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Leer el serial de la tarjeta
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  // Inicializar la variable cardValue para almacenar el UID
  cardValue = "";

  // Leer el UID de la tarjeta y almacenarlo en cardValue
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    // Formatear el valor hexadecimal y almacenarlo en el búfer
    sprintf(uidBuffer, "%02X", mfrc522.uid.uidByte[i]);
    // Concatenar el valor del búfer en la variable cardValue
    cardValue += uidBuffer;
  }

  // Imprimir el valor del UID en el Serial Plotter
  serialP(cardValue);

  // Verificar si la tarjeta está registrada
  if (ifIsRegister(cardValue)) {
    ifIsAlreadyIn(cardValue); // Verificar si el usuario ya está adentro
    // Mostrar mensajes en la pantalla y hacer sonidos apropiados
    entryRegis(cardValue);
  } else {
    // Mostrar mensajes en la pantalla y hacer sonidos apropiados
    lcd.noDisplay();
    lcd.clear();
    delay(100);
    lcd.display();
    displayMessage(F("No registrado"), F(""));
    buzzerTone(250, 1000, 0);
    lcd.noDisplay();
    lcd.clear();
    lcd.display();
    displayMessage(F("Para registrarte"), F("Apreta el boton"));
    delay(1000);
    lcd.noDisplay();
    lcd.clear();
    delay(100);
    lcd.display();
    clock();
  }

  // Limpiar el valor de la tarjeta para la siguiente lectura
  cardValue = "";
  mfrc522.PICC_HaltA(); // Finalizar la comunicación con la tarjeta
}

void registerButton() {
  valueButton = digitalRead(button); // Leer si el botón está pulsado
  
  // Si el botón está presionado
  if (valueButton == LOW) {
    stateButton = 1;
    cardRead = false;
    
    // Mostrar mensaje en la pantalla para colocar la tarjeta
    displayMessage(F("Colocar tarjeta"), F("Para registrarse"));
    
    tx = millis(); // Guardar el tiempo actual en 'tx'

    // Esperar hasta que se cumpla el tiempo de registro o se lea una tarjeta
    while (millis() - tx < 10000) {
      toRegis();
      
      // Si se leyó una tarjeta, actualizar la hora en la pantalla y salir del bucle
      if (cardRead == true) {
        clock();
        break;
      }
    }
  }
}

void toRegis() {
  if (cardRead) {
    return;
  }

  // Verificar si hay una nueva tarjeta presente en el lector
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Leer el serial de la tarjeta
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  // Inicializar la variable cardValue para almacenar el UID
  cardValue = "";

  // Leer el UID de la tarjeta y almacenarlo en cardValue
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    // Formatear el valor hexadecimal y almacenarlo en el búfer
    sprintf(uidBuffer, "%02X", mfrc522.uid.uidByte[i]);
    // Concatenar el valor del búfer en la variable cardValue
    cardValue += uidBuffer;
  }

  // Imprimir el valor del UID en el Serial Plotter
  serialP(cardValue);

  // Verificar si la tarjeta está registrada
  if (ifIsRegister(cardValue)) {
    displayMessage(F("Ya estás"), F("registrado"));
    delay(1000);
    displayMessage(F("Inténtalo"), F("nuevamente"));
    buzzerTone(250, 250, 0);
    cardRead = false;
    clock();
    return; // Salir de la función si la tarjeta ya está registrada
  }
  
  // Mostrar mensaje en la pantalla y hacer sonidos apropiados
  displayMessage(F("Registrando"), F("..."));
  toRegister(cardValue);
  delay(1000);
  displayMessage(F("Has sido"), F("registrado"));
  buzzerTone(500, 1000, 1);
  cardRead = true;
  cardValue = "";
  clock();
}

void buzzerTone(int t1, int t2, int mode){
  digitalWrite(buzzerPin, HIGH);
  delay(t1);
  digitalWrite(buzzerPin, LOW);
  
  if (mode == 0) {
    delay(t1);
    digitalWrite(buzzerPin, HIGH);
    delay(t1);
    digitalWrite(buzzerPin, LOW);
  }
  
  delay(t2);
}

void displayMessage(const __FlashStringHelper* line1, const __FlashStringHelper* line2) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(line1);
  lcd.setCursor(0, 1);
  lcd.print(line2);
}

void toRegister(String uid) {
  // Cerrar el archivo antes de abrirlo nuevamente
  usuarios.close();
  delay(100);
  
  // Abre el archivo en modo escritura
  usuarios = SD.open(usuariosF, FILE_WRITE);
  delay(100);
  
  if (usuarios) {
    usuarios.print(uid); // Escribe el UID en el archivo
    usuarios.print(",");
    usuarios.flush(); // Verificar que ningún dato quedó a medio camino
    usuarios.close(); // Cierra el archivo después de escribirlo
    Serial.println(F("Datos escritos en el archivo correctamente."));
    clock();
  } else {
    Serial.println(F("Error al abrir el archivo para escritura."));
    clock();
  }
}

bool ifIsRegister(String user) {
  usuarios.close();
  delay(100);
  
  // Abre el archivo en modo lectura
  usuarios = SD.open(usuariosF, FILE_READ);
  delay(100);
  
  if (usuarios) {
    String verifyUser;
    char nextCharacter;

    while (usuarios.available()) {
      nextCharacter = usuarios.read();
      if (nextCharacter == ',') {
        if (verifyUser == user) {
          usuarios.close();
          clock();
          return true;
        }
        verifyUser = ""; // Limpiar la cadena para el siguiente UID
      } else {
        verifyUser += nextCharacter; // Agregar el caracter leído al UID temporal
      }
    }
    
    // Verificar el último UID al final del archivo
    if (verifyUser == user) {
      usuarios.close();
      clock();
      return true;
    }
    usuarios.close();
  }
  clock();
  return false; // El archivo no se pudo abrir o el usuario no está registrado
}
void serialP(String message){
  Serial.println(message);
}

void entryRegis(String uid) {
  RtcDateTime now = Rtc.GetDateTime();
  year = now.Year();
  mes = now.Month();
  dia = now.Day();
  hora = now.Hour();
  minuto = now.Minute();
  
  String completeTime = String(hora) + ":" + String(minuto) + " " + String(dia) + "|" + String(mes) + "|" + String(year);
  
  Serial.println(completeTime);

  registro.close();
  delay(100);
  
  registro = SD.open(registroF, FILE_WRITE); // Abre el archivo en modo escritura
  
  delay(100);
  
  if (registro) {
    registro.print(uid); // Escribe el UID en el archivo
    registro.print(" ");
    registro.print(completeTime); // Agrega la fecha y hora al registro
    registro.print(","); // Agrega una coma para separar los UID
    registro.flush(); // Verificar que ningún dato quedó a medio camino
    registro.close(); // Cierra el archivo después de escribirlo
    Serial.println(F("Datos escritos en el archivo correctamente."));
    clock();
  } else {
    Serial.println(F("Error al abrir el archivo para escritura."));
    clock();
  }
}
void ifIsAlreadyIn(String user) {
  entraron.close();
  delay(100);
  entraron = SD.open(entraronF, FILE_READ); // Abre el archivo en modo lectura
  delay(100);
  if (entraron) {
    String verifyUser;
    char nextCharacter;
    lengthU = user.length()-1;
    while (entraron.available()) {
      nextCharacter = entraron.read();
      toRun = lengthU + entraron.position();
      if (nextCharacter == ',') {
        if (verifyUser == user) {
          entraron.flush();
          entraron.close();
          delay(100);
          removeText(lengthU, toRun);
          displayMessage(F("Hasta luego,"), F("nos vemos!!"));
          buzzerTone(350, 750, 0);
          entraron.flush();
          entraron.close();
        }
        verifyUser = ""; // Limpiar la cadena para el siguiente UID
      } else {
        verifyUser += nextCharacter; // Agregar el caracter leído al UID temporal
      }
    }
    // Verificar el último UID al final del archivo
    if (verifyUser == user) {
      entraron.close();
      removeText(lengthU, toRun);
      displayMessage(F("Hasta luego,"), F("nos vemos!!"));// El usuario estaba en la escuela
      buzzerTone(350, 750, 0);
      clock();
    }
    entraron.close();
  }
  addUser(cardValue);
  displayMessage(F("Bienvenido!!"), F("de vuelta"));
  buzzerTone(500, 1000, 1); // El usuario no estaba en la escuela
  clock();
}
void removeText(int num, int lenghtNum){
  if (!SD.exists(fileF)) {
    file = SD.open(fileF, FILE_WRITE);
    file.close();
    Serial.println(F("fileF ha sido creado"));
  } else {
    Serial.println(F("fileF ya existe"));
  }
  
  entraron.close();
  delay(100);
  entraron = SD.open(entraronF, FILE_READ); // Abre el archivo en modo leer
  file = SD.open(fileF, FILE_WRITE); // Abre el archivo en modo escribir
  delay(100);
  entraron.seek(0);
  while (entraron.available()){
    int actualPos = entraron.position();
    entraron.seek(actualPos);
    if(actualPos < num || actualPos > lenghtNum+1){
      copy = entraron.read();
      file.print(copy);
    } else if(copy == " ") {
      return;
    } else {
      actualPos++;
    }
  }
  
  entraron.flush();
  entraron.close();
  file.flush();
  file.close();
  entraron = SD.open(entraronF, FILE_WRITE); // Abre el archivo en modo escribir
  file = SD.open(fileF, FILE_READ); // Abre el archivo en modo escribir
  while(file.available()){
    copy = file.read();
    entraron.print(copy);
    if(copy == " ") {
      return;
    }
  }
  
  entraron.flush();
  entraron.close();
  file.flush();
  file.close();
  SD.remove("FILE.TXT");
  Serial.print(F("Text removed"));
  clock();
}

void addUser(String user){
  entraron.close();
  delay(100);
  entraron = SD.open(entraronF, FILE_WRITE); // Abre el archivo en modo escribir
  delay(100);
  entraron.print(user);
  entraron.print(",");
  entraron.flush();
  entraron.close();
  Serial.println(user);
  Serial.print(F("User added"));
  clock();
  }

I note this part could be improved:

if (!SD.exists(usuariosF)) {
    usuarios = SD.open(usuariosF, FILE_WRITE);
    usuarios.close();
    Serial.println(F("usuariosF ha sido creado"));
  } else {
    Serial.println(F("usuariosF ya existe"));
    } 
  // Verificar si el archivo "registroF" existe y crearlo si no existe
  if (!SD.exists(registroF)) {
    registro = SD.open(registroF, FILE_WRITE);
    registro.close();
    Serial.println(F("registroF ha sido creado"));
  } else {
    Serial.println(F("registroF ya existe"));
  }

  // Verificar si el archivo "entraronF" existe y crearlo si no existe
  if (!SD.exists(entraronF)) {
    entraron = SD.open(entraronF, FILE_WRITE);
    entraron.close();
    Serial.println(F("entraronF ha sido creado"));
  } else {
    Serial.println(F("entraronF ya existe"));
  }

I can use this:

void initializeFile(const char* filename) {
  if (!SD.exists(filename)) {
    File file = SD.open(filename, FILE_WRITE);
    if (file) {
      file.close();
      Serial.print(filename);
      Serial.println(F(" ha sido creado"));
    } else {
      Serial.print(F("Error al crear "));
      Serial.println(filename);
    }
  } else {
    Serial.print(filename);
    Serial.println(F(" ya existe"));
  }
} 

And then:

initializeFile(usuariosF);
initializeFile(registroF);
initializeFile(entraronF);
#include <MFRC522.h>
#include <Wire.h>
#include <ThreeWire.h>
#include <LiquidCrystal_I2C.h>
#include <RtcDS1302.h>
#include <SD.h>

// Definiciones de pines
#define RST_PIN     9 //RFID
#define SS_PIN      10 //RFID
#define button      2
#define buzzerPin   3
#define cspin       4 //SD

// Variables globales
byte valueButton = 1;
byte minutoA, minuto, mes, dia, hora;
int year, lengthU, toRun;
byte stateButton = 0;
unsigned long tx = 0;
bool cardRead = false;
String cardValue;
char uidBuffer[4];
String copy = "";

// Objetos para componentes
LiquidCrystal_I2C lcd(0x27, 16, 2);
ThreeWire myWire(6, 5, 7);
RtcDS1302<ThreeWire> Rtc(myWire);
MFRC522 mfrc522(SS_PIN, RST_PIN);

// Archivos
File usuarios, registro, entraron, file;
char fileF[] = "FILE.TXT";
char entraronF[] = "ENTRARON.TXT";
char usuariosF[] = "USUARIOS.TXT";
char registroF[] = "REGISTRO.TXT";

void setup() {
  Serial.begin(9600);
  pinMode(cspin, OUTPUT);
  digitalWrite(cspin, HIGH);
  
  pinMode(buzzerPin, OUTPUT);
  pinMode(button, INPUT_PULLUP);
  lcd.begin(16, 2);
  lcd.backlight();
  Rtc.Begin();
  //Configuración para RTC
  /*RtcDateTime currentTime = RtcDateTime(__DATE__ , __TIME__);
  Rtc.SetDateTime(currentTime);
  */
  SPI.begin();
  mfrc522.PCD_Init();

  // Inicializar la tarjeta SD
  if (!SD.begin(cspin)) {
    Serial.println(F("Error initializing SD card."));
    displayMessage(F("Error iniciando"), F("tarjeta SD"));
    while(1){}
  }
  Serial.println(F("SD card initialized successfully."));

  // Esperar para iniciar la comunicación Serial
  while (!Serial);
  mfrc522.PCD_DumpVersionToSerial();
  Serial.println(F("Comunicaión Serial iniciada"));

  initializeFile(usuariosF);
  initializeFile(registroF);
  initializeFile(entraronF);
}

void loop() {
  registerButton();
  clock();
  mainProcess();
}

void clock() {
  // Obtener la fecha y hora actual del RTC
  RtcDateTime now = Rtc.GetDateTime();

  // Obtener los componentes de fecha y hora
  minutoA = now.Minute();

  delay(100);

  // Actualizar los componentes de fecha y hora
  year = now.Year();
  mes = now.Month();
  dia = now.Day();
  hora = now.Hour();
  minuto = now.Minute();

  // Mostrar "Colocar tarjeta" en la primera línea del LCD
  lcd.setCursor(0, 0);
  lcd.print("Colocar tarjeta");

  // Mostrar la fecha y hora en la segunda línea del LCD
  lcd.setCursor(0, 1);
  printDoubleDigit(dia);
  lcd.print("/");
  printDoubleDigit(mes);
  lcd.print("/");
  lcd.print(year);
  lcd.print("|");
  printDoubleDigit(hora);
  lcd.print(":");
  printDoubleDigit(minuto);

  // Actualizar la pantalla solo si hay cambios en la fecha o hora
  if ((minutoA != minuto)) {
    lcd.setCursor(0, 1);
    printDoubleDigit(dia);
    lcd.print("/");
    printDoubleDigit(mes);
    lcd.print("/");
    lcd.print(year);
    lcd.print("|");
    printDoubleDigit(hora);
    lcd.print(":");
    printDoubleDigit(minuto);
  }
}

// Función para imprimir un número de dos dígitos
void printDoubleDigit(int number) {
  if (number < 10) {
    lcd.print("0");
  }
  lcd.print(number);
}

void initializeFile(const char* filename) {
  if (!SD.exists(filename)) {
    File file = SD.open(filename, FILE_WRITE);
    if (file) {
      file.close();
      Serial.print(filename);
      Serial.println(F(" ha sido creado"));
    } else {
      Serial.print(F("Error al crear "));
      Serial.println(filename);
    }
  } else {
    Serial.print(filename);
    Serial.println(F(" ya existe"));
  }
} 

void mainProcess() {
  // Verificar si hay una nueva tarjeta presente en el lector
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Leer el serial de la tarjeta
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  // Inicializar la variable cardValue para almacenar el UID
  cardValue = "";

  // Leer el UID de la tarjeta y almacenarlo en cardValue
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    // Formatear el valor hexadecimal y almacenarlo en el búfer
    sprintf(uidBuffer, "%02X", mfrc522.uid.uidByte[i]);
    // Concatenar el valor del búfer en la variable cardValue
    cardValue += uidBuffer;
  }

  // Imprimir el valor del UID en el Serial Plotter
  serialP(cardValue);

  // Verificar si la tarjeta está registrada
  if (ifIsRegister(cardValue)) {
    ifIsAlreadyIn(cardValue); // Verificar si el usuario ya está adentro
    // Mostrar mensajes en la pantalla y hacer sonidos apropiados
    entryRegis(cardValue);
  } else {
    // Mostrar mensajes en la pantalla y hacer sonidos apropiados
    lcd.noDisplay();
    lcd.clear();
    delay(100);
    lcd.display();
    displayMessage(F("No registrado"), F(""));
    buzzerTone(250, 1000, 0);
    lcd.noDisplay();
    lcd.clear();
    lcd.display();
    displayMessage(F("Para registrarte"), F("Apreta el boton"));
    delay(1000);
    lcd.noDisplay();
    lcd.clear();
    delay(100);
    lcd.display();
    clock();
  }

  // Limpiar el valor de la tarjeta para la siguiente lectura
  cardValue = "";
  mfrc522.PICC_HaltA(); // Finalizar la comunicación con la tarjeta
}

void registerButton() {
  valueButton = digitalRead(button); // Leer si el botón está pulsado
  
  // Si el botón está presionado
  if (valueButton == LOW) {
    stateButton = 1;
    cardRead = false;
    
    // Mostrar mensaje en la pantalla para colocar la tarjeta
    displayMessage(F("Colocar tarjeta"), F("Para registrarse"));
    
    tx = millis(); // Guardar el tiempo actual en 'tx'

    // Esperar hasta que se cumpla el tiempo de registro o se lea una tarjeta
    while (millis() - tx < 10000) {
      toRegis();
      
      // Si se leyó una tarjeta, actualizar la hora en la pantalla y salir del bucle
      if (cardRead == true) {
        clock();
        break;
      }
    }
  }
}

void toRegis() {
  if (cardRead) {
    return;
  }

  // Verificar si hay una nueva tarjeta presente en el lector
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Leer el serial de la tarjeta
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  // Inicializar la variable cardValue para almacenar el UID
  cardValue = "";

  // Leer el UID de la tarjeta y almacenarlo en cardValue
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    // Formatear el valor hexadecimal y almacenarlo en el búfer
    sprintf(uidBuffer, "%02X", mfrc522.uid.uidByte[i]);
    // Concatenar el valor del búfer en la variable cardValue
    cardValue += uidBuffer;
  }

  // Imprimir el valor del UID en el Serial Plotter
  serialP(cardValue);

  // Verificar si la tarjeta está registrada
  if (ifIsRegister(cardValue)) {
    displayMessage(F("Ya estás"), F("registrado"));
    delay(1000);
    displayMessage(F("Inténtalo"), F("nuevamente"));
    buzzerTone(250, 250, 0);
    cardRead = false;
    clock();
    return; // Salir de la función si la tarjeta ya está registrada
  }
  
  // Mostrar mensaje en la pantalla y hacer sonidos apropiados
  displayMessage(F("Registrando"), F("..."));
  toRegister(cardValue);
  delay(1000);
  displayMessage(F("Has sido"), F("registrado"));
  buzzerTone(500, 1000, 1);
  cardRead = true;
  cardValue = "";
  clock();
}

void buzzerTone(int t1, int t2, int mode){
  digitalWrite(buzzerPin, HIGH);
  delay(t1);
  digitalWrite(buzzerPin, LOW);
  
  if (mode == 0) {
    delay(t1);
    digitalWrite(buzzerPin, HIGH);
    delay(t1);
    digitalWrite(buzzerPin, LOW);
  }
  
  delay(t2);
}

void displayMessage(const __FlashStringHelper* line1, const __FlashStringHelper* line2) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(line1);
  lcd.setCursor(0, 1);
  lcd.print(line2);
}

void toRegister(String uid) {
  // Cerrar el archivo antes de abrirlo nuevamente
  usuarios.close();
  delay(100);
  
  // Abre el archivo en modo escritura
  usuarios = SD.open(usuariosF, FILE_WRITE);
  delay(100);
  
  if (usuarios) {
    usuarios.print(uid); // Escribe el UID en el archivo
    usuarios.print(",");
    usuarios.flush(); // Verificar que ningún dato quedó a medio camino
    usuarios.close(); // Cierra el archivo después de escribirlo
    Serial.println(F("Datos escritos en el archivo correctamente."));
    clock();
  } else {
    Serial.println(F("Error al abrir el archivo para escritura."));
    clock();
  }
}

bool ifIsRegister(String user) {
  // Abre el archivo en modo lectura
  usuarios = SD.open(usuariosF, FILE_READ);
  delay(100);
  
  if (usuarios) {
    String verifyUser;
    char nextCharacter;

    while (usuarios.available()) {
      nextCharacter = usuarios.read();
      if (nextCharacter == ',') {
        if (verifyUser == user) {
          usuarios.close();
          return true;
        }
        verifyUser = ""; // Limpiar la cadena para el siguiente UID
      } else {
        verifyUser += nextCharacter; // Agregar el caracter leído al UID temporal
      }
    }
    
    // Verificar el último UID al final del archivo
    if (verifyUser == user) {
      usuarios.close();
      return true;
    }
    usuarios.close();
  }
  clock();
  return false; // El archivo no se pudo abrir o el usuario no está registrado
}
void serialP(String message){
  Serial.println(message);
}

void entryRegis(String uid) {
  RtcDateTime now = Rtc.GetDateTime();
  year = now.Year();
  mes = now.Month();
  dia = now.Day();
  hora = now.Hour();
  minuto = now.Minute();
  String completeTime = String(hora) + ":" + String(minuto) + " " + String(dia) + "|" + String(mes) + "|" + String(year);
  Serial.println(completeTime);
  registro = SD.open(registroF, FILE_WRITE); // Abre el archivo en modo escritura
  delay(50);
  
  if (registro) {
    registro.print(uid); // Escribe el UID en el archivo
    registro.print(" ");
    registro.print(completeTime); // Agrega la fecha y hora al registro
    registro.print(","); // Agrega una coma para separar los UID
    registro.flush(); // Verificar que ningún dato quedó a medio camino
    registro.close(); // Cierra el archivo después de escribirlo
    Serial.println(F("Datos escritos en el archivo correctamente."));
    clock();
  } else {
    Serial.println(F("Error al abrir el archivo para escritura."));
    clock();
  }
}
void ifIsAlreadyIn(String user) {
  entraron = SD.open(entraronF, FILE_READ); // Abre el archivo en modo lectura
  delay(50);
  if (entraron) {
    String verifyUser;
    char nextCharacter;
    lengthU = user.length() + 1; // Agregar 1 para contar la coma también
    int startPos = -1; // Posición inicial del usuario en el archivo
    int endPos = -1; // Posición final del usuario en el archivo
    int pos = 0; // Posición actual en el archivo

    while (entraron.available()) {
      nextCharacter = entraron.read();

      if (nextCharacter == ',' || nextCharacter == '\n') {
        if (verifyUser == user) {
          endPos = pos; // Actualizar la posición final
          break; // Salir del bucle si se encuentra el usuario
        }
        verifyUser = ""; // Limpiar la cadena para el siguiente UID
        startPos = -1; // Reiniciar la posición inicial
      } else {
        if (startPos == -1) {
          startPos = pos; // Guardar la posición inicial
        }
        verifyUser += nextCharacter; // Agregar el caracter leído al UID temporal
      }

      pos++;
    }

    if (endPos != -1) {
      removeText(startPos, (endPos + 1)); // Eliminar el usuario del archivo
      displayMessage(F("Hasta luego,"), F("nos vemos!!"));
      buzzerTone(350, 750, 0);
    } else {
      addUser(cardValue); // Agregar el usuario si no se encontró en el archivo
      displayMessage(F("Bienvenido!!"), F("de vuelta"));
      buzzerTone(500, 1000, 1);
    }

    entraron.close();
  }
  clock();
}
void removeText(int start, int length) {
  File entraronFile = SD.open(entraronF, FILE_READ); // Abrir archivo en modo lectura
  if (!entraronFile) {
    Serial.println(F("Error al abrir el archivo."));
    return;
  }

  String updatedContent = "";
  char character;
  int pos = 0;

  while (entraronFile.available()) {
    character = entraronFile.read();
    if (pos < start || pos >= (start + length)) {
      updatedContent += character;
    }
    pos++;
  }

  entraronFile.close();

  file = SD.open(entraronF, FILE_WRITE); // Abrir archivo en modo escritura
  if (!file) {
    Serial.println(F("Error al abrir el archivo para escritura."));
    return;
  }

  file.print(updatedContent);
  file.close();

  Serial.println(F("Texto eliminado"));
  clock();
}

void addUser(String user){
  entraron = SD.open(entraronF, FILE_WRITE); // Abre el archivo en modo escribir
  delay(100);
  entraron.print(user);
  entraron.print(",");
  entraron.flush();
  entraron.close();
  Serial.println(user);
  Serial.print(F("User added"));
  clock();
  }

The code works, but it only shows me the message on the lcd "Hasta luego, nos vemos"