Some simple code help....

Hi,

I have been ‘playing’ with datalogging and getting some results. I have tried tweaking the code but just don’t know enough yet to get it right… hoping someone might have a couple of mo’s to point me in the right direction.

The two things I am trying to achieve are:
Include the YEAR in the date (not coming out right at the mo - I think it is something to do with the buffer? - Year was not in the original code)
Change the sample time from 1 second to 5 seconds (eventually I would like to use a variable to set the sample time in seconds… or maybe its already doing this?)

Thanks for any help - I am learning… slowly :slight_smile:

The data as output at the moment:

01/03//0, 17:05:24, 016
01/03//0, 17:05:25, 015
01/03//0, 17:05:26, 016
01/03/30, 17:05:27, 015
01/03/30, 17:05:28, 015

The Sketch:

#include "FileLogger.h"
#include "DS1307.h"
#include <WProgram.h>
#include <Wire.h>

#define Timing 0
#define Accept  1
#define Record  2


byte start[7]= { 'B','e','g','i','n',0x0D,0x0A};
byte buffer[23];
int temp;
byte ASCII[10]={'0','1','2','3','4','5','6','7','8','9'};
unsigned char result;
unsigned char state;
int time=0;
int now=0;
int oldtime=0;


void setup(void)
{
  result = FileLogger::append("data.txt", start, 7);//Initial the SD Card
  while(result)  result = FileLogger::append("data.txt", start, 7);
  RTC.stop();
  RTC.set(DS1307_MIN,05);     //set the minutes
  RTC.set(DS1307_HR,17);       //set the hours
  RTC.set(DS1307_DATE,01);       //set the date
  RTC.set(DS1307_MTH,03);        //set the month
  RTC.set(DS1307_YR,10);         //set the year
  RTC.start();
}

void loop(void) 
{
  
  switch(state)
  { 
  case Timing:

        time=RTC.get(DS1307_SEC,true);
        delay(200); 
        if(time!=oldtime)
        { 
          oldtime=time;    
          temp=RTC.get(DS1307_DATE,false);
          buffer[0]=ASCII[(temp/10)];
          buffer[1]=ASCII[(temp%10)];
          buffer[2]='/';
          temp=RTC.get(DS1307_MTH,false);
          buffer[3]=ASCII[(temp/10)];
          buffer[4]=ASCII[(temp%10)];
          buffer[5]='/';
          temp=RTC.get(DS1307_YR,false);
          buffer[6]=ASCII[(temp/10)];
          buffer[7]=ASCII[(temp%10)];
          buffer[8]=',';
          temp=RTC.get(DS1307_HR,false);
          buffer[9]=ASCII[(temp/10)];
          buffer[10]=ASCII[(temp%10)];
          buffer[11]=':';
          temp=RTC.get(DS1307_MIN,false);
          buffer[12]=ASCII[(temp/10)];
          buffer[13]=ASCII[(temp%10)];
          buffer[14]=':';
          //temp=RTC.get(DS1307_SEC,false);
          buffer[15]=ASCII[(time/10)];
          buffer[16]=ASCII[(time%10)];
          buffer[17]=',';
          state=Accept;
        }
    break;

  case Accept: 
     temp=analogRead(0);
     buffer[18]=ASCII[(temp/100)];
     buffer[19]=ASCII[((temp%100)/10)];
     buffer[20]=ASCII[(temp%10)];
     buffer[21]=0x0D;
     buffer[22]=0x0A;
     state=Record;
    break;

  case Record:
    result = FileLogger::append("data.txt", buffer, 23);
    if (result==0)
    {
      state=Timing;
    }
    break;

  default:
    state=Timing;
    break;

  }

}

Your 1 second vs. 5 second problem is cured by looking at how these two lines work, and making appropriate modifications. Have a look at the Blink Without Delay example for clues.

