Save HEX data in an array to send by Xbee

Hi Drmpf, Im no going to Ask anything with Xbee and Softwareserial.
In addition to the time and date, I want to send the value of a voltage that I am going to measure with the Arduino's ADC.
So I did this, I saw some ADC tutorials, I initialized the A0 port where my analog signal is going to enter and then I initialized a variable called voltPanel, this variable is going to contain the real value of the voltage that I am reading.

Then to be able to intrododuce in the data frame, use the same procedure that I saw:

 //FRAME
        size_t idx = 0;
        memmove(fullPacket + idx, packet1, sizeof(packet1));
        idx += sizeof(packet1);
        memmove(fullPacket + idx, packet2, sizeof(packet2));
        idx += sizeof(packet2);
        if ((idx + 19 + 1) > sizeof(fullPacket)) {
          // Si el tamaƱo es incorrecto
          Serial.println(F("fullPacket size wrong"));
          }
        memmove(fullPacket + idx, String(voltPanel), sizeof(String(voltPanel)));
        idx += sizeof(voltPanel);     //HERE 

        memmove(fullPacket + idx, buffer, 20);
        idx += 20;
        // dejar un byte para el cheksum
        if ((idx + 1) != sizeof(fullPacket)) {
          // fullPacket size wrong
          Serial.println(F("fullPacket size wrong"));
          }

This is the actual code

//Librerias
#include <SafeString.h>
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <SoftwareSerial.h>
#include <SPI.h> 
#include <SD.h>

//DataLogeer
const int chipSelect = 4;

//Xbee
SoftwareSerial XBeeSerial(2, 3); //(Rx, TX)
uint8_t Rx_nextByte;
#define START_DELIMITER 0x7E

//ADC
const int ADC = A0;
float voltPanel;

//TRAMAS
byte packet1[] = {0x7E, 0x00, 0x21}; //Byte delimitador y Longitud de trama // variar por el voltaje
byte packet2[] = {0x10, 0x01, 0x00, 0x13, 0xA2, 0x00, 0x40, 0xD7, 0xAE, 0xAD, 0xFF, 0xFE, 0x00, 0x00,}; //Id de trama, tipo de trama, direccion de 64bits, opciones y numero de brincos.
// ,00:11:46,05/01/2021 == 20 chars
byte fullPacket[3 + 14 + 2 + 20 + 1];// (Delimitador + longitud )+ (tipo + direccion)+ voltage + fecha + checksum = 37
                                //  [3 + 14 + 2 + 20 + 1]
char buffer [25]; // 19 la fecha  +1  '\0', 25 bytes de tamaƱo // cambiar porque hay voltaje
#define DEBUG SafeString::Output

//Setup
void setup() {
  Serial.begin(9600);
  XBeeSerial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    while (1);
  }
  Serial.println("card initialized.");
  Serial.println();
  Serial.println("Programacion de ATMega328:");
  SafeString::setOutput(Serial);
  }
 
