Go Down

Topic: Problem saving data to SD (Read 2773 times) previous topic - next topic

madepablo

Nov 06, 2009, 07:17 pm Last Edit: Nov 06, 2009, 07:54 pm by madepablo Reason: 1
Hi,
I try to save the data acquired by different sensors into an ascii file (named data.log) at a microSD card (This one: http://www.libelium.com/tienda/catalog/product_info.php?cPath=21&products_id=66), by the use of Filelogger library.

I tried to use the example included with the filelogger library with a very simple example that measure the inner temperature and voltage of my arduino duemillanove without any extra component. In my real project i want to save more data from temperature, humidity, pressure sensor, etc. But in this way is more simple.
The problem is that the code does not compile. I am sure that the solution is quite simple, but i don´t know how to solve it...

Here is the code:
Code: [Select]
#include <FileLogger.h>
#define MEM_PW 8

long readTemp() {
 long result;
 // Read temperature sensor against 1.1V reference
 ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3);
 delay(20); // Wait for Vref to settle
 ADCSRA |= _BV(ADSC); // Convert
 while (bit_is_set(ADCSRA,ADSC));
 result = ADCL;
 result |= ADCH<<8;
 result = (result - 125) * 1075;
 return result;
}

long readVcc() {
 long result;
 // Read 1.1V reference against AVcc
 ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
 delay(2); // Wait for Vref to settle
 ADCSRA |= _BV(ADSC); // Convert
 while (bit_is_set(ADCSRA,ADSC));
 result = ADCL;
 result |= ADCH<<8;
 result = 1126400L / result; // Back-calculate AVcc in mV
 return result;
}

void write_data(float msg[]){
 unsigned int length = (strlen(msg)+1);
 buffer[length];
 for(int i=0; i<length;i++)
   buffer[i] = msg[i];
 FileLogger::append("data.log", buffer, length);
}

void setup() {
 pinMode(MEM_PW, OUTPUT);
 digitalWrite(MEM_PW, HIGH);
 Serial.begin(9600);
 Serial.println("Arduino inner temperature and inner voltage");
}

void loop() {
 Serial.print(readTemp(), DEC); // read the inner temperature of arduino
 Serial.print(" x10^-4 degC   ");
 Serial.print(readVcc(), DEC); // read the voltage
 Serial.println("  mVolts");
 buffer[0]= (readTemp()); // save the data at the microSD
 buffer[1]= (readTemp());
 writedata(buffer);
 delay(1000);
}


and this is the error showed during compilation:
Quote
In function 'void write_data(float*)':
error: cannot convert 'float*' to 'const char*' for argument '1' to 'size_t strlen(const char*)' In function 'void loop()':


I think that the problem is related to two things. First that i should establish the number of components in the array, what is the number of values that i want to save in the same line of the data.log file, one per sensor... And the other topic is that i should establish the length of each value in the array. I am not sure if this is true and neither how should i do that.

Could you help me?
Thanks!

madepablo

ok, i made some small changes in the code.

I deleted the writedata procedure and i wrote the procedures in the void loop().

This is how it looks like now:
Code: [Select]
#include <FileLogger.h>
#define MEM_PW 8

long readTemp() {
 long result;
 // Read temperature sensor against 1.1V reference
 ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3);
 delay(20); // Wait for Vref to settle
 ADCSRA |= _BV(ADSC); // Convert
 while (bit_is_set(ADCSRA,ADSC));
 result = ADCL;
 result |= ADCH<<8;
 result = (result - 125) * 1075;
 return result;
}

long readVcc() {
 long result;
 // Read 1.1V reference against AVcc
 ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
 delay(2); // Wait for Vref to settle
 ADCSRA |= _BV(ADSC); // Convert
 while (bit_is_set(ADCSRA,ADSC));
 result = ADCL;
 result |= ADCH<<8;
 result = 1126400L / result; // Back-calculate AVcc in mV
 return result;
}

void setup() {
 pinMode(MEM_PW, OUTPUT);
 digitalWrite(MEM_PW, HIGH);
 Serial.begin(9600);
 Serial.println("Arduino inner temperature and inner voltage");
}

void loop() {
 Serial.print(readTemp(), DEC); // read the inner temperature of arduino
 Serial.print(" x10^-4 degC   ");
 Serial.print(readVcc(), DEC); // read the voltage
 Serial.println("  mVolts");
 char data[1];
 unsigned int length = (strlen(data[1])+1);
 data[0]=(readTemp());
 data[1]=(readVcc());
 FileLogger::append("data.log", data, length);
 delay(1000);
}


