Go Down

Topic: Using a SD card to retrieve variables - I have managed to confuse myself (Read 10201 times) previous topic - next topic

1studio1

Hi all,

First off big thanks to those knowledgeable people that contribute on this site  - you have saved me hours, given me ideas and generally made this a fun hobby.

Ok so I have totally confused myself somewhat and need some help please.

I have written a quite lengthy sketch for the Arduino Mega (I needed lots of pins) and it does what I want it to do in controlling a  home made geared camera head - it's a bit like an electronic motor controlled tri pod  (lots of reasons I wanted to do this)

One annoying thing is you have to go through each of the menus I created setting certain variables to the required setting each time it is powered on. these are obviously declared and defaulted in the void.setup part of the sketch. e.g.
int tilDegree = 0;                           // number of degrees per photo to tilt -  1,2,3,4 or 5
int tilDirec = 2;                               //chosen Tilt direction 2 (up) or 3 (down)
int rotDegree = 0;                         //number of degrees per photo to rotate. - 1,2,3,4 or 5
int rotDirec = 0;                            //chosen Rotational direction 0 (right) or 1 (Left)
..... There is actually about 20 settings

So I thought it would be nice to use a sd card (given they are so cheap) to store the last settings used and even have different cards with different data settings for particular types and scenarios of use.

I am using the IDE 1.0.5 supplied SD library a adafruit sd card breakout http://www.adafruit.com/products/254 and a micro SD card formatted FAT16. and for testing only, it's all plugged into a Uno rev3.(main sketch is not included for testing to make things simpler) pins are the standard 13, 12, 11 and 10 on the UNO 53, 52, 51 and 50 when it gos to the mega.

I can get it to work in so far as it will write the txt file to the SD card and store a value for each variable:
    myFile.println(tilDegree); //writes the value of 'variable' to the text file
    myFile.println(tilDirec); //writes the value of 'variable' to the text file
...... etc.
These lines are added to the Read/Write sketch by David A. Mellis in the bit where it writes data. unaltered file  attached

But the bit I am confused by is how to pull it back the other way, as in read the card and assign the data in the txt file to each respective variable. So line 1 would be the data to assign to tilDegree, line 2 would be the data to assign to tilDirec ...and so on.

I have read that this is possible but haven't found details on how. the problem is I have now read so many posts on forums and howto documents I am no longer sure what is actually needed.

Can someone please help as i am quite stuck here? is there any more info I need to share?

Steve


PaulS

Instead of just printing each character read, as you do here:
Code: [Select]
    while (myFile.available()) {
    Serial.write(myFile.read());
    }

You need to save the characters in an array (of type char). When you find a carriage return or line feed, add a NULL to the end of the array. Then, use atoi() to convert the value to an int. (Or atof() to convert to float, etc.)

The tricky part will be knowing which variable to store the value in. More intelligence in what you save would help.

Instead of just saving the values, save the names, too:
tilDegree = xxx<cr><lf>

Then, use strtok() to parse the array, to get two tokens ("tilDegree" and "xxx"). Use one token to decide where to store the data and the other as the data to convert.
The art of getting good answers lies in asking good questions.

1studio1

Thank you so much PaulS. - I was hoping it would be you that replied first as I have followed and used a few of your suggestions in the past to great effect. :)

So I am not flogging a dead horse then!  8)

Quote
Instead of just printing each character read, ... You need to save the characters in an array (of type char). 

So I could dump that bit of code then and replace it with some sort of specific read? or is that not what you mean. Would it be something like: myFile.read(tilDegree);?

I haven't used arrays much other than to set a timer to pause the sketch for long exposures (EV) as in this:
int EVArray[10]= {0, 3, 5, 10, 20, 40, 80, 160, 300}; the 16,4 LCD screen  (sorry forgot to mention that bit before)  displays the system pause time akin to the cameras display:
char* Exposure[]={" Off", " 1/4", " 1/2", "  1s", "  2s","  4s","  8s"," 16s"," 30s"}; but the system stops based on that EVArray *100 .

So, I am not sure how to set that up. is it like the char message[6] = "hello"; in the Array reference page.?

atoi(); and strok(); are new to me - is there a help page in arduino reference for those? or can you walk me through how to use them "properly"