void loop() {
  Serial.println("Espera a recibir seƱal");
  while (XBeeSerial.available()) {
    Rx_nextByte = XBeeSerial.read();
    if (Rx_nextByte == START_DELIMITER){
      Serial.println("SeƱal recibida");
      
      //RTC
      tmElements_t tm;
      if (RTC.read(tm)) {
        sprintf(buffer, ",%02d:%02d:%02d,%02d/%02d/%04d", tm.Hour , tm.Minute, tm.Second, tm.Day, tm.Month, tmYearToCalendar(tm.Year));
        Serial.println(buffer); // con este print compruebo que la fecha y hora

        //ADC
        //Convertir voltaje a Valor REAL
        voltPanel= ((float(analogRead(ADC))*5.0/1023)*6+10, 2);  //Aqui multiplico por 5 y divido por 1023, 
        //para tener el voltaje que lee el ADC y multiplo por 3 y umo 25, para dar el voltaje real.
        //convertir voltPanel a HEX
         
        //Ingresar este valor a los datos, parte de la horayfecha (xx.xx,00:11:46,05/01/2021)
    
        //Actualizar en el encabezado length y las tramas de arriba.
        //{0x7E, 0x00, 0x27}; //0x27 19 caracteres de fecha + 4bytes del flotante + espacio

        //DATALOGGER 
        String dataString = "";
        dataString += String(voltPanel);
        dataString += String(buffer);
        File dataFile = SD.open("datalog.txt", FILE_WRITE);
        // if the file is available, write to it:
        if (dataFile) {
          dataFile.println(dataString);
          dataFile.close();
          // print to the serial port too:
          Serial.println(dataString);  
          
        //FRAME
        size_t idx = 0;
        memmove(fullPacket + idx, packet1, sizeof(packet1));
        idx += sizeof(packet1);
        memmove(fullPacket + idx, packet2, sizeof(packet2));
        idx += sizeof(packet2);
        if ((idx + 19 + 1) > sizeof(fullPacket)) {
          // Si el tamaƱo es incorrecto
          Serial.println(F("fullPacket size wrong"));
          }
        memmove(fullPacket + idx, String(voltPanel), sizeof(String(voltPanel)));
        idx += sizeof(voltPanel);  
        memmove(fullPacket + idx, buffer, 20);
        idx += 20;
        // dejar un byte para el cheksum
        if ((idx + 1) != sizeof(fullPacket)) {
          // fullPacket size wrong
          Serial.println(F("fullPacket size wrong"));
          }

        int chksum = 0;
        for (size_t i = 3; i < idx; i++) {// comienza despues del tamaƱo dela trama
           chksum += fullPacket[i]; // comienza a sumar 
            }
        chksum = (0xff & chksum);
        chksum = (0xff) & (0xff - chksum);
        fullPacket[idx] = chksum;
        
        idx++; // El tamao total de la trama
        if (idx != sizeof(fullPacket)) {
           Serial.println(F("fullPacket size error"));
            }

        // Imprime el paquete
        size_t printSize = idx * 3; // ..<space>
        cSF(sfPacket, printSize);
        for (size_t i = 0; i < idx; i++) {
          //sfPacket += "0x";
        if (fullPacket[i] < 16) {
           sfPacket += '0'; 
           }
        sfPacket.print(fullPacket[i], HEX); 
        sfPacket.print(' ');
           }
        Serial.println(sfPacket);

        XBeeSerial.write (fullPacket , idx);   
        delay(5000);
       }
      }    
     }
    }    
   } 
void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
  Serial.print(number);
}

A problem with that is, if your volts are <10 the length will be incorrect
The latest SafeString library V4.1.1 has a fixed witdth print function
sfString.print(value, decimalPoints, fixedWidth)
That always padd/reduces the output to fixedWidth if volts <10 it adds a space if volts >99 it reduces the decmial places to 1

Try this

byte packet1[] = {0x7E, 0x00, 0x27}; //Byte delimitador y Longitud de trama  // updated length
byte packet2[] = {0x10, 0x01, 0x00, 0x13, 0xA2, 0x00, 0x41, 0x4E, 0xF1, 0x24, 0xFF, 0xFE, 0x00, 0x00,}; //Id de trama, tipo de trama, direccion de 64bits, opciones y numero de brincos.

// 00:11:46 05/01/2021 == 19 chars
// volts, xx.xx, == 6 chars  
byte fullPacket[3 + 14 + 6 + 19 + 1];// (start + length) + (type+address) + volts + date + checksum = 43
// length of data, including mode and address but witout checksum
// without checksum length is 43-4 = 39 = 0x27
. . . 
   sprintf(buffer, "%02d:%02d:%02d %02d/%02d/%04d", 5 , 6, 7, 8, 9, 2021);
    Serial.println(buffer); // con este print compruebo que la fecha y hora
    // build fullpacket
    size_t idx = 0;
    memmove(fullPacket + idx, packet1, sizeof(packet1));
    idx += sizeof(packet1);
    memmove(fullPacket + idx, packet2, sizeof(packet2));
    idx += sizeof(packet2);
    if ((idx + 6 + 19 + 1) > sizeof(fullPacket)) {
      // fullPacket size wrong
      Serial.println(F("fullPacket size wrong"));
    }
    float voltPanel= ((float(analogRead(ADC))*5.0/1023)*6+10, 2);  //Aqui multiplico por 5 y divido por 1023, 
    cSF(sfVolts,6); // for xx.xx == 5 and comma 1 == 6
    sfVolts.print(voltPanel,2,5); // "xx.xx" or " x.xx" or "-x.xx" or "xxx.x" but ALWAYS exactly 5 chars 
    sfVolts.print(','); // add the comma
    memmove(fullPacket + idx, sfVolts.c_str(), 6); // always 6 long
    idx += 6;
    if ((idx + 19 + 1) > sizeof(fullPacket)) {
      // fullPacket size wrong
      Serial.println(F("fullPacket size wrong"));
    }
    
    memmove(fullPacket + idx, buffer, 19);
    idx += 19;
    // should have just one byte left for check sum
    if ((idx + 1) != sizeof(fullPacket)) {
      // fullPacket size wrong
      Serial.println(F("fullPacket size wrong"));
    }
