Reading Calibration Variables from usb drive to Arduino Giga R1

I am trying to read values from a Txt file located on a USB drive to variables on the Arduino giga. I am trying to do this to allow the calibration values for the sensors I am using to be changed without needing to re-upload the Arduino sketch. I want to do this with a USB stick since I already use one to store data from the sensor, and the Giga does not have an onboard Micro SD card. I have been trying to build off of one of the example sketches shown below that reads a file from the USB and prints it to serial, but I am having trouble figuring out how to transition that to variables that I can use.

/*
  Portenta - FileRead

  The sketch shows how to mount an usb storage device and how to
  read from an existing file.
  to use this sketch create a .txt file named Arduino.txt,
  in your storage device and write some content inside.

  The circuit:
   - Portenta H7

  This example code is in the public domain.
*/

#include <Arduino_USBHostMbed5.h>
#include <DigitalOut.h>
#include <FATFileSystem.h>

USBHostMSD msd;
mbed::FATFileSystem usb("usb");

// If you are using a Portenta Machine Control uncomment the following line
mbed::DigitalOut otg(PB_14, 0);
 
void setup() {
  Serial.begin(115200);
  while (!Serial);

  delay(2500);
  Serial.println("Starting USB File Read example...");

  // if you are using a Max Carrier uncomment the following line
  //start_hub();

  while (!msd.connect()) {
    delay(1000);
  }

  Serial.println("Mounting USB device...");
  int err =  usb.mount(&msd);
  if (err) {
    Serial.print("Error mounting USB device ");
    Serial.println(err);
    while (1);
  }
  Serial.print("read done ");
  mbed::fs_file_t file;
  struct dirent *ent;
  int dirIndex = 0;
  int res = 0;
  Serial.println("Open file..");
  FILE *f = fopen("/usb/Arduino.txt", "r+");
  char buf[256];
  Serial.println("File content:");

  while (fgets(buf, 256, f) != NULL) {
    Serial.print(buf);
  }

  Serial.println("File closing");
  fflush(stdout);
  err = fclose(f);
  if (err < 0) {
    Serial.print("fclose error:");
    Serial.print(strerror(errno));
    Serial.print(" (");
    Serial.print(-errno);
    Serial.print(")");
  } else {
    Serial.println("File closed");
  }
}

void loop() {
    delay(1000);
    // handle disconnection and reconnection
    if (!msd.connected()) {
        msd.connect();
    }
}

Hi @mcknick26.

You mention "calibration values" (plural) so I assume the file contains multiple values which you want to use individually. So the first thing to tackle is how to extract the values individually from the file. This will depend on how you have formatted the data in the file. You are reading the data from the file using fgets:

 while (fgets(buf, 256, f) != NULL) {
    Serial.print(buf);
  }

https://cplusplus.com/reference/cstdio/fgets/

stores them as a C string into str until (num -1) characters have been read or either a newline or the end-of-file is reached

So if you have one value on each line in the file, then you will get one value on each iteration of that while loop. If you have a different data format (e.g., INI, JSON) then some further parsing code will be needed.

Once you have extracted an individual value, the next problem is that you likely need to use it in integer form, but it is in string form. You can use the appropriate conversion function for this purpose:

https://cplusplus.com/reference/string/#functions

The Values that I am looking to pull in look like this in the text file.

Aa2=-0.00000054019
Aa1=0.02461
Aa0=-8.50114
Ba2=0.00000028496
Ba1=0.02113
Ba0=-7.19828
Ca2=-0.00000023275
Ca1=0.02648
Ca0=-9.30802

I am trying to take these values, read them with the fgets function, and store each as an entry in the array (not sure if that is possible due to the way arrays store characters). Then, later in the program, store these entries to the corresponding variables. there might be a better way to do it but I haven't been able to figure one out. I would also need to, when converting the strings to long, remove the first bit, but that should remain a known value and be pretty easy to do.

OK, so you will need to do a bit of parsing of the lines returned by fgets before converting the values from strings to numerical form.

The naive approach would be to use character position, since we can see that the first three characters are the identifier, the fourth is the delimiter, and all following are the numeric data. A more flexible approach would be to use the strtok function to split the lines on the = delimiter:

https://cplusplus.com/reference/cstring/strtok/

Ok thank you, I have things working now. Just one last question, is there a better way to take the parsed values from the array and store them to variable names. Right now as shown bellow I am just using the order that the variables are declared and then storing them to the variables accordingly. it works and should be fine but it might not be the most reliable way to do it. I have attached a copy of the code bellow.

/*
  Portenta - FileRead

  The sketch shows how to mount an usb storage device and how to
  read from an existing file.
  to use this sketch create a .txt file named Arduino.txt,
  in your storage device and write some content inside.

  The circuit:
   - Portenta H7

  This example code is in the public domain.
*/

#include <Arduino_USBHostMbed5.h>
#include <DigitalOut.h>
#include <FATFileSystem.h>
#include <string>

USBHostMSD msd;
mbed::FATFileSystem usb("usb");

// If you are using a Portenta Machine Control uncomment the following line
mbed::DigitalOut otg(PB_14, 0);
 