Quote
tilDegree = xxx<cr><lf>

Brilliant I wanted to be able to save the names in the txt file but thought that would be impossible so that is great news to hear that you would recommend it.

Would you be able to assist me further with the writing of some stand alone code that I could then develop further   as at the moment I think I may have just stepped way out of my depth with this task. (only been doing it for a year and this is only my 2nd major project. I appreciate that you have a life of your own and possible many other things you would rather be doing. but I think I might end up struggling with this.

Steve

PaulS

Quote
Would you be able to assist me further with the writing of some stand alone code that I could then develop further   as at the moment I think I may have just stepped way out of my depth with this task.


Code: [Select]
   char buffer[40]; // May need to be a bit bigger if you have long names
   byte index = 0;
   while (myFile.available())
   {
    char c = myFile.read();
       if(c == '\n' || c == '\r') // Test for <cr> and <lf>
       {
           parseAndSave(buffer);
           index = 0;
           buffer[index] = '\0'; // Keep buffer NULL terminated
       }
       else
       {
           buffer[index++] = c;
           buffer[index] = '\0'; // Keep buffer NULL terminated
       }
   }


The parseAndSave() function would look something like:
Code: [Select]
void parseAndSave(char *buff)
{
   char *name = strtok(buff, " =");
   if(name)
   {
       char *junk = strtok(NULL, " ");
       if(junk)
       {
           char *valu = strtok(NULL, " ");
           if(valu)
           {
               int val = atoi(valu);
               if(strcmp(name, tilDegree) == 0)
               {
                   tilDegree = val;
               }
               else if(strcmp(name, tilDirec) == 0)
               {
                   tilDirec = val;
               }
           }
        }
     }
  }
}

This assumes that the file contains
Quote
name = value

stuff. If it contains
Quote
name=value

stuff, then the parsing function would not need the junk token (which contains =,space).
The art of getting good answers lies in asking good questions.

1studio1

Cripes ! !

Well thank you so much that is very kind of you and so quickly as well.

I will give your code a go and see where I get to. Hopefully this should be a good kick start (not that I understand all of it lol)

Again, Thank you so much PaulS - I'll let you know how I get on and hope that you don't mind if I need to ask you any further questions.

Steve

Magicj

Any reason why you aren't using the inbuilt EEPROM to store the data?

With only 20 or so variables I think you will find this much easier than using an SD card. You could then have a number of scenarios saved which could be selected via  a menu item.

1studio1

Well I opted for SD over EEPROM as I had read lots of posts saying that the EEPROM had a maximum number of writes (life) and other folk seem to elude to using SD instead. The other reason was that allowed for PC editing of the variable file in that I can quickly write a recipe, save it and slip the SD card in and we are good to go.... bit belt and braces I know but it just added some functionality. The SD cards I am using (SanDisk) are also so cheap at only a couple of $£'s.

So today I have written out a text file that looks like this;

rotDegree = 4
rotDirec = 1
tilDegree = 4
tilDirec = 2
travPower = 90
travDirec = 1
OperateMotors = 50
DelayMotors = 30
bracket = 3
intervalometerSET = 1
EVComp = 2
Camera_Make = 1

using the following line(s) to write the file data (1st line  only shown, but repeats for each of the above variables)
Code: [Select]
myFile.print("rotDegree = ");myFile.println(rotDegree);

Do I need to add the <cr><lf> to the end of each line so that the text file reads rotDegree = 4<cr><lf>?
Code: [Select]
myFile.print("rotDegree = ");myFile.print(rotDegree);myFile.println("<cr><lf");

Steve

1studio1

Okay, I'm stuck again... Sorry I am being a pain I know but I can't seem to get my head round this at all.

