Array of Char Arrays Question

Hello everyone,

I’m trying to create a dynamic array of strings, using the RTC module. I’ve managed to retrieve the time as a pointer to a char array in the format of “12/3/12 14:45:44”. Now, I’m trying to capture them as a separate char array of arrays to store as a global variable, with the push of a button. It is sort of a data logger, but it stores the values as a big array. (i’ll be using the char array of arrays to communicate between two nRLF2401+ modules).

Below is my code. The bottom functions are for the RTC Clock. The trouble I believe I’m running into is within void(loop).

#include <SPI.h>
#include <String.h>

const int  cs = 6;  //  chip select for the RTC module

const int maxRecordings = 100;

char * myStrings[maxRecordings] = {};

const int timeSaveButton = 2; //  Button pin for saving time and reading time

int n = 0;

int saveButtonState; //  Interrupt states for buttons
int saveLastButtonState = 0;

void setup(){
  Serial.begin(9600);

  pinMode(timeSaveButton, INPUT);
  
  RTC_init();
  Serial.begin(9600);
}

void loop(){
  //when button is pressed;
  saveButtonState = digitalRead(timeSaveButton);
  if (saveButtonState != saveLastButtonState && saveLastButtonState == HIGH) {
    for (int i = 0; i <= n; i++){
      if (i == n) {
        myStrings[i] = readTimeDate();
      }    
      Serial.print("Recording #");
      Serial.print(i);
      Serial.print(": ");
      Serial.println(myStrings[i]);
    }
    n++;
    Serial.println("");          
  }
  saveLastButtonState = saveButtonState;
  delay(1000);
}
 
 
int RTC_init(){ 
	  pinMode(cs, OUTPUT); // chip select
	  // start the SPI library:
	  SPI.begin();
	  SPI.setBitOrder(MSBFIRST); 
	  SPI.setDataMode(SPI_MODE3); // both mode 1 & 3 should work 
	  //set control register 
	  digitalWrite(cs, LOW);  
	  SPI.transfer(0x8E);
	  SPI.transfer(0x60); //60= disable Osciallator and Battery SQ wave @1hz, temp compensation, Alarms disabled
	  digitalWrite(cs, HIGH);
	  delay(10);
}
//=====================================
int setTimeDate(int d, int mo, int y, int h, int mi, int s){ 
	int TimeDate [7]={s,mi,h,0,d,mo,y};
	for(int i=0; i<=6;i++){
		if(i==3)
			i++;
		int b= TimeDate[i]/10;
		int a= TimeDate[i]-b*10;
		if(i==2){
			if (b==2)
				b=B00000010;
			else if (b==1)
				b=B00000001;
		}	
		TimeDate[i]= a+(b<<4);
		  
		digitalWrite(cs, LOW);
		SPI.transfer(i+0x80); 
		SPI.transfer(TimeDate[i]);        
		digitalWrite(cs, HIGH);
  }
}
//=====================================
char * readTimeDate(){
  
	String temp;
	int TimeDate [7]; //second,minute,hour,null,day,month,year		
	for(int i=0; i<=6; i++) {
		if(i==3)
			i++;
		digitalWrite(cs, LOW);
		SPI.transfer(i+0x00); 
		unsigned int n = SPI.transfer(0x00);        
		digitalWrite(cs, HIGH);
		int a=n & B00001111;    
		if(i==2){	
			int b=(n & B00110000)>>4; //24 hour mode
			if(b==B00000010)
				b=20;        
			else if(b==B00000001)
				b=10;
			TimeDate[i]=a+b;
		}
		else if(i==4){
			int b=(n & B00110000)>>4;
			TimeDate[i]=a+b*10;
		}
		else if(i==5){
			int b=(n & B00010000)>>4;
			TimeDate[i]=a+b*10;
		}
		else if(i==6){
			int b=(n & B11110000)>>4;
			TimeDate[i]=a+b*10;
		}
		else{	
			int b=(n & B01110000)>>4;
			TimeDate[i]=a+b*10;	
			}
	}
	temp.concat(TimeDate[4]);
	temp.concat("/") ;
	temp.concat(TimeDate[5]);
	temp.concat("/") ;
	temp.concat(TimeDate[6]);
	temp.concat(" ") ;
	temp.concat(TimeDate[2]);
	temp.concat(":") ;
	temp.concat(TimeDate[1]);
	temp.concat(":") ;
	temp.concat(TimeDate[0]);

// added the below bit to return a char array, easier to save in EEPROM.

  const int len = 51;
  char buf[len];
  temp.toCharArray(buf, len);

  return buf;
}