Now, the error compiling is:
Quote
In function 'void loop()':
error: invalid conversion from 'char' to 'const char*


I tried changing the type of array called data, form chart to float (because the resulting values that i want to store are that...).
This is the line of code:
Code: [Select]
float data[1];
but then, the error is very similar:
Quote
In function 'void loop()':
error: cannot convert 'float' to 'const char*' for argument '1' to 'size_t strlen(const char*)


So i decided to assign a value to the length of each value stored in the array to 8 digits (in the example, temperature and voltage should be smaller numbers...).
This is the line code:
Code: [Select]
unsigned int length = (8);
and this is the new error:
Quote
In function 'void loop()':
error: cannot convert 'float*' to 'byte*' for argument '2' to 'int FileLogger::append(const char*, byte*, long unsigned int)'


Reading this error message and the FileLogger .h and .ccp files, looks like if the data array should be only byte type of data.
It means that i should convert the reading from the sensors (in this case "readTemp()" and "readVcc()") to byte. I don´t know if it is possible and how to do it. does it means to convert to string?
I am lost in this point. I really appreciate any idea and help.

Thanks!

madepablo

#2
Nov 06, 2009, 11:18 pm Last Edit: Nov 06, 2009, 11:19 pm by madepablo Reason: 1
Hi again,

I am in the way...

Since the FileLogger library require a byte array, i changed the definition of the array to:
Code: [Select]
byte data[1];

and the length of each value of the array to 20 in that way:
Code: [Select]
unsigned int length = (20);

So the void loop() code looks like that right now:
Code: [Select]
void loop() {
 byte data[1];
 unsigned int length = (20);
 data[0]=(readTemp());
 data[1]=(readVcc());
 FileLogger::append("data.log", data, length);
 Serial.print(readTemp(), DEC); // read the inner temperature of arduino
 Serial.print(" x10^-4 degC   ");
 Serial.print(readVcc(), DEC); // read the voltage
 Serial.println("  mVolts");
 delay(1000);
}


And it compiles!! and it also save things into the SD!!
However, there are also bad news.. it not store the data....
what i have when i open the file into the wordpad, is this:
Quote
6L   8 % data.log x L    6L   8 % data.log x L    6L   8 % data.log x L    6L   8 % data.log x L    

and so on for a very long long line. It looks like if each value generate this sequence. In fact, the spaces seems to be tabs and not spaces...
So, the code is not correct!! but i am in the way.

As even, any comment and help is really welcome!

P.D. Sorry, this posts looks like my own blog!

PaulS

You declared data to be an array of length 1. Then, you tried to put more data into the array than would fit.

You didn't include the code for readTemp and readVcc, so I can't tell if they return bytes, or larger values.

When you told FileLogger to append 20 bytes, it tried, but you didn't supply 20 bytes. The array you supplied was only one byte long, so it grabbed some random data to fill the extra space.

Change data like this:

byte data[20];


madepablo

Thanks PaulS,

yeah, i included the complete code in posts #1 and #2. I included only the last changes in post #3 to made the post shorter.

Yes, you are right, there was an error on the number of members in the array. It is fixed!

readTemp() return a value such as 285000 meanwhile readVcc() returns a value such as 4940. how many bytes are this?? (sorry. probably is a very stupid question but i really don´t have idea...).

I will change the code and try again...
Thanks for the light!

madepablo

Hi again, here is the void loop code that i have right now:
Code: [Select]
void loop() {
 byte data[2];
 float temp=readTemp();
 float Vcc=readVcc();
 data[0]=(temp);
 data[1]=(Vcc);
 unsigned long length = sizeof(data[2]);
 FileLogger::append("data.log", data, length);
}


I made automatic the calculation of the size of the data array.
I declare correctly the size of the array.

It compiles, but the stored result is this:
Quote
5555555555555


So, may be i should convert the sequence of data into a long string, with "," in between the different data (in this case, temp and Vcc). Any idea about how to do it?
Thanks!

PaulS

In your earlier post, you declared readTemp and readVcc as returning longs. Longs are 4 bytes.

Setting a variable of one type (byte) to the value of another variable of a different type can result in loss of data, if the types are not the same size.

Longs and bytes are not the same size, so there is data loss when you stuff temp and Vcc into data[0] and data[1].

Finally, the sizeof function returns the size of the specified argument. You specified the third member of a two member array as the argument. The sizeof function figured out that the argument was of type byte, so it returned the size of a byte, in bytes, or 1.

So, the FileLogger logged one byte.

If you are going to use sizeof, the argument is the whole array, not one member of the array.

In any case, I think you are going to get better results if you convert the values to strings, and save them. The sprintf function will do that.