I have modified the code from the example sketch to include the variables from the main sketch that I want to read and write.
To test it they are doubled up - the ones written are from the main sketch but the read backs have a 'R' appended to the name. this was the easiest way I could think of doing it to test it. (create file with first set the read file to set the second set (R).

The file writes out correctly but I can not figure out where to add PaulS's code and what I need to delete from the original. I think I am going into melt down here mainly because I don't know what the heck I am doing lol

here is what I have so far (also attached as a ino file)
Code: [Select]
/*
  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:
** UNO:  MOSI - pin 11, MISO - pin 12, CLK - pin 13, CS - pin 4 (CS pin can be changed)
  and pin #10 (SS) must be an output
** Mega:  MOSI - pin 51, MISO - pin 50, CLK - pin 52, CS - pin 4 (CS pin can be changed)
  and pin #52 (SS) must be an output
** Leonardo: Connect to hardware SPI via the ICSP header


created   Nov 2010  by David A. Mellis
modified 9 Apr 2012  by Tom Igoe

This example code is in the public domain.

*/

#include <SPI.h>
#include <SD.h>

File myFile;

const int chipSelect = 10;

void setup()
{
  // WRITE DATA
int rotDegree = 4;
int rotDirec = 1;
int tilDegree = 4;
int tilDirec = 2;
int travPower = 90;
int travDirec = 1;
int OperateMotors = 50;
int DelayMotors = 30;
int bracket = 3;
int intervalometerSET = 1;
int EVComp = 2;
int Camera_Make = 1;
   //READ DATA
int rotDegreeR;
int rotDirecR;
int tilDegreeR;
int tilDirecR;
int travPowerR ;
int travDirecR;
int OperateMotorsR;
int DelayMotorsR;
int bracketR;
int intervalometerSETR;
int EVCompR;
int Camera_MakeR;

// Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  Serial.print("Initializing SD card...");
   pinMode(SS, OUTPUT);
   
  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
 
  SD.remove("test.txt") ;                                             // if file is present delete it before writing new data
   
  myFile = SD.open("test.txt", FILE_WRITE);                           // open the file.
 
  if (myFile) {
    Serial.print("Writing to test.txt...");                           // if the file opened okay, write to it:
   
    myFile.print("rotDegree = ");myFile.println(rotDegree);
    myFile.print("rotDirec = ");myFile.println(rotDirec);
    myFile.print("tilDegree = ");myFile.println(tilDegree);
    myFile.print("tilDirec = ");myFile.println(tilDirec);
    myFile.print("travPower = ");myFile.println(travPower);
    myFile.print("travDirec = ");myFile.println(travDirec);
    myFile.print("OperateMotors = ");myFile.println(OperateMotors);
    myFile.print("DelayMotors = ");myFile.println(DelayMotors);
    myFile.print("bracket = ");myFile.println(bracket);
    myFile.print("intervalometerSET = ");myFile.println(intervalometerSET);
    myFile.print("EVComp = ");myFile.println(EVComp);
    myFile.print("Camera_Make = ");myFile.println(Camera_Make);
   
    myFile.close();  Serial.println("done.");                       // close the file:
  } else {
     Serial.println("error opening test.txt");                      // if the file didn't open, print an error:
  }
 
// re-open the file for reading:
  myFile = SD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");
   
// read from the file until there's nothing else in it:
    while (myFile.available()) {
    Serial.write(myFile.read());
    }
    myFile.close();                                                 // close the file:
  } else {
    Serial.println("error opening test.txt");                // if the file didn't open, print an error:
  }

Serial.print("Rotation degrees ");Serial.println(rotDegreeR);
Serial.print("Rotation direction ");Serial.println(rotDirecR);
Serial.print("Tilt degrees ");Serial.println(tilDegreeR);
Serial.print("Tilt direction ");Serial.println(tilDirecR);
Serial.print("Traverser Power ");Serial.println(travPowerR) ;
Serial.print("Traverser Direction ");Serial.println(travDirecR);
Serial.print("Operate Motors for ");Serial.println(OperateMotorsR);
Serial.print("Pause Motors for ");Serial.println(DelayMotorsR);
Serial.print("Bracketing ");Serial.println(bracketR);
Serial.print("Intervalometer ");Serial.println(intervalometerSETR);
Serial.print("EV Compensator ");Serial.println(EVCompR);
Serial.print("Camera Make ");Serial.println(Camera_MakeR);

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



Steve (brain crash)

Magicj


Well I opted for SD over EEPROM as I had read lots of posts saying that the EEPROM had a maximum number of writes (life) and other folk seem to elude to using SD instead.


100,000+ writes available - it might to a while to use up all of those  :smiley-roll:


The other reason was that allowed for PC editing of the variable file in that I can quickly write a recipe, save it and slip the SD card in and we are good to go.... bit belt and braces I know but it just added some functionality.


Fair enough, but you could easily use the serial monitor to transfer a 'recipe' to the EEPROM if you wanted to.

There is often more than one way to achieve an outcome and following your SD card approach will provide another learning experience for me  :)