The result of this code is such:

Recording #0: 22/2/12 3:49:18

Recording #0: 22/2/12 3:49:18
Recording #1: 22/2/12 3:49:23

Recording #0: 22/2/12 3:49:23
Recording #1: 22/2/12 3:49:23
Recording #2: 22/2/12 3:49:29

As you can see, once the pointer is updated with readTimeDate(), it changes all the myStrings values, since essentially they are the same pointers. How can I store the value readTimeDate() is initially pointing to?

Additionally, re: the way myStrings is declared is a little confusing.

const int maxRecordings = 100;

char * myStrings[maxRecordings] = {};

Do I always have to say the length of my char arrays? Is it not possible to create a dynamic array that grows whenever I wish to input a value? The below code compiles correctly, however returns jibberish once I actually open the serial monitor.

//const int maxRecordings = 100;

char * myStrings[] = {};

Any help would be greatly appreciated. Original code for the RTC functions can be found in the Sparkfun website below.

http://www.sparkfun.com/datasheets/BreakoutBoards/DS3234_Example_Code.c

All the best,

Mert

Think about what you're doing; the pointers in the array points to the same bit of memory every time. If you want to save different times, I'd just use the "millis" value stored in a 32 bit unsigned long, or similar.

AWOL, thanks for the prompt reply.

I agree that the code isn't correct, it is pointing to the same place every time. I'm straying away from milis() since it resets every time the Arduino restarts. I'm using the DS3234 RTC module to keep track of time externally. The breakout board can be found here:

http://www.sparkfun.com/products/10160

M

OK, then convert the RTC's output to Unix time or something similar - a 32 bit number of seconds since 1970 (or whatever datum you choose) is a lot more compact than a string.

char * myStrings[maxRecordings] = {};

If you are not actually going to supply initializers, leave off the = {} bit.

char * readTimeDate()
{
  char buf[len];
  temp.toCharArray(buf, len);

  return buf;
}

buf is a local variable that goes out of scope when the function ends. You can not return a pointer to it.

        myStrings[i] = readTimeDate();

myStrings[ i ] now points to memory that is going to be reused on the next call to the function, or any other function.

The readTimeDate function needs to be supplied with a persistent place to store data.

char myStrings[maxRecordings][51]; would be a better way to define myStrings. Then,

readDateTime(myStrings[i]);

to get the data into the ith position in the myStrings array, and readDateTime() looks like:

void readDateTime(char *dest)
{
.
.
.
  const int len = 51;
  temp.toCharArray(dest, len);
}

Although, if you are going to supply the function with a char array, dump the String crap, and populate the array directly.

Keep in mind that the myStrings array is going to take 5100 bytes of the 2000 available on the Duemilanove/UNO/Mini, etc.

Thanks for all the suggestions Paul. I didn't think of the memory challenge with storing big arrays, you're absolutely right.

AWOL, I love just using the unixtime values, however the current library I'm using can't return those. Any tips on where to start? I think if I can manage to get those it will be way easier/more efficient to create a dynamic int array.

This one looks promising, I'll dig deeper.

http://www.l8ter.com/?p=375

No luck so far. The maniacbug code isn't giving consistent returns as much as the original Sparkfun RTC code. Any idea how to return Unixtime (seconds since 1st of Jan 1970) using that snippet?

        int a=n & B00001111;    
        if(i==2){   
            int b=(n & B00110000)>>4; //24 hour mode
            if(b==B00000010)
                b=20;        
            else if(b==B00000001)
                b=10;
            TimeDate[i]=a+b;
        }
        else if(i==4){
            int b=(n & B00110000)>>4;
            TimeDate[i]=a+b*10;
        }
        else if(i==5){
            int b=(n & B00010000)>>4;
            TimeDate[i]=a+b*10;
        }
        else if(i==6){
            int b=(n & B11110000)>>4;
            TimeDate[i]=a+b*10;
        }
        else{   
            int b=(n & B01110000)>>4;
            TimeDate[i]=a+b*10; 
            }

Any idea what those B01110000 numbers are?

Thanks in advance!