if(time!=oldtime)
        {
          oldtime=time;

I'd suggest looking at the documentation for the DS1307 library. I think that the function to get the year is returning a value like 2010. Dividing that by 10 results in the first digit being 201. There is no value in your ASCII array in position 201.

Have you noticed that ASCII[x] = x + '0' for all values of x that you have defined? You don't really need this array. Just add '0' to all single digit integers to get the corresponding character.

Hi Paul & Groove,

Thanks for getting back to me, much appreciated. As I don't fully understand what the code is doing I think I might start from scratch and build a sketch that I do understand - even if it isn't elegant I will hopefully learn a lot !!! Will use your points as a start so thanks again and have a good weekend :)

point5, here is a simple sketch that I think does what you want:

#include "FileLogger.h"
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t

byte start[7]= { 'B','e','g','i','n',0x0D,0x0A};
byte Buffer[24];
int temp;
time_t oldtime=0;
int result;


void setup(void)
{ 
  do
  {
     result = FileLogger::append("data.txt", start, 7);//Initial the SD Card  
  }
  while(result != 0);  
// the next two lines can be removed if the RTC has been set
  setTime(17,05,0,1,3,10); // set time to 17:05:00  1 Mar 2010 
  RTC.set(now());  // set the RTC to the current time (set in the previous line)

  setSyncProvider(RTC.get);   // the function to get the time from the RTC
}

void loop(void)
{
  if( now() != oldtime)
  {
    // here if a second has elapsed
     do
     {
       temp=analogRead(0);
       sprintf((char*)Buffer,"%02d/%02d/%02d,%02d:%02d:%02d,%03d\r\n",day(),month(), year()-2000, hour(), minute(), second(),temp );

       oldtime = now(); 
       result = FileLogger::append("data.txt", Buffer, 23);
     }
     while( result != 0);   
  }
}

This uses the Time and DS1307RTC libraries from the playground: http://www.arduino.cc/playground/Code/Time

Once the RTC is set to the correct time, you can remove the code to set the time and RTC in setup.

At the moment, the code you have uses the fact that the current "second" value read is different to the last. Since this value will count 0..59, simply count the every five changes and then do your logging. Then reset your counter.