1studio1

Quote
100,000+ writes available

I have read of people getting a lot lot less than the manufacturers quoted figure but that's still a heck of a lot of writes and this was my first choice. The SD card idea came out as being more flexible in the end so I decided to go that route.

Quote
but you could easily use the serial monitor to transfer a 'recipe' to the EEPROM if you wanted to.

:) Very true, but I have other less technically minded friends involved in this (none commercial) project I think some of them might struggle with the concept. emailing them a memory card file would be quite simple. LOL

Quote
and following your SD card approach will provide another learning experience for me
LOL  :) Well it is nice if more people can benefit and I like sharing so win win -I am just so out of my depth with this one bit, hence needing help.

All the best
Steve

PaulS

Code: [Select]
    while (myFile.available()) {
    Serial.write(myFile.read());
    }

This isn't doing anything to save, parse, and properly assign values to the variables with the R suffixes.
The art of getting good answers lies in asking good questions.

1studio1

Sorry PaulS - just looked back at my post and realised it looked like I had not taken your advice. this could not be further from the truth. I just had not added the code you kindly provided.

Here is what I think I was meant to do with your coding added.
Code: [Select]
// SD card read/write

#include <SPI.h>
#include <SD.h>
File myFile;
const int chipSelect = 10;
 // WRITE DATA
int rotDegree = 4;
int rotDirec = 1;
int tilDegree = 4;
int tilDirec = 2;
int travPower = 90;
int travDirec = 1;
int OperateMotors = 50;
int DelayMotors = 30;
int bracket = 3;
int intervalometerSET = 1;
int EVComp = 2;
int Camera_Make = 1;
  //READ DATA
int rotDegreeR;
int rotDirecR;
int tilDegreeR;
int tilDirecR;
int travPowerR ;
int travDirecR;
int OperateMotorsR;
int DelayMotorsR;
int bracketR;
int intervalometerSETR;
int EVCompR;
int Camera_MakeR;