void setup() {
  Serial.begin(115200);
  while (!Serial);

  delay(2500);
  Serial.println("Starting USB File Read example...");

  // if you are using a Max Carrier uncomment the following line
  //start_hub();

  while (!msd.connect()) {
    delay(1000);
  }

  Serial.println("Mounting USB device...");
  int err =  usb.mount(&msd);
  if (err) {
    Serial.print("Error mounting USB device ");
    Serial.println(err);
    while (1);
  }
  Serial.print("read done ");
  mbed::fs_file_t file;
  struct dirent *ent;
  int dirIndex = 0;
  int res = 0;
  Serial.println("Open file..");
  FILE *f = fopen("/usb/config.txt", "r+");
  char buf[256];
  Serial.println("File content:");
int i=0;
double Cal[10];
 while (fgets(buf, sizeof(buf), f) != NULL) {
    Serial.println(buf);
    char *token = strtok(buf, "=");
    if (token != nullptr) {
      token = strtok(NULL, "="); // Get the second token
      if (token != nullptr) {
        Cal[i] = strtod(token, nullptr);
        Serial.println(Cal[i]);
      } else {
        Serial.println("Failed to parse value");
      }
    } else {
      Serial.println("Failed to parse line");
    }
    i++;
  }

double Aa2=Cal[0],Aa1=Cal[1], Aa0=Cal[2];
double Ba2=Cal[3], Ba1=Cal[4], Ba0=Cal[5];
double Ca2=Cal[6], Ca1=Cal[7], Ca0=Cal[8];


  Serial.println("File closing");
  fflush(stdout);
  err = fclose(f);
  if (err < 0) {
    Serial.print("fclose error:");
    Serial.print(strerror(errno));
    Serial.print(" (");
    Serial.print(-errno);
    Serial.print(")");
  } else {
    Serial.println("File closed");
  }
}

void loop() {
    delay(1000);
    // handle disconnection and reconnection
    if (!msd.connected()) {
        msd.connect();
    }
}

You can save the data returned by the first call of strtok and then use strcmp to determine which of the identifiers it is:

https://cplusplus.com/reference/cstring/strcmp/

  char *identifier = strtok(buf, "=");
  if (identifier != nullptr) {
    char *value = strtok(NULL, "=");  // Get the second token
    if (value != nullptr) {
      if (strcmp(identifier, "Aa0") == 0) {
        // Code to handle Aa0 calibration
      } else if (strcmp(identifier, "Aa1") == 0) {
        // Code to handle Aa1 calibration

If you want to make it more elegant, you can use an array of the identifier strings, something like this struct-based approach:

const byte maximumIdentifierLength = 3;  // All identifiers are <= this number of characters.
struct calibration_t {
  char identifier[maximumIdentifierLength + 1];
  double value;
};

calibration_t calibrations[] = {
  { "Aa0", 0 },
  { "Aa1", 0 },
  // and so on...
};

[...]

  char *identifier = strtok(buf, "=");
  if (identifier != nullptr) {
    char *value = strtok(NULL, "=");  // Get the second token
    if (value != nullptr) {
      for (byte calibrationIndex = 0; calibrationIndex < (sizeof(calibrations) / sizeof(calibrations[0])); calibrationIndex++) {
        if (strcmp(identifier, calibrations[calibrationIndex].identifier) == 0) {
          calibrations[calibrationIndex].value = strtod(value, nullptr);
          break;
        }
      }
    }
  }

So I have the proposed sloution working in a separate program

/* Portenta - FileRead

  The sketch shows how to mount an usb storage device and how to
  read from an existing file.
  to use this sketch create a .txt file named Arduino.txt,
  in your storage device and write some content inside.

  The circuit:
   - Portenta H7

  This example code is in the public domain.
*/

#include <Arduino_USBHostMbed5.h>
#include <DigitalOut.h>
#include <FATFileSystem.h>
//#include <string>

USBHostMSD msd;
mbed::FATFileSystem usb("usb");

// If you are using a Portenta Machine Control uncomment the following line
mbed::DigitalOut otg(PB_14, 0);
 

  struct cal_val {
    const char* identifier;
    double value;
  };

  
   cal_val calibrations[] = {
    {"Aa2", 0},
    {"Aa1", 0},
    {"Aa0", 0},
    {"Ba2", 0},
    {"Ba1", 0},
    {"Ba0", 0},
    {"Ca2", 0},
    {"Ca1", 0},
    {"Ca0", 0},
  };

void setup() {
  Serial.begin(115200);
  while (!Serial);

  delay(2500);
  Serial.println("Starting USB File Read example...");

  // if you are using a Max Carrier uncomment the following line
  //start_hub();

  while (!msd.connect()) {
    delay(1000);
  }

  Serial.println("Mounting USB device...");
  int err =  usb.mount(&msd);
  if (err) {
    Serial.print("Error mounting USB device ");
    Serial.println(err);
    while (1);
  }




  Serial.print("read done ");
  mbed::fs_file_t file;
  struct dirent *ent;
  int dirIndex = 0;
  int res = 0;
  Serial.println("Open file..");
  FILE* f = fopen("/usb/config.txt", "r+");
  char buf[256];
  Serial.println("File content:");
int i=0;
double Cal[10];

while (fgets(buf, sizeof(buf), f) != NULL) {
   char *identifier = strtok(buf, "=");
  if (identifier != nullptr) {
    char *value = strtok(NULL, "=");  // Get the second token
    if (value != nullptr) {
      for (byte calibrationIndex = 0; calibrationIndex < (sizeof(calibrations) / sizeof(calibrations[0])); calibrationIndex++) {
        if (strcmp(identifier, calibrations[calibrationIndex].identifier) == 0) {
          calibrations[calibrationIndex].value = strtod(value, nullptr);
          break;
        }
      }
    }
  

    } else {
      Serial.println("Failed to parse line");
    }
    i++;
  }

Serial.println(get_cal_val("Aa1"),10);


  Serial.println("File closing");
  fflush(stdout);
  err = fclose(f);
  if (err < 0) {
    Serial.print("fclose error:");
    Serial.print(strerror(errno));
    Serial.print(" (");
    Serial.print(-errno);
    Serial.print(")");
  } else {
    Serial.println("File closed");
  }
}

void loop() {
    delay(1000);
    // handle disconnection and reconnection
    if (!msd.connected()) {
        msd.connect();
    }
}

double get_cal_val(const char* id) {
    for (byte calibrationIndex = 0; calibrationIndex < (sizeof(calibrations) / sizeof(calibrations[0])); calibrationIndex++) {
        if (strcmp(id, calibrations[calibrationIndex].identifier) == 0) {
            return calibrations[calibrationIndex].value;
        }
    }
    // If the identifier is not found, return a default value (you can choose an appropriate value)
    return -1;
}

I have been trying to now move that into the main program I have been writing shown here:

/*
Written By: Nick Mckenna
Date Last Modified: 2024-07-08

Purpose: This code is designed to read the input from 3 flexi force A401 sensors and display them on an arduino giga.
In additionaly to displaying the output the software can also log and save the messured data. well it will be able to eventually.


*/
#include "Arduino.h"


//Librarys Included Required to make the Giga R1 Display function:
// Arduino Mbed OS giga Boards// installed in the board manager
//just look for arduino giga display libary and install all depents, then go and manually set lvgl version
#include "Arduino_H7_Video.h"//https://github.com/arduino/ArduinoCore-mbed/tree/main/libraries/Arduino_H7_Video
#include "lvgl.h" //version: 8.3.11//https://github.com/lvgl/lvgl //powes the graphics and GUI
#include "Arduino_GigaDisplayTouch.h"//https://www.arduino.cc/reference/en/libraries/arduino_gigadisplaytouch/
#include "lv_conf.h"
//C:\Users\NM27Eng\AppData\Local\Arduino15\packages\arduino\hardware\mbed_giga\4.1.1\libraries\Arduino_H7_Video\src\lv_conf.h
//is the location to change config stuff for the program. havent been able to get it to use the one in lib folder

//Librarys Required to make the on board RTC function
#include "mbed.h"
#include <mbed_mktime.h>

//Library needed to enable comunication with the usb thumb drive
#include <PluggableUSBHID.h>
//#include <USBHID_Types.h>
#include <USBKeyboard.h>
#include <USBMouse.h>
#include <USBMouseKeyboard.h>
#include <Arduino_USBHostMbed5.h>
#include <DigitalOut.h>
#include <FATFileSystem.h>

 

//#include "lv_conf.h" //includes the lv_conf.h file into sketch
//"" means sketch will look in sketch folder first.
/*
hardware info:
Arduino Pin    ->   Breakout Pin
--------------------------------
A0            ->    input from sensor 1
A1            ->    Input from Sensor 2
A2            ->    Input grom Sensor 3

*/

int flexiAin=A0, flexiBin=A1, flexiCin=A2;//pins for input from each sensor
int polrate = 1000;//number of milliseconds between each sensor poll/log
//Linear Scaleing Values to convert the sensor inputs into pounds
// Variables are in the Form of lbf=mx+B, where the Ms and Bs are denoted by A,B,or C
double Aa2=0,Aa1=1, Aa0=0;
double Ba2=0, Ba1=1, Ba0=0;
double Ca2=0, Ca1=1, Ca0=0;
double Cal[10];//array for calibration values
//Specifying height and width of display
Arduino_H7_Video Display(800, 480, GigaDisplayShield);
Arduino_GigaDisplayTouch TouchDetector;//needed to enable touch for display.
//bool RTC_page_visible = false;



  
   
   

//global variables

lv_obj_t * RTC_time;//text on RTC portion
lv_obj_t * LOAD;// pointer for total load of fixture
lv_obj_t * SA;//pointer for sensor A
lv_obj_t * SB;// pointer for sensor B
lv_obj_t * SC;// pointer for sensor C
lv_obj_t * ELPTIME;// pointer for total elapsed time
lv_obj_t * menu;// Pointer for current menu
lv_obj_t * RTC_page;
lv_obj_t * ELPTIME_btn;//
lv_timer_t * Sensor_Read;
lv_obj_t * I_A_LABEL;
lv_obj_t * I_B_LABEL;
lv_obj_t * I_C_LABEL;
lv_obj_t * I_A;
lv_obj_t * I_B;
lv_obj_t * I_C;
lv_obj_t * USB_btn;
lv_obj_t * USB_btn_label;

static lv_style_t  logbtn_pr;
static lv_style_t logbtn;
static lv_style_t logbtn_no_drive;
static lv_style_t S_A;
static lv_style_t S_B;
static lv_style_t S_C;

char drive_name[11]="B_DATA_LOG";
USBHostMSD msd;
mbed::FATFileSystem usb(drive_name);
mbed::fs_file_t file;
mbed::DigitalOut otg(PB_14, 0);
struct dirent *ent;
int err;//for if usb throws error
FILE* f;
bool datalog = 0;
bool msdcon = false;//true when msd has been connected false when msd has not been connected.
unsigned int ELPTIME_timer;
unsigned int ELPTIME_start;
unsigned int ELPTIME_cnt;
char Char_RTC_time[32];
char RTC_Date[17];

//data structure for importing values from usb drive
  struct cal_val {
    const char* identifier;
    double value;
  };

  
   cal_val calibrations[] = {
    {"Aa2", 0},
    {"Aa1", 0},
    {"Aa0", 0},
    {"Ba2", 0},
    {"Ba1", 0},
    {"Ba0", 0},
    {"Ca2", 0},
    {"Ca1", 0},
    {"Ca0", 0},
  };


void setup() {



  Display.begin();// Initlizes display
  TouchDetector.begin();//Initalizes Touch interface

  Serial.begin(9600);// Initalizes Serial Monitor 
  analogReadResolution(12);//enables the analog to digital converter to do #bit numbers
  Serial.println("setup");
 

     static lv_style_t style;
  lv_style_init (&style);
  lv_style_set_text_font(&style, &lv_font_montserrat_28);
  lv_style_set_text_color(&style, lv_color_make(0, 0, 0));
     static lv_style_t lstyle;
  lv_style_init (&lstyle);
  lv_style_set_text_font(&lstyle, &lv_font_montserrat_48);
  lv_style_set_text_color(&lstyle, lv_color_make(0, 0, 0));
  //static lv_style_t logbtn_pr;
    lv_style_init (&logbtn_pr);
    lv_style_set_outline_width(&logbtn_pr,30);
    lv_style_set_bg_color(&logbtn_pr, lv_palette_main(LV_PALETTE_RED));
    
  //static lv_style_t logbtn;
    lv_style_init (&logbtn);
    lv_style_set_outline_width(&logbtn,0);
    lv_style_set_bg_color(&logbtn, lv_palette_main(LV_PALETTE_GREEN));

  //static lv_style_t logbth_no_drive
  lv_style_set_bg_color(&logbtn, lv_palette_main(LV_PALETTE_GREEN));
  int radius = 250;
  // lv_style_t S_A 
    lv_style_init (&S_A);
    lv_style_set_radius(&S_A, radius);
    lv_style_set_text_font(&S_A, &lv_font_montserrat_28);
    lv_style_set_bg_color(&S_A, lv_color_make(0, 255, 0));
    lv_style_set_text_color(&S_A, lv_color_make(0, 0, 0));

  
  //Style for S_B, button circle indication
    lv_style_init(&S_B);
    lv_style_set_radius(&S_B,radius);
    lv_style_set_text_font(&S_B, &lv_font_montserrat_28);
    lv_style_set_bg_color(&S_B, lv_color_make(0, 255, 0));
    lv_style_set_text_color(&S_B, lv_color_make(0, 0, 0));

  //style for S_C, Button circle indication
    lv_style_init(&S_C);
    lv_style_set_radius(&S_C, radius);
    lv_style_set_text_font(&S_C, &lv_font_montserrat_28);
    lv_style_set_bg_color(&S_C, lv_color_make(0, 255, 0));
    lv_style_set_text_color(&S_C, lv_color_make(0, 0, 0));
  
  //Initilizes the pins required for the USB-A port
  pinMode(PA_15, OUTPUT);  //enable the USB-A port
  digitalWrite(PA_15, HIGH);//Powers the usb port
  //usb_connect();



  //defines the partent "main page" and acoscated stuff
  menu = lv_menu_create(lv_scr_act());
    lv_obj_set_size(menu,Display.width(), Display.height());
    lv_obj_center(menu);

   
    lv_obj_t * cont;
   


 //RTC page
   RTC_page = lv_menu_page_create(menu, NULL);
  cont = lv_label_create(RTC_page);
lv_obj_set_size(RTC_page,Display.width(), Display.height());
lv_obj_center(RTC_page);
  lv_obj_clear_flag(RTC_page, LV_OBJ_FLAG_SCROLLABLE);//removes ability to scroll button in grid

  
     lv_obj_t * SET_YEAR = lv_textarea_create(RTC_page);
      lv_obj_align(SET_YEAR, LV_ALIGN_TOP_MID, -100, -100);
      lv_obj_set_size(SET_YEAR, 200,105);
      lv_textarea_set_placeholder_text(SET_YEAR, "YYYY");
    //lv_obj_center(SET_YEAR);
    //lv_obj_add_event_cb(RTC_btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
     lv_textarea_set_one_line(SET_YEAR, true);
    
  




// main menu
lv_obj_t * main_page = lv_menu_page_create(menu, NULL);
  lv_obj_clear_flag(main_page, LV_OBJ_FLAG_SCROLLABLE);//removes ability to scroll button in the object "main menu"
  
  
  
  static lv_coord_t col_dsc[] = { 370, 115 ,115, 115, LV_GRID_TEMPLATE_LAST };//sets size and number of colombs on main page
  static lv_coord_t row_dsc[] = { 105, 105, 105, 105, LV_GRID_TEMPLATE_LAST };//sets size and number of rows on main page
  

  lv_obj_t* grid = lv_obj_create(main_page);
  lv_obj_set_grid_dsc_array(grid, col_dsc, row_dsc);
  lv_obj_set_size(grid, Display.width(), Display.height());
  lv_obj_clear_flag(grid, LV_OBJ_FLAG_SCROLLABLE);//removes ability to scroll button in the object "main menu"
  
  
  //top left
   lv_obj_t* obj; 
    obj = lv_obj_create(grid);
    lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, 0, 1,  //column
                       LV_GRID_ALIGN_STRETCH, 0, 3);      //row
    lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);//removes ability to scroll button in grid

    I_A = lv_btn_create(obj);
      lv_obj_add_style(I_A, &S_A, 0);
      lv_obj_set_size(I_A, 125,125);
            lv_obj_align(I_A, LV_ALIGN_CENTER, 0, -80);
      I_A_LABEL = lv_label_create(I_A);
      lv_obj_center(I_A_LABEL);
      lv_label_set_text(I_A_LABEL, "A\n66666");
      lv_obj_set_style_text_align(I_A_LABEL, LV_TEXT_ALIGN_CENTER, 0);//Sets the text of the label to center aligned

    I_B = lv_btn_create(obj);
      lv_obj_add_style(I_B, &S_B, 0);
      lv_obj_set_size(I_B, 125,125);
      lv_obj_align(I_B, LV_ALIGN_CENTER, 80, 80);
      I_B_LABEL = lv_label_create(I_B);
      lv_obj_center(I_B_LABEL);
      lv_obj_set_style_text_align(I_B_LABEL, LV_TEXT_ALIGN_CENTER, 0);//Sets the text of the label to center aligned

    I_C = lv_btn_create(obj);
      lv_obj_add_style(I_C, &S_C, 0);
      lv_obj_set_size(I_C, 125,125);
      lv_obj_align(I_C, LV_ALIGN_CENTER, -80, 80);
      I_C_LABEL = lv_label_create(I_C);
      lv_obj_center(I_C_LABEL);
      lv_obj_set_style_text_align(I_C_LABEL, LV_TEXT_ALIGN_CENTER, 0);//Sets the text of the label to center aligned
 
 
  //bottom left-RTC_time
  obj = lv_obj_create(grid);
  lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, 0, 1,  //column
                       LV_GRID_ALIGN_STRETCH, 3, 1);      //row
   lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);//removes ability to scroll button in grid
  
  
  //Button
  //lv_obj_t * RTC_time;
  lv_obj_t * RTC_btn = lv_btn_create(obj);
  lv_obj_set_size(RTC_btn, 370,105);
  lv_obj_center(RTC_btn);
  lv_obj_add_event_cb(RTC_btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
  lv_menu_set_load_page_event(menu, RTC_btn, RTC_page);
  

  

  lv_timer_t * timer = lv_timer_create(get_rtc, 1000, NULL);
  lv_timer_ready(timer);

 
  RTC_time = lv_label_create(RTC_btn);

  lv_label_set_text(RTC_time, "0000-00-00 00:00:00");//sets text to somthing before the RTC kicks in with the actual value
  lv_obj_add_style(RTC_time, &style, 0);
  lv_obj_center(RTC_time);                     

//top right-main load value
//format for grid cells is (begining quadrant, amout to stretch cell)
  obj = lv_obj_create(grid);
  lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, 1, 3,  //column
                       LV_GRID_ALIGN_STRETCH, 0, 2);      //row
        lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);//removes ability to scroll button in grid