. . . 

I try and show me this.

Oh yes yes I understand the value, The initial idea is to read the voltage of a solar panel, the panel generates between 40 and 25 volts, make a circuit with some power operations amplifiers that reduce the voltage to be able to measure it in the ADC. when I have 40 volts, at the ADC input I will have 4.9 close to 5 V and when I have 25 volts I will have a value close to 3 volts at the ADC input.

Did you updated to SafeString V4.1.1?

Thanks for the reminder.
And yes, the program its working well.
thank you so much Drmpf

Hi Mr. Drmpf, men Im sorry for this. I was tested the program with ADC on. Last time I only compile in the IDLE of Arduino and dont show any error. But today I do a Test, and look haha I think the data it much larger than the operation I dont know.

This is the code I run Today:

#include <SD.h>
#include <SPI.h> 
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <SafeString.h>
#include <SoftwareSerial.h>

//DATALOGGER
const int chipSelect = 4;

//ADC
const int ADCpin = A0;
float voltPanel; 

//XBEE
SoftwareSerial XBeeSerial(2, 3); //(Rx, TX)
uint8_t Rx_nextByte;
#define START_DELIMITER 0x7E

//TRAMAS
byte packet1[] = {0x7E, 0x00, 0x21}; //Byte delimitador y Longitud de trama // variar por el voltaje
byte packet2[] = {0x10, 0x01, 0x00, 0x13, 0xA2, 0x00, 0x40, 0xD7, 0xAE, 0xAD, 0xFF, 0xFE, 0x00, 0x00,}; //Id de trama, tipo de trama, direccion de 64bits, opciones y numero de brincos.
// 00:11:46 05/01/2021 == 19 chars
byte fullPacket[3 + 14 + 19 + 1];// (Delimitador + longitud )+ (tipo + direccion)+ voltage + fecha + checksum = 37
                                //  [3 + 14 + 4 + 19 + 1]
char buffer [25]; // 19 la fecha  +1  '\0', 25 bytes de tamaƱo // cambiar porque hay voltaje
#define DEBUG SafeString::Output

//SETUP
void setup() {
  Serial.begin(9600);
  delay(100);
  XBeeSerial.begin(9600);
  delay(100);
 // if (!SD.begin(4)) {
   // Serial.println("initialization failed!");
    //while (1);
  //}
  //Serial.println("initialization done.");
  delay(2000);
  Serial.println();
  Serial.println("MACHOTE DEL CODIGO PRINCIPAL");
  Serial.println("-------------------");

  SafeString::setOutput(Serial);
  }
  
