Read file function in separate .cpp file

Ok so I have a specific question. I am making measuring system that is based on Uno microcontroller. I have code splitted in to parts in separate .cpp files so is more readable and easier to organize. The thing that is bugging me at the momment is how to read data from SD card where I have stored some parameters that are needed to bring the sistem to life in a case of power shortage so in other words I have parameters if arduino restarts.

I am flexible in the way how the data is storred in the LogFile on SD card as it will be generated on the first start of the system and later on will be used just as a backup. I am aware of the EEPROM option but I would rather have the backup on the SD as it is planned to be used later on for other things.

Currently I have data storred on the SD card in a form like:

1, 10

so it is like CSV. The other wat that I tought of saving parameter values was like:

x = 1
y = 10

I do not know which way is better but probably the first one. I will post the current code bellow.

This is how my .cpp looks like

// libraries
#include <Arduino.h>
#include "FileManagement.h"
#include <SPI.h>
#include <SD.h>

FileManagement::FileManagement(String file_name){
    _file_name = file_name;
}

int *FileManagement::read(){
    File myFile;

    while (!Serial) {
    }
    Serial.print("Initializing SD card...");

    if (!SD.begin(10)) {
        Serial.println("Initialization failed!");
        while (1);
    }
    Serial.println("Initialization done.");
    myFile = SD.open(_file_name);
    if (myFile)
    {
        Serial.println("Reading from ");
        Serial.println(_file_name);
        while (myFile.available())
        {
        char inChar = myFile.read();  //get a character
        if (inChar == '\n') //if it is a newline
        {
            parseRecord(recordNum);
            recordNum++;
            charNum = 0;  //start again at the beginning of the array record
        }
        else
        {
            aRecord[charNum] = inChar;  //add character to record
            charNum++;  //increment character index
            aRecord[charNum] = '\0';  //terminate the record
        }
        }
        myFile.close();
        Serial.println("Done.");
    }
    else
    {
        Serial.println("Error opening!");
        Serial.print(_file_name);
    }

    int cycles = String(parameterArray[0]).toInt();
    int baseline = String(parameterArray[1]).toInt();

    static int return_data[2];
    return_data[0] = cycles;
    return_data[1] = baseline;
    return return_data;
}


void FileManagement::parseRecord(byte index)
        {
        char * ptr;
        ptr = strtok(aRecord, " = ");  //find the " = "
        ptr = strtok(NULL, ""); //get remainder of text
        strcpy(parameterArray[index], ptr + 2); //skip 2 characters and copy to array
        }

And this is what I have in the setup()

  //LogFile 
  SD.begin();

  if (SD.exists(logfile_path))
  {
    int* logdata = LogFile.read();
    total_cycles = String(logdata[0]).toInt();
    force_baseline_value = String(logdata[1]).toInt();
  }else
  {
    //First time creating the file
    LogFile.write(0, 0);
  }

The problem is probably in pointers as I can not change the value of the variable in the main after assigning the value to the variable from the file. Can someone please help me how to create separate file with class methods to perform reading file data.

Thanks in advance.

Welcome to the forum.

Could you show all your code ? and can you turn on extra messages for the compiler and show us the compiler output ?

A static variable as member of a class makes the class less flexible. I think that only one object can be created or else they get mixed up.

So the data is read into variable 'inChar' which is added to 'aRecord' which is converted to 'parameterArray', which is converted to a String object, which converted to integers, which is converted to a static integer array, which is converted to a integer pointer, which is converted to a String object.

During those seven conversions, you don't print the data, so you don't know if they are valid and where it fails.

The SD.begin() function is usually called just once in setup(). You seem to call it every time you want to read something from a file.

I put the global variables in the main *.ino file. It is always the first tab on the left. When I make more tabs, then I put less important functions in the tabs in other *.ino files. Specific "library-alike" functions and classes can be put in *.h and *.cpp files, but then you have to specify precisely which *.h file to include where. The Arduino pre-processor can do a lot more than in the past, but sometimes it fails.

I suggest to make the class flexible and don't use any static variables there. Declare the variable or object that holds the data in the main *.ino file.
Parameters "by reference" is a nice way to set more than one variable.
Maybe you better rewrite all that code.

are these ascii char strings?

you might consider using

s, storing/reading data as binary. this is an example of named data

I can share more code, no problem if that would be needed for better understanding. The biggest problem is that I am not native to C programing. I am mechanical engeneer and am learning it on the way. I know Python but here everything is different. I am trying to adopt C logic. I know that a lot of operations that I do to get what I want can probably be avoided with smarter code but I do not know how to do it.