void setup()
{
// Open serial communications and wait for port to open:
 Serial.begin(9600);
  while (!Serial) {
   ; // wait for serial port to connect. Needed for Leonardo only
 }


 Serial.print("Initializing SD card...");
  pinMode(SS, OUTPUT);
 
 if (!SD.begin(chipSelect)) {
   Serial.println("initialization failed!");
   return;
 }
 Serial.println("initialization done.");
 
 SD.remove("test.txt") ;                                             // if file is present delete it before writing new data
 
 myFile = SD.open("test.txt", FILE_WRITE);                           // open the file.
 
 if (myFile) {
   Serial.print("Writing to test.txt...");                           // if the file opened okay, write to it:
   
   myFile.print("rotDegree = ");myFile.println(rotDegree);
   myFile.print("rotDirec = ");myFile.println(rotDirec);
   myFile.print("tilDegree = ");myFile.println(tilDegree);
   myFile.print("tilDirec = ");myFile.println(tilDirec);
   myFile.print("travPower = ");myFile.println(travPower);
   myFile.print("travDirec = ");myFile.println(travDirec);
   myFile.print("OperateMotors = ");myFile.println(OperateMotors);
   myFile.print("DelayMotors = ");myFile.println(DelayMotors);
   myFile.print("bracket = ");myFile.println(bracket);
   myFile.print("intervalometerSET = ");myFile.println(intervalometerSET);
   myFile.print("EVComp = ");myFile.println(EVComp);
   myFile.print("Camera_Make = ");myFile.println(Camera_Make);
   
   myFile.close();  Serial.println("done.");                       // close the file:
 } else {
    Serial.println("error opening test.txt");                      // if the file didn't open, print an error:
 }
 
// re-open the file for reading:
 myFile = SD.open("test.txt");
 if (myFile) {
   Serial.println("test.txt:");
   
// read from the file
char buffer[40]; // May need to be a bit bigger if you have long names
   byte index = 0;
   while (myFile.available())
   {
    char c = myFile.read();
       if(c == '\n' || c == '\r') // Test for <cr> and <lf>
       {
           parseAndSave(buffer);
           index = 0;
           buffer[index] = '\0'; // Keep buffer NULL terminated
       }
       else
       {
           buffer[index++] = c;
           buffer[index] = '\0'; // Keep buffer NULL terminated
       }
   }

   myFile.close();                                                 // close the file:
 } else {
   Serial.println("error opening test.txt");              // if the file didn't open, print an error:
 }

Serial.print("Rotation degrees ");Serial.println(rotDegreeR);
Serial.print("Rotation direction ");Serial.println(rotDirecR);
Serial.print("Tilt degrees ");Serial.println(tilDegreeR);
Serial.print("Tilt direction ");Serial.println(tilDirecR);
Serial.print("Traverser Power ");Serial.println(travPowerR) ;
Serial.print("Traverser Direction ");Serial.println(travDirecR);
Serial.print("Operate Motors for ");Serial.println(OperateMotorsR);
Serial.print("Pause Motors for ");Serial.println(DelayMotorsR);
Serial.print("Bracketing ");Serial.println(bracketR);
Serial.print("Intervalometer ");Serial.println(intervalometerSETR);
Serial.print("EV Compensator ");Serial.println(EVCompR);
Serial.print("Camera Make ");Serial.println(Camera_MakeR);

}
void loop()
{
// main code and function calls
}
void parseAndSave(char *buff)
{
   char *name = strtok(buff, " =");
   if(name)
   {
       char *junk = strtok(NULL, " ");
       if(junk)
       {
           char *valu = strtok(NULL, " ");
           if(valu)
           {
               int val = atoi(valu);
               if(strcmp(name, rotDegree) == 0)
               {
                   rotDegreeR = val;
               }
               if(strcmp(name, rotDirec) == 0)
               {
                   rotDirecR = val;
               }
               if(strcmp(name, tilDegree) == 0)
               {
                   tilDegreeR = val;
               }
               if(strcmp(name, tilDirec) == 0)
               {
                   tilDirecR = val;
               }
               if(strcmp(name, tilDirec) == 0)
               {
                   tilDirecR = val;
               }
               if(strcmp(name, travPower) == 0)
               {
                   travPowerR = val;
               }
               if(strcmp(name, travDirec) == 0)
               {
                   travDirecR = val;
               }
               if(strcmp(name, OperateMotors) == 0)
               {
                   OperateMotorsR = val;
               }
               if(strcmp(name, DelayMotors) == 0)
               {
                   DelayMotorscR = val;
               }
               if(strcmp(name, bracket) == 0)
               {
                   bracketR = val;
               }
               if(strcmp(name, intervalometerSET) == 0)
               {
                   intervalometerSETR = val;
               }
               if(strcmp(name, EVComp) == 0)
               {
                   EVCompR = val;
               }
               else if(strcmp(name, Camera_Make) == 0)
               {
                   Camera_MakeR = val;
               }
           }
        }
     }
}



I think I have missed something major out as I get compile errors
ReadWrite_testing:184: error: invalid conversion from 'int' to 'const char*'
ReadWrite_testing:184: error: initializing argument 2 of 'int strcmp(const char*, const char*)'
This repeats for  each line in the function paseAndSave.

Am I meant to include a library for the strcmp command - I read that it was automatically included but the someone said (and now I can't find the post that suggested it) that one should include a library of some sort?

Steve

1studio1

Woohoo.

I think I may have actually sorted this out at last... With a fresh look and reading the error message in another way I realized that the code:
Code: [Select]
if(strcmp(name, rotDegree) == 0)
needed quotation marks as it was reading text not a var. and should read:
Code: [Select]
if(strcmp(name, "rotDegree") == 0)
Feeling a tad embarrassed at missing a class 101 error

Huge thanks to PaulS for his enlightenment, wisdom and code I owe you a beer !

Many thanks again

Steve

PaulS

Quote
Feeling a tad embarrassed at missing a class 101 error

Well, me too. Glad you found the solution.
The art of getting good answers lies in asking good questions.

Go Up