lv_obj_t * LOAD_btn = lv_btn_create(obj);
  LOAD = lv_label_create(LOAD_btn);
  
    lv_obj_set_size(LOAD_btn, 370,220);
    lv_obj_center(LOAD_btn);
    lv_obj_add_event_cb(LOAD_btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
    
            lv_label_set_text(LOAD, "000"); //sets text to somthing before the RTC kicks in with the actual value
      lv_obj_add_style(LOAD, &lstyle, 0);
      lv_obj_center(LOAD);


/*lv_timer_t */ Sensor_Read = lv_timer_create(S_Read, polrate, NULL);
lv_timer_ready(Sensor_Read);
// right side, 3 individual sensor inputs, 


  //Sensor 1
    obj = lv_obj_create(grid);
    lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, 1, 1,  //column
                       LV_GRID_ALIGN_STRETCH, 2, 1);      //row
    lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);//removes ability to scroll button in grid
  //  static lv_style_t style;
  //lv_style_init (&style);
  //lv_style_set_text_font(&style, &lv_font_montserrat_28);// Dont need this since font size was set above?
  
   lv_obj_t * SA_btn = lv_btn_create(obj);
  SA = lv_label_create(SA_btn);
  
    lv_obj_set_size(SA_btn, 115,105);
    lv_obj_center(SA_btn);
    lv_obj_set_style_text_align(SA, LV_TEXT_ALIGN_CENTER, 0);//Sets the text of the label to center aligned
    lv_obj_add_event_cb(SA_btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
      // static lv_style_t style;
      //lv_style_init (&style);
      //lv_style_set_text_font(&style, &lv_font_montserrat_28);// Dont need this since font size was set above?
            lv_label_set_text(SA, "000"); //sets text to somthing before the RTC kicks in with the actual value
      lv_obj_add_style(SA, &style, 0);
      lv_obj_center(SA);

   //sensor 2
    obj = lv_obj_create(grid);
    lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, 2, 1,  //column
                       LV_GRID_ALIGN_STRETCH, 2, 1);      //row  
      lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);//removes ability to scroll button in grid
    lv_obj_t * SB_btn = lv_btn_create(obj);
  SB = lv_label_create(SB_btn);
    lv_obj_set_style_text_align(SB, LV_TEXT_ALIGN_CENTER, 0);//Sets the text of the label to center aligned
    lv_obj_set_size(SB_btn, 115,105);
    lv_obj_center(SB_btn);
    lv_obj_add_event_cb(SB_btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
    
            lv_label_set_text(SB, "000"); //sets text to somthing before the RTC kicks in with the actual value
      lv_obj_add_style(SB, &style, 0);
      lv_obj_center(SB);

                       

  //Sensor 3
    obj = lv_obj_create(grid);
    lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, 3, 1,  //column
                       LV_GRID_ALIGN_STRETCH, 2, 1);      //row
      lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);//removes ability to scroll button in grid