The final result that I would like to get would be to assign those 2 intigers to variable. As I understand now in C I can not return arrays from functions or better said function can have single output. The intigers are dependent on the system and will not be equal for all systems. That is why I can not make them constant as they will have to be read based on the system configuration.

They can be stored as ascii strings. As I said it does not matter how they are storred. If there is better option for reading them I can alter the writing method for LogFile.

consider following (fragment of larger file)

presumably it's inevitable that things will expand and change, so adopting something more flexible may save time in future

#include "Arduino.h"

#include "cfg.h"
#include "eng.h"
#include "utils.h"
#include "vars.h"

const char *cfgFname = "/spiffs/koala.cfg";

const char *cfgMagic = "TLV";

// -------------------------------------
enum CfgId_e : byte {
    C_NONE,

    C_VER  = 1,

    C_NAME,
    C_SSID,
    C_PASS,
    C_HOST,
    C_PORT,

    C_LOCO,

    C_L_00 = 100, C_L_01, C_L_02, C_L_03, C_L_04,
    C_L_05,       C_L_06, C_L_07, C_L_08, C_L_09,
    C_L_10,       C_L_11, C_L_12, C_L_13, C_L_14,
    C_L_15,       C_L_16, C_L_17, C_L_18, C_L_19,

    C_E_00 = 200, C_E_01, C_E_02, C_E_03, C_E_04,
    C_E_05,       C_E_06, C_E_07, C_E_08, C_E_09,
    C_E_10,       C_E_11, C_E_12, C_E_13, C_E_14,
    C_E_15,       C_E_16, C_E_17, C_E_18, C_E_19,
};

struct CfgHdr_s {
    CfgId_e id;
    byte    size;
};

#define HDR_SIZE    sizeof(CfgHdr_s)

// -------------------------------------
// stored (configurable) variable description
enum { V_NONE, V_STR, V_INT, V_LOCO, V_ENG };

struct CfgVar_s {
    CfgId_e     id;
    void       *p;
    byte        nByte;
    byte        type;
    const char *desc;
};

// -------------------------------------
//  stored variables

CfgVar_s cfgVarTbl [] = {
    { C_NAME, (void*)   name,       MAX_CHAR,       V_STR,  "name" },

    { C_SSID, (void*)   ssid,       MAX_CHAR,       V_STR,  "ssid"},
    { C_PASS, (void*)   pass,       MAX_CHAR,       V_STR,  "password" },
    { C_HOST, (void*)   host,       MAX_CHAR,       V_STR,  "hostname" },
    { C_PORT, (void*) & port,       sizeof(int),    V_INT,  "port" },

    { C_LOCO, (void*) & locoIdx,    sizeof(int),    V_INT,  "locoIdx" },

    { C_L_00, (void*) & locos [0],  sizeof(Loco_s), V_LOCO, "loco_1" },
    { C_L_01, (void*) & locos [1],  sizeof(Loco_s), V_LOCO, "loco_2" },
    { C_L_02, (void*) & locos [2],  sizeof(Loco_s), V_LOCO, "loco_3" },
    { C_L_03, (void*) & locos [3],  sizeof(Loco_s), V_LOCO, "loco_4" },
    { C_L_04, (void*) & locos [4],  sizeof(Loco_s), V_LOCO, "loco_5" },

    { C_L_05, (void*) & locos [5],  sizeof(Loco_s), V_LOCO, "loco_6" },
    { C_L_06, (void*) & locos [6],  sizeof(Loco_s), V_LOCO, "loco_7" },
    { C_L_07, (void*) & locos [7],  sizeof(Loco_s), V_LOCO, "loco_8" },
    { C_L_08, (void*) & locos [8],  sizeof(Loco_s), V_LOCO, "loco_9" },
    { C_L_09, (void*) & locos [9],  sizeof(Loco_s), V_LOCO, "loco_10" },

    { C_E_00, (void*) & engs  [0],  sizeof(Eng_s),  V_ENG,  "eng_0"  },
    { C_E_01, (void*) & engs  [1],  sizeof(Eng_s),  V_ENG,  "eng_1"  },
    { C_E_02, (void*) & engs  [2],  sizeof(Eng_s),  V_ENG,  "eng_2"  },
    { C_E_03, (void*) & engs  [3],  sizeof(Eng_s),  V_ENG,  "eng_3"  },

    { C_E_04, (void*) & engs  [4],  sizeof(Eng_s),  V_ENG,  "eng_4"  },
    { C_E_05, (void*) & engs  [5],  sizeof(Eng_s),  V_ENG,  "eng_5"  },
    { C_E_06, (void*) & engs  [6],  sizeof(Eng_s),  V_ENG,  "eng_6"  },
    { C_E_07, (void*) & engs  [7],  sizeof(Eng_s),  V_ENG,  "eng_7"  },

    { C_E_08, (void*) & engs  [8],  sizeof(Eng_s),  V_ENG,  "eng_8"  },
    { C_E_09, (void*) & engs  [9],  sizeof(Eng_s),  V_ENG,  "eng_9"  },
    { C_E_10, (void*) & engs [10],  sizeof(Eng_s),  V_ENG,  "eng_10" },
    { C_E_11, (void*) & engs [11],  sizeof(Eng_s),  V_ENG,  "eng_11" },

    { C_NONE, NULL,               0,              V_NONE, NULL     },
};