// Global scope or static declarations
const byte loggingRate = 5;
byte logPeriod = loggingRate;
...
...
  time=RTC.get(DS1307_SEC,true);
  delay(200);
  if(time!=oldtime)
  {
    oldtime=time;
    logPeriod--;
    if (logPeriod == 0) {
       logPeriod = loggingRate;
       // now do your logging here

This avoids messy modulo arithmetic.

I missed out the logging rate functionality, to add this to the sketch I posted above, add the loggingRate variable declaration and change: if( now() != oldtime) to if( now() >= oldtime + loggingRate)

for example, if loggingRate is set equal to 5 then logging will occur every 5 seconds

Thanks for the noted code mem, I do understand better now :-)… and it works just great.

Thanks AWOL, I’m not sure I understand, would it be posible to expalin a little more ?

The next step I would like to make is reading variables from a config.txt file (on the SD card) and using them in the sketch. I need to slowly work out what I’m doing here but if you think the concept is do-able I would welcome any thoughts.

Start time: 17:00:00
Start date: 16/02/2010
Sample interval seconds: 0
Sample interval minutes: 5
Sample interval hours: 0
Sample value during interval: Average (0), Max (1), Min (2)
Stop: When full (0), indefinite (1)

I just received a Seeeduino Stalker last week and have been playing with the demo code that came with it. The code is the same code that point5 posted. I was having problems with the code so I found this thread. The code mem posted works good but has a problem if I add any Serial.print statements. When I add the Serial.begin() statement, it causes the date to go bad. Serial.print statements will cause the program to hang. I experimented with many different combinations but it appears that there is a conflict between the filelogger and DC1307RTC libraries and the Serial.print statement.

Any help is appreciated and thanks mem for the code. Following is my sketch:

#include "FileLogger.h"
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t

byte start[7]= { 'B','e','g','i','n',0x0D,0x0A};
byte Buffer[24];
int temp;
time_t oldtime=0;
int result;
unsigned long loggingRate=5;

void setup(void)
{ 
  Serial.begin(9600);
  Serial.println("Initializing SD Card");
  do
  {
     result = FileLogger::append("data.txt", start, 7);//Initialize the SD Card
  }
  while(result != 0);  
  Serial.println("SD Card Initialized");
// the next two lines can be removed if the RTC has been set
//  setTime(17,05,0,1,3,10); // set time to 17:05:00  1 Mar 2010
//  RTC.set(now());  // set the RTC to the current time (set in the previous line)

  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  Serial.println("RTC has set the system time");      

}

void loop(void)
{
  if( now() >= oldtime + loggingRate)
  {
    // here if a loggingRate seconds has elapsed
     do
     {
//       temp=analogRead(0);
         temp=999;
       sprintf((char*)Buffer,"%02d/%02d/%02d,%02d:%02d:%02d,%03d\r\n",day(),month(), year()-2000, hour(), minute(), second(),temp );

       oldtime = now();
       result = FileLogger::append("data.txt", Buffer, 23);
     }
     while( result != 0);
     Serial.println((char*)Buffer);
   }
}

The DS1307RTC library works fine with print – the examples in the download use Serial.print extensively.

I wonder if you are running out of RAM. You could test with a simpler version of your code. Comment out all the print statements in setup. Comment out the sprintf statement in loop and just Serial.print the minutes and seconds: Serial.print(minute()); Serial.print(" "); Serial.println(second());

If that works then you probably have a memory problem.

What controller chip are you using?

Thanks for the reply. In addition to removing the print statements in setup, I had to move the "Serial.begin(9600);" statement from the beginning to the end of setup or else the sketch would hang.

Commenting out the sprintf statement caused the date to print corrrectly. I assigned temporary data to the Buffer and it recorded to the SD card correctly.

The Stalker uses the ATmega 168 microprocesser.

I had to move the "Serial.begin(9600);" statement from the beginning to the end of setup or else the sketch would hang.

Can you post the version of the sketch that works. Did you try that version with Serial.begin at the start of setup?

I moved the Serial.begin statement back to the beginning and it worked a couple of times and then stopped working when the SD card became corrupted. I reformatted the card and tried again and it worked as long as I gave the Stalker a little extra time to reset between closing and opening the serial monitor. The onboard led flashes each time the card is written to so I wait until it flashes a few times before starting the serial monitor. The led stays on continuously if the SD card is corrupted. Following is the code:

#include "FileLogger.h"
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t

byte start[7]= { 
  'B','e','g','i','n',0x0D,0x0A};
byte testData[11]= { 
  'T','e','s','t',' ','D','a','t','a',0x0D,0x0A};
byte Buffer[24];
int temp;
time_t oldtime=0;
int result;
unsigned long loggingRate=5;

void setup(void)
{ 
  Serial.begin(9600); //works here
  do
  {
    result = FileLogger::append("data.txt", start, 7);//Initialize the SD Card
  }
  while(result != 0);  
  // the next two lines can be removed if the RTC has been set
  //  setTime(17,05,0,1,3,10); // set time to 17:05:00  1 Mar 2010
  //  RTC.set(now());  // set the RTC to the current time (set in the previous line)

  setSyncProvider(RTC.get);   // the function to get the time from the RTC

  //Serial.begin(9600); //works here
}

void loop(void)
{
  if( now() >= oldtime + loggingRate)
  {
    // here if a loggingRate seconds has elapsed
    do
    {
      //       temp=analogRead(0);
      temp=999;
      //sprintf((char*)Buffer,"%02d/%02d/%02d,%02d:%02d:%02d,%03d\r\n",day(),month(), year()-2000, hour(), minute(), second(),temp );
      oldtime = now();
      result = FileLogger::append("data.txt", testData, 11);
      //result=0;
    }
    while( result != 0);
    Serial.println((char*)testData);
    Serial.print(month());
    Serial.print("/");
    Serial.print(day());
    Serial.print("/");
    Serial.print(year());
    Serial.print(" ");
    Serial.print(hour());
    Serial.print(":");
    Serial.print(minute());
    Serial.print(":");
    Serial.println(second());
  }
}

I modified your example code to record data from my electrical meter and it appears to be working good. Thanks! I’m new to the Arduino so it is taking a little while to get things worked out.

I did a little more experimenting and found the code will run with the sprintf statement as long as it included only three time elements. Any more and it would over run the Ram and give bad time data. Following is the code that worked:

#include "FileLogger.h"
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t

byte start[7]= { 
  'B','e','g','i','n',0x0D,0x0A};
byte Buffer[10];
int temp;
time_t oldtime=0;
int result;
unsigned long loggingRate=5;
int length;

void setup(void)
{ 
  do
  {
    result = FileLogger::append("data.txt", start, 7);//Initialize the SD Card
  }
  while(result != 0);  
  // the next two lines can be removed if the RTC has been set
  //  setTime(17,05,0,1,3,10); // set time to 17:05:00  1 Mar 2010
  //  RTC.set(now());  // set the RTC to the current time (set in the previous line)

  setSyncProvider(RTC.get);   // the function to get the time from the RTC

  Serial.begin(9600);
}

void loop(void)
{
  if( now() >= oldtime + loggingRate)
  {
    // here if a loggingRate seconds has elapsed
    do
    {
      //temp=analogRead(0);
      //temp=999;
      //sprintf((char*)Buffer,"%02d/%02d/%02d,%02d:%02d:%02d,%03d\r\n",day(),month(), year()-2000, hour(), minute(), second(),temp );
      sprintf((char*)Buffer,"%02d:%02d:%02d\r\n",hour(),minute(),second());
      length=sizeof(Buffer);
      oldtime = now();
      result = FileLogger::append("data.txt", Buffer, length);
    }
    while( result != 0);
    Serial.print(month());
    Serial.print("/");
    Serial.print(day());
    Serial.print("/");
    Serial.print(year());
    Serial.print(" ");
    Serial.print(hour());
    Serial.print(":");
    Serial.print(minute());
    Serial.print(":");
    Serial.println(second());
  }
}

I also modified the code mem posted to record data from my electric meter and it worked well for a 24 hour period. Following is the code:

//Meter reading program using Itron smart meter
//Added LED that turns on/off with each watt

#include "FileLogger.h"
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t

const int ledPin = 9; // LED watt indicator initialization. Turns on or off each time a watt is measured
int ledState = LOW;

volatile unsigned long wattSensor=0;  //Counts power pulses in interrupt, 1 pulse = 1 watt
unsigned long wattSensorTemp=0;  //Temporary power pulse counts for printing
unsigned long totalWatts=0; //Temporary total power used for printing - watts

byte start[7]= { 
  'B','e','g','i','n',0x0D,0x0A};
byte Buffer[40];
time_t oldtime=0;
int result;
unsigned long loggingRate=5;

void setup(void)
{ 
  pinMode(ledPin, OUTPUT);  // LED watt indicator initialization
  attachInterrupt(1, wattSensorInterrupt, FALLING);  //interrupt1 - digital pin 3 - If we detect a change from HIGH to LOW we call wattSensorInterrupt

  do
  {
    result = FileLogger::append("data.txt", start, 7);//Initialize the SD Card
    delay(1000);
  }
  while(result != 0);  
  // the next two lines can be removed if the RTC has been set
  //  setTime(17,05,0,1,3,10); // set time to 17:05:00  1 Mar 2010

    setSyncProvider(RTC.get);   // the function to get the time from the RTC
  //  Serial.begin(9600);

}

void loop(void)
{
  //  if( now() >= oldtime + loggingRate) //logs data at loggingRate in seconds
  if( minute() != oldtime) //logs data each minute
  {
    //here if a loggingRate seconds has elapsed
    wattSensorTemp=wattSensor; //watts
    wattSensor=0; //reset watts counter
    totalWatts=totalWatts+wattSensorTemp; //total watts counter
    oldtime = minute(); //use this for one minute interval
    //oldtime = now(); //or this for loggingRate interval

    do
    {
      sprintf((char*)Buffer,"%02d/%02d/%02d,%02d:%02d:%02d,%05lu,%08lu\r\n",day(),month(), year()-2000, hour(), minute(), second(),wattSensorTemp,totalWatts);

      result = FileLogger::append("data.txt", Buffer, 34);
    }
    while( result != 0);
    //     Serial.println((char*)Buffer);
  }
}

void wattSensorInterrupt() //Interrupt1 counter routine for counting watts
{
  wattSensor=wattSensor+1;  //Update number of pulses, 1 pulse = 1 watt
  if (ledState == LOW) //Cycle LED on/off each time one watt is counted
    ledState = HIGH;
  else
    ledState = LOW;
  digitalWrite(ledPin, ledState);
}

I would like to transmit the data via an xBee using the serial.print statement so if anybody has an ideal how to do this within the Ram constraints of the mega168 I would appreciate any advice.

The easiest way to reduce ram usage is to remove all the serial code (it looks like its only used for debugging)

If you do need serial output but are not using Serial receive you could reduce the serial buffer in HardwareSerial.cpp (change RX_BUFFER_SIZE from 128 down to something like 8).

Another approach if the log data is intended to be read by some software is to store the numeric time instead of using sprintf. Most software can convert the time values into a user friendly time string.

Thanks for the advice. I set RX_BUFFER_SIZE from 128 to 8 and now the Serial.print works for the full sprintf statement. I also added the print statements to my electric meter program and it sends the serial data through the xBee.

I am also going to look into saving the data as numeric data instead of using sprintf. I am using Access to store the data so all I really need to log is UnixTime, oneMinuteWatts, and totalWatts.

Once again, thanks for the help.

Good to hear you have it working. A google search should result in many hits for displaying the format used by the Time library (Unix time) in Access.

I reworked the sketch without using sprintf and replacing the ASCII array with the ASCII = x + ‘0’ as PaulS noted. Seeeduino has indicated that they hope to release the Stalker with the ATmega 328 once they have a good supply of the 328. At that time, the sprintf version might be a good option.

I am going to post the sketch on the exhibition section to help other Stalker users. Following is the latest sketch:

#include "FileLogger.h" // http://code.google.com/p/arduino-filelogger/
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library - http://www.arduino.cc/playground/Code/Time

byte start[7]= { 
  'B','e','g','i','n',0x0D,0x0A};
byte Buffer[23]; //Buffer for printing to SD card
int temp; //temp test data
time_t oldtime=0; //last time loop has ran
int result; //status of SD card write - 0 = "OK", 1 = "Fail initializing", 2 = "Fail appending"
unsigned long loggingRate=5; //logging rate in seconds
int length; //length of buffer string

void setup(void)
{ 
  do
  {
    result = FileLogger::append("data.txt", start, 7);//Initialize the SD Card
  }
  while(result != 0);

  // the next two lines can be removed if the RTC has been set

  //  setTime(17,05,0,1,3,10); // set time to 17:05:00  1 Mar 2010 (see below)
  //  RTC.set(now());  // set the RTC to the current time (set in the previous line)

  // format for setting time - setTime(hr,min,sec,day,month,yr);

  setSyncProvider(RTC.get);   // the function to get the time from the RTC

  Serial.begin(9600); //initialize serial port
}

void loop(void)
{
  //if( now() >= oldtime + loggingRate) //proceed with loop if loggingRate seconds have elapsed
  if(minute() != oldtime) //use this if 1 minute intervals is desired
  {
    //oldtime = now(); //use this if loggingRate seconds is desired
    oldtime = minute(); //use this if 1 minute intervals is desired
    do
    {
      temp=month();
      Buffer[0]=(temp/10)+'0';
      Buffer[1]=(temp%10)+'0';
      Buffer[2]='/';
      temp=day();
      Buffer[3]=(temp/10)+'0';
      Buffer[4]=(temp%10)+'0';
      Buffer[5]='/';
      temp=year();
      Buffer[6]=((temp-2000)/10)+'0';
      Buffer[7]=((temp-2000)%10)+'0';
      Buffer[8]=' ';
      temp=hour();
      Buffer[9]=(temp/10)+'0';
      Buffer[10]=(temp%10)+'0';
      Buffer[11]=':';
      temp=minute();
      Buffer[12]=(temp/10)+'0';
      Buffer[13]=(temp%10)+'0';
      Buffer[14]=':';
      temp=second();
      Buffer[15]=(temp/10)+'0';
      Buffer[16]=(temp%10)+'0';
      Buffer[17]=' ';
      temp=999;
      Buffer[18]=(temp/100)+'0';
      Buffer[19]=((temp%100)/10)+'0';
      Buffer[20]=(temp%10)+'0';
      Buffer[21]=0x0D;
      Buffer[22]=0x0A;

      length=sizeof(Buffer);
      result = FileLogger::append("data.txt", Buffer, length); //write data to SD card
    }
    while( result != 0); //repeat loop until successful write to SD card

    for (int i=0; i < length ;i++) {
      Serial.print(Buffer[i]); //Serial print buffer string to serial monitor or xBee
    }
  }
}

Looks good.

One small improvement that will make the code more robust is to get the time once only to prevent the case where the clock rolls over in the middle of a log. Although this is unlikely to happen in practice, the code to prevent this is easy to implement.

Here is some (untested) loop code that avoids rollover:

void loop(void)
{
  time_t t = now();
  //if(t >= oldtime + loggingRate) //proceed with loop if loggingRate seconds have elapsed
  if(minute(t) != oldtime) //use this if 1 minute intervals is desired
  {
    //oldtime = t; //use this if loggingRate seconds is desired
    oldtime = minute(t); //use this if 1 minute intervals is desired
    do
    {
      temp=month(t);
      Buffer[0]=(temp/10)+'0';
      Buffer[1]=(temp%10)+'0';
      Buffer[2]='/';
      temp=day(t);
      Buffer[3]=(temp/10)+'0';
      Buffer[4]=(temp%10)+'0';
      Buffer[5]='/';
      temp=year(t);
      Buffer[6]=((temp-2000)/10)+'0';
      Buffer[7]=((temp-2000)%10)+'0';
      Buffer[8]=' ';
      temp=hour(t);
      Buffer[9]=(temp/10)+'0';
      Buffer[10]=(temp%10)+'0';
      Buffer[11]=':';
      temp=minute(t);
      Buffer[12]=(temp/10)+'0';
      Buffer[13]=(temp%10)+'0';
      Buffer[14]=':';
      temp=second(t);
      Buffer[15]=(temp/10)+'0';
      Buffer[16]=(temp%10)+'0';
      Buffer[17]=' ';
      temp=999;
      Buffer[18]=(temp/100)+'0';
      Buffer[19]=((temp%100)/10)+'0';
      Buffer[20]=(temp%10)+'0';
      Buffer[21]=0x0D;
      Buffer[22]=0x0A;

      length=sizeof(Buffer);
      result = FileLogger::append("data.txt", Buffer, length); //write data to SD card
    }
    while( result != 0); //repeat loop until successful write to SD card

    for (int i=0; i < length ;i++) {
      Serial.print(Buffer[i]); //Serial print buffer string to serial monitor or xBee
    }
  }
}

Perhaps even better is to simply log the four byte value returned from the now function and let the reading side convert this into strings. This reduces code on the Arduino side and allows the reading code to do simple date math and also to display the date and time in whatever local format the user wants.

Thanks for the advice. I had seen the use of “time_t t = now();” routine in the playground and considered using it but had not noticed any use of it in any sketches I could find. I agree that it would be a good ideal to use this method and will include it in future sketches.

I agree that it would be best to store the value from now() for data logging and data transmittal but my goal with this sketch is to provide a better demo code for the Seeeduino Stalker that a new user can understand. IMO the Stalker demo code that the OP posted is much too complicated for that purpose. I think I will create two sketches using both methods.

So far my experience with the Stalker has been positive and it looks like it might be a good platform for a simple inexpensive data logger but needs better documentation and better example sketches.