lv_obj_t * SC_btn = lv_btn_create(obj);
  SC = lv_label_create(SC_btn);
    lv_obj_set_style_text_align(SC, LV_TEXT_ALIGN_CENTER, 0);//Sets the text of the label to center aligned
    lv_obj_set_size(SC_btn, 115,105);
    lv_obj_center(SC_btn);
    lv_obj_add_event_cb(SC_btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
    
            lv_label_set_text(SC, "000"); //sets text to somthing before the RTC kicks in with the actual value
      lv_obj_add_style(SC, &style, 0);
      lv_obj_center(SC);

// usb drive installed
obj = lv_obj_create(grid);
    lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, 1, 1,  //column
                       LV_GRID_ALIGN_STRETCH, 3, 1);      //row
      lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);//removes ability to scroll button in grid

  USB_btn = lv_btn_create(obj);
 USB_btn_label = lv_label_create(USB_btn);
  lv_obj_add_style(USB_btn, &logbtn, 0);
    lv_obj_set_size(USB_btn, 115,105);
    lv_obj_center(USB_btn);
   
    lv_obj_add_event_cb(USB_btn, USBbtn_event_cb, LV_EVENT_CLICKED, NULL);
    //lv_obj_add
   //lv_obj_add_style(ELPTIME, &style, 0);
    //lv_obj_add_style(ELPTIME_btn,&btn_pr, LV_STATE_PRESSED);
    
            lv_label_set_text(USB_btn_label, LV_SYMBOL_USB LV_SYMBOL_REFRESH); //sets text to somthing before the RTC kicks in with the actual value
      lv_obj_add_style(USB_btn_label,&style, 0);
      lv_obj_center(USB_btn_label);