char message[20];
sprintf(message,"%l,%l", temp, Vcc);

Then, use strlen to get the length of the string, and pass message and the length to FileLogger::append.

madepablo

#7
Nov 07, 2009, 01:37 am Last Edit: Nov 07, 2009, 01:39 am by madepablo Reason: 1
Great PaulS!

Thanks so much for all this help. And also for the information about the bytes.

I made some changes in the code, but now i have problems in compilation.

Firstly, i changed the temp and Vcc to long type for don´t loss information.

Later, i included your ideas in the code, but i have a problem... the FileLogger procedure require a byte sequence, so i converted the message to an array, following the same example than in FileLogger example. But it have problems to know the size of the message...

Here is the code:
Code: [Select]
void loop() {
 long temp=readTemp();
 long Vcc=readVcc();
 char message[20];
 sprintf(message,"%l,%l", temp, Vcc);
 unsigned long length = strlen(message);
 byte data[]=message;
 FileLogger::append("data.log", data, length);
}


The error in compilation is this:
Quote
In function 'void loop()':
error: initializer fails to determine size of 'data'


as i said, if i remove the declaration of the array and call to the message in the FileLogger procedure, the error is this:
Quote
In function 'void loop()':
error: invalid conversion from 'char*' to 'byte*'


Thanks!

PaulS

The char and byte types are the same size. The FileLogger::append function says it wants a byte array, but what it means is that it wants a byte-sized array.

You have a byte-sized array, message. You simply need to tell the function that. You do that with a cast:

Code: [Select]
FileLogger::append("data.log", (byte *)message, length);


The (byte *) part is called a cast. It tells the compiler to treat message as though it was a byte array.

There are implicit and explicit casts. This code uses an implicit cast:

Code: [Select]
long temp;
int i = 14;

temp = i;


The temp variable expects a long value, but you can supply an int, and the compiler will convert the value to a long, implicitly, before storing it.

Any time you try to store a smaller value type in a larger value type, an implicit cast is performed, as long as the types are the same (int, float, etc.).

When the types are not the same, but the sizes are, an explicit cast is required.

When the types are not the same, and the sizes are not the same, an explicit cast may result in loss of data, if the destination is smaller than the source.

madepablo

#9
Nov 07, 2009, 02:40 am Last Edit: Nov 07, 2009, 02:43 am by madepablo Reason: 1
Great lesson PaulS, i am learning a lot!

I made the changes that you proposed. Now it compiles, but it produce a sequence of symbols without sense...

This is the code:
Code: [Select]
void loop() {
 long temp=readTemp();
 long Vcc=readVcc();
 char message[20];
 sprintf(message,"%l,%l", temp, Vcc);
 unsigned long length = strlen(message);
 FileLogger::append("data2.log", (byte*)message, length);
delay(1000);
}


and this what it write in the SD card, repeated in a long sequence:
Quote
@  <[ch8221]



=l
ÿ
jå[ch8226]



Thanks for your interest, help, efforts and patience!

PaulS

Add a Serial.println call after the sprintf function, to verify that message contains what it's supposed to, please. Let me know what it prints.

madepablo

#11
Nov 07, 2009, 03:06 am Last Edit: Nov 07, 2009, 03:09 am by madepablo Reason: 1
i did, and just only enters seems to be passed to the serial, because the vertical scroll increase each second, but not characters appear on the screen...

So rare, isn´t it?

UPDATE: at the end of the void loop() i also included some lines to print temp and Vcc, and they appear! So the problem is not related to the procedures but sending the results to the SD.

PaulS

What does your loop code look like, now?

PaulS

The sprintf statement should have "%ld, %ld", not "%l, %l".

madepablo

Great!!!! Congratulations!!!! it works!!!!

Yes, it was the problem... since i didn´t know whatt the "gramatic" of this expressions i couldn´t detect it. Good job!

Now i have other couple of question related to this... but much more easy!

it save all the data one near the other in a long long line. So i what to have very couple of data in a single line. In the example of FileLogger it is solver writing \r\n at the end of the string.
In the sentence that you builded, it not run in this way:
Code: [Select]
sprintf(message,"%ld,%ld", temp, Vcc, "\r\n");
Is there any other "magic" combination of characters such as "%ld" to do it? Any idea?

And, finally, if i increase the number of sensors, i suposse that i also should change (increasing) the value of char message[20], is it right?

Thanks so much for your efforts. I will prepare a clean code to post here for future readers, although it must be consider your contribution!
Thanks one more time for your great help and effort!

Thanks PaulS!

Go Up