Go Down

Topic: How to read variables stored on an SDCard? (Read 2030 times) previous topic - next topic

rearview

Hi All,
Would someone be willing to provide an example of how to read/populate a variable from a file on an SDCard?
Currently all of the variables are written directly into code such as...


  • byte ip = {192,168,1,1};

  • int StationID = 1;


etc...

My goal is to have a .txt or .ini file on the SDCard with this info so a user can provide the changes without having to recompile and upload to the device.
I don't really care what the file is called and i am not too worried about the structure.
Something like...
   StationID = 1
   ArduinoIP = 192,168,1,1
Would be fantastic and reasonably self-explanatory to the user.

My current hardware configuration is the Arduino UNO with the Ethernet/SD Shield purchased from Adafruit.
I have been accessing/working thru the examples on the SDCard with the SDFat lib.

Sorry but I only have a week or so of experience with Arduino/C and learn mostly by example and doing.
I just haven't found anything yet i can get my head around yet.

Thanks in advance - Shane

PaulS

Reading data from a file is the inverse of writing data to the file. So, the first thing you need to define is the EXACT format that the data in the file will have.

The next thing you need to do is to be able to read a record from the file. Precisely how to do that depends on exactly which library you are using to read from the SD card, and exactly how the data is written to the SD card.

Finally, you need to parse the data in that record. You could use strtok() to extract the tokens, and atof(), atoi(), etc. to convert the tokens to numbers, or you could use sscanf to extract the data from the record.