//bottom right, start and stop data collection

  obj = lv_obj_create(grid);
    lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, 2, 2,  //column
                       LV_GRID_ALIGN_STRETCH, 3, 1);      //row
      lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);//removes ability to scroll button in grid

  ELPTIME_btn = lv_btn_create(obj);
  ELPTIME = lv_label_create(ELPTIME_btn);
  lv_obj_add_style(ELPTIME_btn, &logbtn, 0);
    lv_obj_set_size(ELPTIME_btn, 370,105);
    lv_obj_center(ELPTIME_btn);
    //lv_obj_add_flag(ELPTIME_btn, LV_OBJ_FLAG_CHECKABLE);
    lv_obj_add_event_cb(ELPTIME_btn, btn_envent_cb, LV_EVENT_CLICKED, NULL);
    //lv_obj_add
   //lv_obj_add_style(ELPTIME, &style, 0);
    //lv_obj_add_style(ELPTIME_btn,&btn_pr, LV_STATE_PRESSED);
    
            lv_label_set_text(ELPTIME, "start"); //sets text to somthing before the RTC kicks in with the actual value
      lv_obj_add_style(ELPTIME, &style, 0);
      lv_obj_center(ELPTIME);
       

lv_menu_set_page(menu, main_page);
}



void loop() {
  lv_timer_handler();//continuously updates the display and GUI. 
}



