Using SD card to store variables on text file and loading at runtime

Hi all..

I am sure something like this came up before. I have even found a couple of related topics on the forum but couldn't make use of the suggestions given for my project.
I am trying to use the SD memory card as storage for variables (specific sensor static data, not measurements) which will be loaded at runtime for processing. The processor would then proceed to poll the sensors for measurements.

See below:

# SENSOR DATA 



SENSORID=1
BUSID=100
LOCATION=LAB
CHANNELS=2
CH1=CHA
CH2=CHB
LOG=N
END


SENSORID=2
BUSID=90
LOCATION=A/V CABINET
CHANNELS=1
CH1=A/V CABINET
LOG=N
END

Each sensor is given a serial number (SENSORID)
Location is a char description, BUSID is a byte variable to store the sensor’s unique address on the RS485 bus etc
The ‘END’ signifies the end of the data for each sensor in order to assist the processing.
Ideally I need to develop a function that would fetch data for each sensor based on its ID and the specific variable needed. An issue is the fact that some variables are char types, some byte types and others Boolean (eg LOG)
I was thinking of something like :

int GetSDData (char *SDdata, byte Len,  byte SENSORID) {
…
..
}

I thought that SDdata would store the variable name to fetch (eg BUSID), Len being the length of the variable name and SENSORID the serial number of the set of the sensor needed.
The function would then return either a number (int) in case of a numeric variable or, if its char variable, the data would be placed in the same array SDdata.
Any thoughts or suggestions on the above or alternative approach would be greatly appreciated.
Also any sample code examples how to efficiently process the text file to extract the needed variables would also be great help.

Thanks !

Any thoughts or suggestions on the above or alternative approach would be greatly appreciated.

Since the data is the same for all sensors, creating a struct to hold the data, and populating the fields of the struct for a given sensor ID makes more sense to me.

The only function that needs to care where the data is stored, or how, is void getSensorData(int sensorID, struct sensorData &theData), which takes an ID and a reference to a struct, and populates theData's fields.

Thanks PaulS for the reply!

Since the data is the same for all sensors...

I didnt clarify this but, although the basic data structure is the same for all sensors, the number of channels differs. Hence the variable CHANNELS that holds the number of available channels and then CH1, CH2 ... accordingly.

I am not sure how a struct can work in this case.

The truth is I ve never used structs before and am not really familiar how to use them either.
I guess have to do some background reading if you still think this is the way to go.

the number of channels differs.

There is a reasonable maximum, isn't there?

I guess have to do some background reading if you still think this is the way to go.

Something like this, perhaps:

struct sensorData
{
   int ID;
   int busID;
   char location[20];
   int channelCount;
   char channelNames[10][20];
   bool log;
};
typedef struct sensorData SensorData;

SensorData aSensor;

aSensor.ID = 1;
aSensor.busID = 100;
aSensor.location = "LAB";
aSensor.channelCount = 2;
aSensor.channelNames[0] = "CHA";
aSensor.channelNames[0] = "CHB";
aSensor.log = false;

Thanks a lot PaulS!

Still need to do some background reading... but seems relatively straight forward.

How would you then propose to populate this struct with data from SD...?

I guess I need to search till the end of file for the required BUSID
How would you then load the actual variable data (say aSensor.location) into the struct?
In other words I need to separate variable name before the "=" with whatever the value that follows it.

I guess I need to search till the end of file for the required BUSID

You mean SENSORID?

Depending on how much data you have, I'd be inclined to simply create an array of structs, and populate it, reading the file once.

You mean SENSORID

Yes sorry, i meant SENSORID.

The idea with using the SD card in the first place was to save on valuable RAM. I d prefer to load just the data needed... when needed. For example search the file, get all available BUSIDs and then poll the sensors for measurements based on the BUSID.

I d prefer to load just the data needed

That can be done. The struct could use an array of pointers to chars, most of which are NULL, so the size of the struct is not constant.

Write a function first that reads the first block of sensor data, and populates the struct. Call the function, and print the struct data (another function perhaps?).

Then, modify the function to take an argument. Have the function find the line that starts the block of data for that function, and populate the struct based on that line and the lines that follow it.

You could (and should) have constructors and destructors in the struct, so that all the fields get initialized and any allocated memory gets freed, when the object is created and deleted.

Thanks again...

Could you elaborate a bit on the

constructors and destructors in the struct

Do you know what constructors and destructors do in a class? They do the same in a struct. There are two basic differences between structs and classes. The first is that a struct without member functions is usable in C. A class or a struct with member functions is not.

The second is that class members and fields default to public. Struct members and fields default to private. (Or maybe I have that reversed; I never let the status default, so I don't remember which default goes with which.)

A constructor is some code that runs when an instance is created.

struct sensorData
{
   sensorData();
   ~sensorData();

   int ID;
   int busID;
   char location[20];
   int channelCount;
   char channelNames[10][20];
   bool log;
};
typedef struct sensorData SensorData;

sensorData::sensorData()
{
   Serial.print("Called when an instance is created");
}

The above function gets called when you:

SensorData *pSensor = new SensorData();
sensorData::~sensorData()
{
   Serial.print("Called when an instance is deleted");
}

The above function is called when you:

   if(pSensor)
      delete pSensor();

Now, if you change char channelNames[10][20]; to char *channelNames[10]; in the struct, and have

   channelNames[channelNumber] = strdup(someChannelNameData);

in the function that modifies the instance that pSensor points to, dynamic memory allocation is happening. If the pSensor instance is deleted, the memory pointed to be pSensor->channelNames[n] remains allocated, with no way to free it, because pSensor is the only thing that has a handle to the memory.

So, the destructor needs to make sure that any dynamically allocated memory is freed, like so:

sensorData::~sensorData()
{
   Serial.print("Called when an instance is deleted");
   for(byte b=0; b<channelCount; b++)
   {
      if(channelNames[b])
         free(channelNames[b]);
   }
}

Thanks again PaulS

Seems like I have to do some reading on structs and play with them for a while as I ve never used them before!