The token approach is better if the record format changes a lot (one value, then 5 values, etc.). The sscanf approach is easier if the records are all alike (and don't contain floats).

rearview

Making progress but looking to understand what I am doing wrong with setting the variable.
Any advice welcome...

So... I have taken the advice from PaulS and nailed down the File structure.
The format of the text file is below:
Code: [Select]

1
0xDE,0xAD,0xBE,0xEF,0xFE,0xED
192,168,3,195
216,139,202,28
99
32
80
1

# ---------------------------------------------------------
# The file structure below must be followed exactly as the Arduino reads
# the first 8 lines of the file
# Line 1 = StationID ie: 1
# Line 2 = Arduino MAC Address
# Line 3 = Arduino IP Address ie: 192,168,3,195
# Line 4 = Mail Server IP Address ie: *,*,202,28
# Line 5 = Maximum Farenheit Temperature ie: 99
# Line 6 = Min Farenheit Temperature ie: 32
# Line 7 = Maximum Humidity ie: 80
# Line 8 = Min Humidity ie: 1

# Any questions regarding editing this file? - See Shane


Next I am reading the file line by line using the full code below.
But I cannot figure out how to set the var .... I continue to get 55: error: invalid array assignment
This is the section of code where i am trying to set it...

Code: [Select]

if(line_number == 1)
      {
        Serial.print("Output in the if statement for line 1 is ");
        Serial.println(buffer);
        SensorID = buffer;
      }


Full Code
Code: [Select]

// Ported to SdFat from the native Arduino SD library example by Bill Greiman
// On the Ethernet Shield, CS is pin 4. SdFat handles setting SS
const int chipSelect = 4;
/*
SD card read/write
 
This example shows how to read and write data to and from an SD card file
The circuit:
* SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4

created   Nov 2010
by David A. Mellis
updated 2 Dec 2010
by Tom Igoe
modified by Bill Greiman 11 Apr 2011
This example code is in the public domain.

*/
#include <SdFat.h>
SdFat sd;


void setup() {
  Serial.begin(9600);
 
  // Initialize SdFat or print a detailed error message and halt
  // Use half speed like the native library.
  // change to SPI_FULL_SPEED for more performance.
  if (!sd.init(SPI_HALF_SPEED, chipSelect)) sd.initErrorHalt();

  const int line_buffer_size = 50;
  char buffer[line_buffer_size];

  char SensorID[50];

  ifstream sdin("CONFIG.TXT");
  int line_number = 0;

  Serial.println("... inside the Loop");

  while (sdin.getline(buffer, line_buffer_size, '\n') || sdin.gcount())
  {
    int count = sdin.gcount();
        count--;  // Don't include newline in count
        ++line_number;
     
      if(line_number == 1)
      {
        Serial.print("Output in the if statement for line 1 is ");
        Serial.println(buffer);
        SensorID = buffer;
      }
     
      if(line_number == 2)
      {
        Serial.print("Output in the if statement for line 2 is ");
        Serial.println(buffer);
      }
   
   
    } // end of while

  Serial.println("did the sensor ID print below ?");
  Serial.println(SensorID);
}

void loop() {
  // nothing happens after setup
}




Thanks Shane

AWOL

Code: [Select]
SensorID = buffer;

C allows (encourages even) pointer assignments, but these are buffers, and for those, you have to use "memcpy" or something similar
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

PaulS

Quote
for those, you have to use "memcpy" or something similar

The strcpy() function is preferred, since it knows when to stop copying.

skyjumper

Code: [Select]

#include "config.h"
#include <SdFat.h>
#define EOF -1
#define MAX_LINE_LEN 80

Config::Config(char *fileName)
{
  SdFat card;
  SdFile file;

  char buf[MAX_LINE_LEN];
 
  if (!card.init(SPI_FULL_SPEED, 31)) card.initErrorHalt();

  // Open the file
  if(!file.open(fileName, O_READ))
  {
    // Failed to open file
    for(;;)
    {
      //digitalWrite(LED_ERROR, LOW);
      delay(75);
      //digitalWrite(LED_ERROR, HIGH);
      delay(75);
    }
  } 
   
  // Begin to read the rows
  unsigned short int p = 0;
  char c;
  do
  {
    //c = fgetc(in); // Watcom CPP
    c = file.read();

    if(c == 0x0D)
    {
      // end of row - process row we read
      buf[p] = '\0';
      p = 0;
      processRow(buf);
    }
    else
    {   
      buf[p++] = c;
    } 
  } while(c != EOF);
 
  // Done reading file, let's close it
  file.close();
}

Config::~Config(){}


void Config::processRow(char *rowBuf)
{
  stripWs(rowBuf);
 
  if(rowBuf[0] == ';')
  {
    return;
  }
 
  // make sure the parameter line contains an = or return
  char *p = strstr(rowBuf, "=");
  if(p == NULL)
  {
    return;
  } 
 
  // break the parameter line into its parameter term and its value
  char *param;
  char *value;

  param = strtok(rowBuf, "=");
  value = strtok(NULL, rowBuf);

  // remove any white space around parameter and value
  stripWs(param);
  stripWs(value);
 
  // Convert both to upper case
  param = strupr(param);
  value = strupr(value);
 
  // identify the parameters and store the values
  if(strcmp(param, "DAMP_LEVEL_TBS") == 0)
  {
    dampTbs = atoi(value);
  } 

  if(strcmp(param, "UPDATE_SECONDS_TBS") == 0)
  {
    updateTbs = atoi(value);
  } 
 
  if(strcmp(param, "LONG_PRESS") == 0)
  {
    longPress = atoi(value);
  } 
 
  if(strcmp(param, "BOATLENGTH") == 0)
  {
    boatLength = atoi(value);
  } 

  if(strcmp(param, "GPS_ANTENNA_LOCATION") == 0)
  {
    gpsAntLocation = atoi(value);
  } 

  if(strcmp(param, "WIND_XDUCER_HEIGHT") == 0)
  {
    windXducerHeight = atoi(value);
  } 
}

void Config::stripWs(char *buf)
{
  int len = strlen(buf);
  char tmp[MAX_LINE_LEN]; 
   
  int p=0;
  for(int i=0; i<len; i++)
  {
    if(buf[i] >= 33 && buf[i] <= 126)
    {
       tmp[p++] = buf[i];
    }
  }
 
  tmp[p] = NULL;
  strcpy(buf, tmp);
}

unsigned char Config::getTbsDamping(void) { return dampTbs; }
unsigned char Config::getTbsUpdateSeconds(void) { return updateTbs; }
unsigned char Config::getLongPress(void) { return longPress; }
unsigned char Config::getBoatLength(void) { return boatLength; }
unsigned char Config::getGpsAnt(void) { return gpsAntLocation; }
unsigned char Config::getWindXducerHeight(void) { return windXducerHeight; }


skyjumper

Code: [Select]

#ifndef CONFIG_H
#define CONFIG_H

class Config
{
  public:
 
  Config(char *);
  ~Config();

  unsigned char getTbsDamping(void);
  unsigned char getTbsUpdateSeconds(void);
  unsigned char getLongPress(void);
  unsigned char getBoatLength(void);
  unsigned char getGpsAnt(void);
  unsigned char getWindXducerHeight(void);
   
  private:
 
  unsigned char dampTbs;
  unsigned char updateTbs;
  unsigned char longPress;
  unsigned char boatLength;
  unsigned char gpsAntLocation;
  unsigned char windXducerHeight;
 
  void processRow(char *);
  void stripWs(char *);
};

#endif


skyjumper

Okay, there is the code for a class that does what you need. I'll paste a sample config file below.

You can change the type of value you are reading easily. Use atoi() for an int, use atof() for a float. A character string is even easier. With this, you need to decide what your parameters are ahead of time, which is pretty normal.

Then change the getters (methods that return your values) accordingly.

Let me know what questions you have.

Code: [Select]

; this is a comment
;
DAMP_LEVEL_TBS=3
;
LONG_PRESS=10
;
BOATLENGTH=30
;
GPS_ANTENNA_LOCATION = 30
;
WIND_XDUCER_HEIGHT=43



rearview

All,
Thank you for the replies...
looks like the atof recommended by skyjumper is what was needed.

Code: [Select]

MaxFTemp = atof(buffer); //convert the array into an float



- Thanks again - Shane

Go Up