// ---------------------------------------------------------
CfgVar_s *
_cfgFindVar (
    CfgId_e  id )
{
    CfgVar_s *p;

    for (p = cfgVarTbl; C_NONE != p->id; p++)  {
        if (id == p->id)
            return p;
    }

    return NULL;
}

// ---------------------------------------------------------
void
cfgDisp (
    Stream   &Serial,
    CfgVar_s *p )
{
    switch (p->type)  {
    case V_INT:
        printf ("  %8s: %6d\n", p->desc, *(int*)p->p);
        break;

    case V_STR:
        printf ("  %8s: %s\n", p->desc, (char*)p->p);
        break;

    case V_ENG:
        printf ("  %8s:", p->desc);
        engDisp ((Eng_s*) p->p);
        break;

    case V_LOCO:
        printf ("  %8s:", p->desc);
        cfgDispLoco ((Loco_s*)p->p);
        break;
    }
}

// ---------------------------------------------------------
void
cfgDispAll (
    Stream &Serial)
{
    for (CfgVar_s *p = cfgVarTbl; V_NONE != p->type; p++)
        cfgDisp (Serial, p);
};

// ---------------------------------------------------------
int
cfgLoad (
    const char *filename )
{
    int res = 1;
    printf ("%s: %s\n", __func__, filename);

    FILE * fp = fopen (filename, "rb");
    if (NULL == fp)  {
        perror ("cfgLoad - fopen");
        res = 0;
        goto done;
    }

    // verify TLV file format
    byte magic [sizeof(cfgMagic)];

    if (1 != fread ((void*) & magic [0], sizeof(magic), 1, fp))  {
        perror ("cfgLoad - fread hdr\n");
        res = 0;
        goto done;
    };

    if (strncmp ((char*)magic, "TLV", sizeof(cfgMagic)))  {
        printf (" %s: non-TLV file\n", __func__);
        res = 0;
        goto done;
    }

    // read TLVs
    CfgHdr_s hdr;
    CfgVar_s *p;

    while (fread ((void*) &hdr, sizeof(hdr), 1, fp))  {
        if (0 == hdr.size)
            break;

        if (NULL == (p = _cfgFindVar (hdr.id)))  {
            printf (" %s: unknown cfgVar id, %d", __func__, hdr.id);
            break;
        }

 //     printf (" %s: %s\n", __func__, p->desc);

        // read amount written specified by hdr.size
        int nread = fread ((void*) p->p, hdr.size, 1, fp);
        if (! nread)  {
            printf (" %s: fread incomplete, %d %d",
                        __func__, nread, p->nByte);
            perror (" cfgLoad - fread");
            res = 0;
            goto done;
        }
    }

done:
    fclose (fp);
    return res;
}

// ---------------------------------------------------------
#if 1
void
cfgSave (
    const char *filename )
{
    printf ("%s: %s\n", __func__, filename);

    FILE * fp = fopen (filename, "wb+");
    if (NULL == fp)  {
        perror ("cfgSave - fopen");
        return;
    }

    if (1 != fwrite ((void*) cfgMagic, sizeof(cfgMagic), 1, fp)) {
        perror ("cfgSave: fwrite cfgMagic");
        goto done;
    }

    CfgHdr_s  hdr;
    for (CfgVar_s *p = cfgVarTbl; V_NONE != p->type; p++)  {
        hdr.id   = p->id;
        hdr.size = p->nByte;

        if (1 != fwrite ((void*) & hdr, sizeof(hdr), 1, fp)) {
            perror ("cfgSave: fwrite hdr");
            goto done;
        }

        int nwr = fwrite ((void*) p->p, p->nByte, 1, fp);
        if (! nwr)  {
            printf (" %s: fwrite incomplete, %d %d",
                        __func__, nwr, p->nByte);
            perror (" cfgSave - fwrite");
            return;
        }
    }

done:
    fclose (fp);
}
#endif

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