// Call back function that is called when button is clicked.
static void btn_event_cb(lv_event_t * e) //keeping this here for refrence right now but no longer needed largly
{

  Serial.println("boop");
  
  //lv_menu_set_page(menu, RTC_page);
  
}

static void btn_envent_cb(lv_event_t * ELPTIME_btn)
  {
    /*
  lv_obj_t * ob = lv_event_get_target(ELPTIME_btn);
  char filename[40];
  if(msd.connect()==true){
  
  if(datalog == 0){
     //mbed::fs_file_t file;
    //lv_style_init (&logbtn_pr);
    lv_obj_add_style(ob, &logbtn_pr, 0);
    datalog=1;
    snprintf(filename,40,"%s/Sensor_log_%s.txt",drive_name, RTC_Date);
    Serial.println(filename);
    lv_label_set_text(ELPTIME, "0:00:00");
    f = fopen(filename,"w+");

    lv_timer_ready(Sensor_Read);//imeditely calls function S_Read and bypasses poll rate of the S_Read function
    } else{
    //lv_style_init (&logbtn);
    lv_obj_add_style(ob, &logbtn, 0);
    datalog = 0;
    lv_label_set_text(ELPTIME, "start");

    fclose(f);


    ELPTIME_timer=0;
    ELPTIME_start=0;
    ELPTIME_cnt=0;
    }
  }else{
  }*/
  //datalog != datalog;//toggles the log variable to indicate if the button was pressed or not
}



// gets the current time from the RTC module and then displays it on the RTC_time, label
static void get_rtc(lv_timer_t * timer) {
     
    tm t;
    _rtc_localtime(time(NULL), &t, RTC_4_YEAR_LEAP_YEAR_SUPPORT);
    
    strftime(Char_RTC_time, 32, "%Y-%m-%d %k:%M:%S", &t);
    lv_label_set_text(RTC_time, Char_RTC_time);
    strftime(RTC_Date, 17, "%Y-%m-%d %k-%M", &t);
    
    
    //return String(buffer);
  

  }



void S_Read(lv_timer_t * Sensor_Read)
  {
  //reads the value of sensors A,B, and C
   int A, Alb, B, Blb, C, Clb, total, diffA,  diffB, diffC;
   float avg;
   float perdiffA,perdiffB, perdiffC;
  
   tm t;
  
    A = analogRead(flexiAin);
    B = analogRead(flexiBin);
    C = analogRead(flexiCin);

    Alb = ((Aa2*(A^2))+(Aa1*A)+Aa0);
    Blb = ((Ba2*(B^2))+(Ba1*B)+Ba0);
    Clb = ((Ca2*(C^2))+(Ca1*C)+Ca0);

    total = (Alb+Blb+Clb);//calcultes the total force being mesured
    //Serial.println(total);
    avg=((A+B+C)/3);
    
    diffA = int((abs(A-avg)/avg)*255);
    diffB = int((abs(B-avg)/avg)*255);
    diffC = int((abs(C-avg)/avg)*255);
    
    perdiffA = (((A-avg)/avg)*100);
    perdiffB = ((B-avg)/avg)*100;
    perdiffC = ((C-avg)/avg)*100;
       
    // Convert integer values to strings
    char As[12], Bs[12], Cs[12], Ts[10], buffer[40];
    snprintf(As, 10, "A\n%d", Alb);
    snprintf(Bs, 10, "B\n%d", Blb);
    snprintf(Cs, 10, "C\n%d", Clb);
    snprintf(Ts, 10, "%d lb",total);
    

    //Color of S_A indcator
    lv_style_set_bg_color(&S_A, lv_color_make(2*diffA, 255-2*diffA, 0));//sets the color of S_A based on how far off A is from average
    lv_obj_invalidate(I_A);// tells lvgl that the style asocated with I_A has changed and that it needs to be reloaded. 
    lv_style_set_bg_color(&S_B, lv_color_make(2*diffB, 255-2*diffB, 0));//sets the color of S_A based on how far off A is from average
    lv_obj_invalidate(I_B);// tells lvgl that the style asocated with I_A has changed and that it needs to be reloaded. 
    lv_style_set_bg_color(&S_C, lv_color_make(2*diffC, 255-2*diffC, 0));//sets the color of S_A based on how far off A is from average
    lv_obj_invalidate(I_C);// tells lvgl that the style asocated with I_A has changed and that it needs to be reloaded. 
  
    // Update labels with the string representations of the integer values

   
    lv_label_set_text_fmt(I_A_LABEL,"A\n%.2f%%", perdiffA);
    lv_label_set_text_fmt(I_B_LABEL,"B\n%.2f%%", perdiffB);
    lv_label_set_text_fmt(I_C_LABEL,"C\n%.2f%%", perdiffC);
    lv_label_set_text(SA, As);
    lv_label_set_text(SB, Bs);
    lv_label_set_text(SC, Cs);
    lv_label_set_text(LOAD, Ts);
    //Serial.println("%c, %c, %c",As, Bs,Cs );
    //Serial.println(Bs);
    //Serial.println(Cs);

  if(datalog==0 && ELPTIME_cnt==0){
    
  }else if(datalog==1 && ELPTIME_cnt==0){
    ELPTIME_start= time(NULL);
    snprintf(buffer,50,"Time,Elapsed Time,A,B,C,Total");
    Serial.println(buffer);
    fprintf(f,"%s\n",buffer);
    snprintf(buffer,50,"%s,%d,%d,%d,%d,%d\r",Char_RTC_time,ELPTIME_cnt,Alb,Blb,Clb,total);
    Serial.println(buffer);
    fprintf(f,"%s\n",buffer);
    ELPTIME_cnt++;
    fflush(stdout);
  }else if(datalog==1 && ELPTIME_cnt>0){
       snprintf(buffer,50,"%s,%d,%d,%d,%d,%d\r",Char_RTC_time,ELPTIME_cnt,Alb,Blb,Clb,total);
    Serial.println(buffer);
    fprintf(f,"%s\n",buffer);
    fflush(stdout);
    ELPTIME_cnt++;

    _rtc_localtime((time(NULL)-ELPTIME_start), &t, RTC_4_YEAR_LEAP_YEAR_SUPPORT);
    
    strftime(buffer, 16, "%k:%M:%S",&t);

  lv_label_set_text(ELPTIME, buffer);
  
  }else{
    Serial.println();
  //usb_connect();

  }
}