//LOOP
void loop() {
  Serial.println("Espera a recibir seƱal");
  while (XBeeSerial.available()) {
    Rx_nextByte = XBeeSerial.read();
    if (Rx_nextByte == START_DELIMITER){
      //Inicia a mandar datos Si el primer Byte que recive de la PC es un 0x7E.
      Serial.println("SeƱal recibida");
      
      //RTC
      tmElements_t tm;
      if (RTC.read(tm)) {
        sprintf(buffer, "%02d:%02d:%02d,%02d/%02d/%04d", tm.Hour , tm.Minute, tm.Second, tm.Day, tm.Month, tmYearToCalendar(tm.Year));
        Serial.println(buffer); // Compruebo la fecha y hora actual.

        //Inicializo un sumador de caracteres
        size_t idx = 0;
        memmove(fullPacket + idx, packet1, sizeof(packet1));
        idx += sizeof(packet1);
        memmove(fullPacket + idx, packet2, sizeof(packet2));
        idx += sizeof(packet2);
        if ((idx + 6 + 19 + 1) > sizeof(fullPacket)) {
          Serial.println(F("Paquete completo esta mal."));
          }
       float voltPanel= ((float(analogRead(ADCpin))*5.0/1023)*6+10, 2);//Obtengo del ADC el voltaje
       //Realizo una operacion y = mx + b para obtener el valor real del Panel Fotovoltaico.
       cSF(sfVolts,6); //Voltage xx.xx == 5 y coma 1 == 6
       sfVolts.print(voltPanel,2,5); // "xx.xx" o " x.xx" o "-x.xx" o "xxx.x"  
       sfVolts.print(','); 
       memmove(fullPacket + idx, sfVolts.c_str(), 6); // Siempre van a ser 6 caracteres de largo
       idx += 6;
       if ((idx + 19 + 1) > sizeof(fullPacket)) {
         Serial.println(F("Paquete completo esta mal 2."));
         }
    
       memmove(fullPacket + idx, buffer, 19);
       idx += 19;
       //Dejo un ultimo byte para tener el Checksum.
       if ((idx + 1) != sizeof(fullPacket)) {
         Serial.println(F("Paquete completo esta mal 3."));
          }

       //Se le da el formato al paquete completo
       size_t printSize = idx * 3; 
       cSF(sfPacket, printSize);
       for (size_t i = 0; i < idx; i++) {
         //sfPacket += "0x";
         if (fullPacket[i] < 16) {
         sfPacket += '0'; 
           }
         sfPacket.print(fullPacket[i], HEX); 
         sfPacket.print(' ');
           }
         Serial.println(sfPacket);//Se comprueba que el formato de la Trama API.

         XBeeSerial.write (fullPacket , idx);//Se envia la trama del sensor a la PC.
         
         //DATALOGGER 
        String dataString = "";
        dataString += String(voltPanel);
        dataString += String(buffer);
        File dataFile = SD.open("TensionDePanel.txt", FILE_WRITE);
        if (dataFile) {
          dataFile.println(dataString);
          dataFile.close();
          // print to the serial port too:
          Serial.println(dataString);

         
         delay(5000);
       }
      }    
     }
    }    
   }               
void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
  Serial.print(number);
}

Im sorry for the Spanish tho.

I going to show you my set to. I have Arduino UNO to run the code, the xbee is power on with 5V direct to the UNO, and I use Arduino Mega to power on RTC and MicroSD with 5V and I take the 3.3V to simulate the output of my sensor (cause I dont have it with me)

image

You are out of memory :frowning:
Not enough stack for
cSF(sfPacket, printSize);
suggest you just skip using the SafeString output

Try

       for (size_t i = 0; i < idx; i++) {
         if (fullPacket[i] < 16) {
           Serial.print('0'); 
          }
         Serial.print(fullPacket[i], HEX); 
         Serial.print(' ');
       }
       Serial.println();

When I tested before this new change, the black window say this:

" Sketch uses 24756 bytes (76%) of the program storage space. The maximum is 32256 bytes.
Global variables use 1512 bytes (73%) of dynamic memory, leaving 536 bytes for local variables. The maximum is 2048 bytes.
"

Going to try with this new option.

With the new option the black window say this:

"Sketch uses 24428 bytes (75%) of the program storage space. The maximum is 32256 bytes.
Global variables use 1508 bytes (73%) of dynamic memory, leaving 540 bytes for local variables. The maximum is 2048 bytes."

So I tested and have this

Which doesn't work the way I expect.
Well, as I had explained before, I managed to send an API frame with the time and date from the arduino to the computer. Then what I did was a small loop at the beginning so that it was constantly asking if it has received a message, the moment it receives it, it begins to send information without stopping, which is what I am looking for for the sensor.

But when it comes to involving the ADC in the programming, something strange happens besides that I run out of memory haha but it is not sending me information once I send it a signal from the computer. Just like he only sent me a plot haha

You still have the cSF(sfPacket, ) statement.
Remove that.
Not a good idea to open/close the SD every loop.
Just output the ADC to Serial to see what is happening.

Morning Mr.Drmpf

  • I remove cSF(sfPacket) from ADC to.
    And print the ADC voltage. I see there a Problem, because the outut voltage is 3.3V and the operation, give me de real voltage float voltPanel= ((float(analogRead(ADCpin))*5.0/1023)*6+10, 2); in this case 29,8V.

And i have this:

The use of memory going down by the way look.
The Sketch uses 18094 bytes (56%) of the program storage space. The maximum is 32256 bytes.
Global variables use 1455 bytes (71%) of dynamic memory, leaving 593 bytes for local variables. The maximum is 2048 bytes.

I run this code, I remove cSF(sfPacket, ) statement. and print:

#include <SD.h>
#include <SPI.h> 
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <SafeString.h>
#include <SoftwareSerial.h>

//DATALOGGER
const int chipSelect = 4;

//ADC
const int ADCpin = A0;
float voltPanel; 

//XBEE
SoftwareSerial XBeeSerial(2, 3); //(Rx, TX)
uint8_t Rx_nextByte;
#define START_DELIMITER 0x7E

//TRAMAS
byte packet1[] = {0x7E, 0x00, 0x21}; //Byte delimitador y Longitud de trama // variar por el voltaje
byte packet2[] = {0x10, 0x01, 0x00, 0x13, 0xA2, 0x00, 0x40, 0xD7, 0xAE, 0xAD, 0xFF, 0xFE, 0x00, 0x00,}; //Id de trama, tipo de trama, direccion de 64bits, opciones y numero de brincos.
// 00:11:46 05/01/2021 == 19 chars
byte fullPacket[3 + 14 + 19 + 1];// (Delimitador + longitud )+ (tipo + direccion)+ voltage + fecha + checksum = 37
                                //  [3 + 14 + 4 + 19 + 1]
char buffer [25]; // 19 la fecha  +1  '\0', 25 bytes de tamaƱo // cambiar porque hay voltaje
#define DEBUG SafeString::Output

//SETUP
void setup() {
  Serial.begin(9600);
  delay(100);
  XBeeSerial.begin(9600);
  delay(100);
 // if (!SD.begin(4)) {
   // Serial.println("initialization failed!");
    //while (1);
  //}
  //Serial.println("initialization done.");
  delay(2000);
  Serial.println();
  Serial.println("MACHOTE DEL CODIGO PRINCIPAL");
  Serial.println("-------------------");

  SafeString::setOutput(Serial);
  }
  
//LOOP
void loop() {
  Serial.println("Espera a recibir seƱal");
  while (XBeeSerial.available()) {
    Rx_nextByte = XBeeSerial.read();
    if (Rx_nextByte == START_DELIMITER){
      //Inicia a mandar datos Si el primer Byte que recive de la PC es un 0x7E.
      Serial.println("SeƱal recibida");
      
      //RTC
      tmElements_t tm;
      if (RTC.read(tm)) {
        sprintf(buffer, "%02d:%02d:%02d,%02d/%02d/%04d", tm.Hour , tm.Minute, tm.Second, tm.Day, tm.Month, tmYearToCalendar(tm.Year));
        Serial.println(buffer); // Compruebo la fecha y hora actual.

        //Inicializo un sumador de caracteres
        size_t idx = 0;
        memmove(fullPacket + idx, packet1, sizeof(packet1));
        idx += sizeof(packet1);
        memmove(fullPacket + idx, packet2, sizeof(packet2));
        idx += sizeof(packet2);
        if ((idx + 6 + 19 + 1) > sizeof(fullPacket)) {
          Serial.println(F("Paquete completo esta mal."));
          }
       float voltPanel= ((float(analogRead(ADCpin))*5.0/1023)*6+10, 2);//Obtengo del ADC el voltaje
       //Realizo una operacion y = mx + b para obtener el valor real del Panel Fotovoltaico.
       Serial.print("ADC voltage:"); 
       Serial.print(voltPanel); // "xx.xx" o " x.xx" o "-x.xx" o "xxx.x"  
       Serial.println(','); 
       
      // memmove(fullPacket + idx, sfVolts.c_str(), 6); // Siempre van a ser 6 caracteres de largo
       //idx += 6;
       //if ((idx + 19 + 1) > sizeof(fullPacket)) {
         //Serial.println(F("Paquete completo esta mal 2."));
         //}
    
       memmove(fullPacket + idx, buffer, 19);
       idx += 19;
       //Dejo un ultimo byte para tener el Checksum.
       if ((idx + 1) != sizeof(fullPacket)) {
         Serial.println(F("Paquete completo esta mal 3."));
          }

       //Se le da el formato al paquete completo
       size_t printSize = idx * 3; 
       
       for (size_t i = 0; i < idx; i++) {
         if (fullPacket[i] < 16) {
           Serial.print('0'); 
          }
         Serial.print(fullPacket[i], HEX); 
         Serial.print(' ');
       }
       Serial.println();//Se comprueba que el formato de la Trama API.

         XBeeSerial.write (fullPacket , idx);//Se envia la trama del sensor a la PC.
         
         //DATALOGGER 
        String dataString = "";
        dataString += String(voltPanel);
        dataString += String(buffer);
        File dataFile = SD.open("TensionDePanel.txt", FILE_WRITE);
        if (dataFile) {
          dataFile.println(dataString);
          dataFile.close();
          // print to the serial port too:
          Serial.println(dataString);

         
         delay(5000);
       }
      }    
     }
    }    
   }               
void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
  Serial.print(number);
}

Oh and I see, the arduino print this Serial.println(F("Paquete completo esta mal."));
So that mean something bad happen , no the same size of the full packet

        size_t idx = 0;
        memmove(fullPacket + idx, packet1, sizeof(packet1));
        idx += sizeof(packet1);
        memmove(fullPacket + idx, packet2, sizeof(packet2));
        idx += sizeof(packet2);
        if ((idx + 6 + 19 + 1) > sizeof(fullPacket)) {
          Serial.println(F("Paquete completo esta mal."));
          }

I see that you used the F macro in the serial print in your last fragment. You have several other prints that could use it too if you're still trying to reduce your RAM footprint.

1 Like

You removed this bit of code so the packet size is not correct now

Also change your error msgs so you can tell with section fails e.g.

Serial.println(F("1 Paquete completo esta mal."));
. . .
Serial.println(F("2 Paquete completo esta mal."));
. . .
Serial.println(F("3 Paquete completo esta mal."));

etc

My question here is the following:
To save memory, I am going to remove cSF.
So I remove:

cSF(sfVolts,6); //Voltage xx.xx == 5 y coma 1 == 6
       sfVolts.print(voltPanel,2,5); // "xx.xx" o " x.xx" o "-x.xx" o "xxx.x"  
       sfVolts.print(','); 

And I change for this:

       Serial.print(voltPanel); // "xx.xx" o " x.xx" o "-x.xx" o "xxx.x"  
       Serial.print(','); 

But in the next stament, I have this:

memmove(fullPacket + idx, sfVolts.c_str(), 6); 

I remove << sfVolts>> so I change for voltPanel. But Its say that we need a function there.

Regarding opening and closing the datalogger.
I saw several videos on YouTube where it is advised to open, save and close the file when using the datalogger.

Just in case this is the video.

At the moment in testing this code, everything is happening fast, a couple of seconds between each message sent, to see how it works. But in reality I plan to send a message every 30 seconds.

But thank you for the advice, and why I should do that ?