// my atempt at making a function to do make the boxes, seemed easir to just copy and paste
/*void make_btn(lv_obj_t * obj, lv_obj_t * label,lv_obj_t * btn, lv_style_t * style ,int x, int y)
{  
   //lv_obj_t * btn = lv_btn_create(obj);
  //lv_label_set_text(RTC_time, "YEEEHAW");
 
    lv_obj_set_size(btn, x, y);
    lv_obj_center(btn);
    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
      //  static lv_style_t style;
     label = lv_label_create(btn);
  
      lv_label_set_text(label, "000"); //sets text to somthing before the RTC kicks in with the actual value
      lv_obj_add_style(label, &style, 0);
      lv_obj_center(label);

}

*/
static void USBbtn_event_cb(lv_event_t * USB_btn){
while (!msd.connect()) {
    delay(1000);
  }

  Serial.println("Mounting USB device...");
  int err =  usb.mount(&msd);
  if (err) {
    Serial.print("Error mounting USB device ");
    Serial.println(err);
    while (1);
  }




  Serial.print("read done ");
  mbed::fs_file_t file;
  struct dirent *ent;
  int dirIndex = 0;
  int res = 0;
  Serial.println("Open file..");
  FILE* f = fopen("/usb/config.txt", "r+");
  char buf[100];
  Serial.println("File content:");
int i=0;
double Cal[10];

while (fgets(buf, sizeof(buf), f) != NULL) {
  //while (i<8){
    Serial.println(i);
   char *identifier = strtok(buf, "=");
  if (identifier != nullptr) {
    char *value = strtok(NULL, "=");  // Get the second token
    if (value != nullptr) {
      for (byte calibrationIndex = 0; calibrationIndex < (sizeof(calibrations) / sizeof(calibrations[0])); calibrationIndex++) {
        if (strcmp(identifier, calibrations[calibrationIndex].identifier) == 0) {
          calibrations[calibrationIndex].value = strtod(value, nullptr);
          break;
        }
      }
    }
  

    } else {
      Serial.println("Failed to parse line");
    }
    i++;
  }
  if (msd.connected()){
  fclose(f);
  Aa2=get_cal_val("Aa2");
  Aa1=get_cal_val("Aa1");
  Aa0=get_cal_val("Aa0");
  Ba2=get_cal_val("Ba2");  
  Ba1=1; 
  Ba0=0;
  Ca2=0;
  Ca1=1;
  Ca0=0;
  Serial.println(Aa2);
  }else{
  Aa2=0;
  Aa1=1;
  Aa0=0;
  Ba2=0; 
  Ba1=1; 
  Ba0=0;
  Ca2=0;
  Ca1=1;
  Ca0=0;
  }

}


double get_cal_val(const char* id) {
    for (byte calibrationIndex = 0; calibrationIndex < (sizeof(calibrations) / sizeof(calibrations[0])); calibrationIndex++) {
        if (strcmp(id, calibrations[calibrationIndex].identifier) == 0) {
            return calibrations[calibrationIndex].value;
        }
    }
    // If the identifier is not found, return a default value (you can choose an appropriate value)
    return -1;
}
`
For whatever reason, everything works until the program hits the fgets that is supposed to parse the calibration data on line 646. I think it probably has something to do with how I have something declared since this isn't the only spot where I am opening and closing files, but I am at a bit of a loss. I tried commenting on just about everything except for the button press event to get the new data, but it still had the same issue. 

Any suggestions would be greatly appreciated.