is small so should be a memory problem. You can move it to a be global and then see the actual memory used.
If you have it as a global you need to clear() it before each use to discard the last lot of data
i.e.

       sfVolts.clear(); // remove last text
       sfVolts.print(voltPanel,2,5); // "xx.xx" o " x.xx" o "-x.xx" o "xxx.x"  
       sfVolts.print(',');`

File flush() seems to do the same job as file.close() just without closing.
Another post suggested lots of SD open/close was not a good idea, but try and see what happens.

1 Like

Hi drmpf, I'm back. I am also designing the PCB where I am going to put the ATmega328 has been crazy.

Well let's see, I'm looking for an example of flush to know how to use it.
On the other hand I have run the program again with the global variable and look I have had this result.

#include <SD.h>
#include <SPI.h> 
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <SafeString.h>
#include <SoftwareSerial.h>

//DATALOGGER
const int chipSelect = 4;

//ADC
const int ADCpin = A0;
float voltPanel; 
cSF(sfVolts,6); 

//XBEE
SoftwareSerial XBeeSerial(2, 3); //(Rx, TX)
uint8_t Rx_nextByte;
#define START_DELIMITER 0x7E

//TRAMAS
byte packet1[] = {0x7E, 0x00, 0x21}; //Byte delimitador y Longitud de trama // variar por el voltaje
byte packet2[] = {0x10, 0x01, 0x00, 0x13, 0xA2, 0x00, 0x40, 0xD7, 0xAE, 0xAD, 0xFF, 0xFE, 0x00, 0x00,}; //Id de trama, tipo de trama, direccion de 64bits, opciones y numero de brincos.
// 00:11:46 05/01/2021 == 19 chars
byte fullPacket[3 + 14 + 19 + 1];// (Delimitador + longitud )+ (tipo + direccion)+ voltage + fecha + checksum = 37
                                //  [3 + 14 + 4 + 19 + 1]
char buffer [25]; // 19 la fecha  +1  '\0', 25 bytes de tamaƱo // cambiar porque hay voltaje
#define DEBUG SafeString::Output

//SETUP
void setup() {
  Serial.begin(9600);
  delay(100);
  XBeeSerial.begin(9600);
  delay(100);
 // if (!SD.begin(4)) {
   // Serial.println("initialization failed!");
    //while (1);
  //}
  //Serial.println("initialization done.");
  delay(2000);
  Serial.println();
  Serial.println("CODIGO PRINCIPAL");
  Serial.println("-------------------");

  SafeString::setOutput(Serial);
  }
  
//LOOP
void loop() {
  Serial.println("Espera a recibir seƱal");
  while (XBeeSerial.available()) {
    Rx_nextByte = XBeeSerial.read();
    if (Rx_nextByte == START_DELIMITER){
      //Inicia a mandar datos Si el primer Byte que recive de la PC es un 0x7E.
      Serial.println("SeƱal recibida");
      
      //RTC
      tmElements_t tm;
      if (RTC.read(tm)) {
        sprintf(buffer, "%02d:%02d:%02d,%02d/%02d/%04d", tm.Hour , tm.Minute, tm.Second, tm.Day, tm.Month, tmYearToCalendar(tm.Year));
        Serial.println(buffer); // Compruebo la fecha y hora actual.

        //Inicializo un sumador de caracteres
        size_t idx = 0;
        memmove(fullPacket + idx, packet1, sizeof(packet1));
        idx += sizeof(packet1);
        memmove(fullPacket + idx, packet2, sizeof(packet2));
        idx += sizeof(packet2);
        if ((idx + 6 + 19 + 1) > sizeof(fullPacket)) {
          Serial.println(F("1 Paquete completo esta mal."));
          }
       float voltPanel= ((float(analogRead(ADCpin))*5.0/1023)*6+10, 2);//Obtengo del ADC el voltaje
       //Realizo una operacion y = mx + b para obtener el valor real del Panel Fotovoltaico.
       sfVolts.clear(); // remove last text //Voltage xx.xx == 5 y coma 1 == 6
       sfVolts.print(voltPanel,2,5); // "xx.xx" o " x.xx" o "-x.xx" o "xxx.x"  
       sfVolts.print(','); 
       memmove(fullPacket + idx, sfVolts.c_str(), 6); // Siempre van a ser 6 caracteres de largo
       idx += 6;
       if ((idx + 19 + 1) > sizeof(fullPacket)) {
         Serial.println(F("2 Paquete completo esta mal."));
         }
    
       memmove(fullPacket + idx, buffer, 19);
       idx += 19;
       //Dejo un ultimo byte para tener el Checksum.
       if ((idx + 1) != sizeof(fullPacket)) {
         Serial.println(F("3 Paquete completo esta mal."));
          }

       //Se le da el formato al paquete completo
       size_t printSize = idx * 3; 
       cSF(sfPacket, printSize);
       for (size_t i = 0; i < idx; i++) {
         if (fullPacket[i] < 16) {
           Serial.print('0'); 
          }
         Serial.print(fullPacket[i], HEX); 
         Serial.print(' ');
       }
       Serial.println();//Se comprueba que el formato de la Trama API.

         XBeeSerial.write (fullPacket , idx);//Se envia la trama del sensor a la PC.
         
         //DATALOGGER 
        String dataString = "";
        dataString += String(voltPanel);
        dataString += String(buffer);
        File dataFile = SD.open("TensionDePanel.txt", FILE_WRITE);
        if (dataFile) {
          dataFile.println(dataString);
          dataFile.close();
          // print to the serial port too:
          Serial.println(dataString);

         
         delay(5000);
       }
      }    
     }
    }    
   }               
void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
  Serial.print(number);
}

Ok ok
I see that in the header I didnt change the number of the bytes. So I changed.

At least eliminate the errors that we had at the beginning, those of "1 Paquete completo esta mal"