A simple Data Collection Framework -- with timing

This is a simple framework you can drop your data collection into.

BUT it uses the DS1307 RTC library and the Time library.

Here is the sort of issue you might face....

I want to read the Analog Sensor Values or maybe a BMA180 accelerometer or maybe both (like I do)-- but every minute I want to collect the Air Pressure and the Temperature and the altitude. (Why not eh? )

I don't need to read the air pressure more often -- it does not change that fast -- at least not for my purposes of determining sound/vibration velocity.

So how do I do that? Try the code below!

When you run it it will synch the RTC clock to the time library and then start cycling. It will print out what it is doing to help you trace along. Then, every minute it stalls while it talks to that "other routine" where it does the "slow stuff".

I could have used an alarm function from the library -- or a timer and an interrupt -- but I wanted something simple and obvious.

Newbie question: (For extra points) Why did I NOT check to see if the time was increasing by one all the time? --- and then trip the additional data collection routine --- Would that have worked?

I don't think it depends on any of the extensions I added to Time and the DS1307 library.

It does require that you have set the clock as a separate issue -- say using the NNTP settime routine that comes with the DS1307 lib. I just programmed it to synch to a DS1307 that already was running and had the correct time set!

There are no great expositions of programming skill here -- just a routine that may be useful to a few people.

/*
 By WillR 
 A Template for getting some use out of the DS1307 Clock
 It uses the Time and the DS1307 libraries
 
 It simply allows a routine to be called whenever the minute value changes
 Nothing fancy. Just one way of doing it...
 
 Take out the delays when you collect data.  *******************
 Delays often interfere with interrupts and timers.  ***********
 
 March 25, 2011
 
 */

// check minute...
#include <Wire.h>

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


int myMinute1;
int myMinute2;
int myFactor;
int SetFlag;

void setup(){
  Serial.begin(9600);
  Serial.flush();
  Serial.println(); // give me a clean line to start logging

  // The RTC was set previously via the NNTP routine... So I will just SYNCH!
  
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  if(timeStatus()!= timeSet) 
    Serial.println("Unable to sync with the RTC");
  else
    Serial.println("RTC has set the system time"); 

  // time adjustment
  myFactor = 0;
  adjustTime(myFactor);     
  setSyncInterval(3600);
  //show the time 
  digitalClockDisplay();  
  delay(1000);
  // finished with the clock...
  

  myMinute1 = minute();

  Serial.print(" Minute is: ");
  Serial.println (myMinute1);
  Serial.println();
  
  SetFlag = 0;


}//end setup


void loop() {

  // the next routine gets the data that you wish to read as fast as possible
  // Perhaps it is a routine that is called only when you interrupt.

  Serial.println("  ****** Cycle Start *****");
  ReadMyUsualData();

  // Inside the next routine you can go off and read the data that is "low speed"
  // or that you only want to read every minute
  //  every time through the loop that the minute value changes -- you call the 
  // low speed routine.

  if (minute() != myMinute1) 
  { 
    myMinute1 = minute();

    Serial.print(" The Minute is: ");
    Serial.println(myMinute1);
   
    // update barometer -- call read function
    readmyotherData();
    
    /* If you wish, you could send the data to your PC via a separate routine here. 
       Maybe it should be a different format or a different port.
       Then you do not need the "pack the additional data area".
    */
    
  }  //end -- if minute!=

  SendAllTheData();


  Serial.println("  *********   Cycle End *****");
  Serial.println();
  delay(1000);


}  //end main loop
//*************** 



void ReadMyUsualData() {

  // read the analog sensors
  Serial.println (" Read a BMA180 Here -------->");
  // Call my BMA180 routines etc... 

} // end ReadMyUsualData


void readmyotherData(void){

  // read my data here

  SetFlag =1;  
  Serial.println(" Read my Barometer here  ------------------------->"); 
  delay(3000);
}  // end ReadMyOtherData


void SendAllTheData(void){

  //pack the usual data -- get it ready to send

    Serial.println(" Packing up the NORMAL data ... ");

  //check for some additional data before sending
  if (SetFlag == 1) {

    Serial.println(" Packing up the **additional** data ++++++++++");
    // put the addtional data in the buffer
    SetFlag = 0;
    delay(5000);
  }

  //now you can send the buffer -- UDP Serial or store it on the SD card...
  // Complete with your additional once-a-minute reading...

  Serial.println(" SENDING ALL the data -- ");

} //end SendAllTheData



// Clock Functions

void digitalClockDisplay(){
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year()); 
  Serial.println(); 
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

Simple and straightforward, usefull but there is one drawback I like to mention.

This works well with an action you want to do every minute() but maybe my sample frequency must be twice or 5 times a minute. How could that be done?

I think there is room for a "period" class, that detects a definable period has passed in just the same way. The Time class minute() divides time in periods of 60 seconds, The period class should be able to divide time in slots of seconds. What do you think of it? Multiple instances of the period class can use different timeslices.

Period period1(60); // == minutes()
Period period2(10); // 30 sec periods
Period period3(43200); // half day periods
Period period4(); // defaults to ???
Period period5(210); // 3 1/2 minute intervals.

int t = period2.slice();
while (t == period1.slice()) ; // wait until slice has passed.

What do you think of this idea?

Oh sure why not?

Pick a function -- any function. As for seconds -- sure change the sketch and use second() -- not minute() -- that should do it. Couple that with the MOD function (%)... and there are your irregular periods.

From Time.h -- use one or a combination of the following functions....

/*  time and date functions   */
int     hour();            // the hour now 
int     hour(time_t t);    // the hour for the given time
int     hourFormat12();    // the hour now in 12 hour format
int     hourFormat12(time_t t); // the hour for the given time in 12 hour format
uint8_t isAM();            // returns true if time now is AM
uint8_t isAM(time_t t);    // returns true the given time is AM
uint8_t isPM();            // returns true if time now is PM
uint8_t isPM(time_t t);    // returns true the given time is PM
int     minute();          // the minute now 
int     minute(time_t t);  // the minute for the given time
int     second();          // the second now 
int     second(time_t t);  // the second for the given time
int     day();             // the day now 
int     day(time_t t);     // the day for the given time
int     weekday();         // the weekday now (Sunday is day 1) 
int     weekday(time_t t); // the weekday for the given time 
int     month();           // the month now  (Jan is month 1)
int     month(time_t t);   // the month for the given time
int     year();            // the full four digit year: (2009, 2010 etc) 
int     year(time_t t);    // the year for the given time

Here is the whole Time.h library header -- it has "elapsed time and all that neat stuff...

/*
  time.h - low level time and date functions
  modified by D, Robinson Feb10, 2011 -- added access to square wave control byte
  see tmSQControl for example...
  and this below...
  uint8_t SQControl; // see DS1307 Sheet dwr feb10/2011  ** added tmSQControl above
*/

#ifndef _Time_h
#define _Time_h

#include <inttypes.h>

typedef unsigned long time_t;

typedef enum {timeNotSet, timeNeedsSync, timeSet
}  timeStatus_t ;

typedef enum {
    dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday
} timeDayOfWeek_t;

typedef enum {
    tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmSQControl,tmNbrFields
} tmByteFields;	   

typedef struct  { 
  uint8_t Second; 
  uint8_t Minute; 
  uint8_t Hour; 
  uint8_t Wday;   // day of week, sunday is day 1
  uint8_t Day;
  uint8_t Month; 
  uint8_t Year;   // offset from 1970;
  uint8_t SQControl; // see DS1307 Sheet dwr feb10/2011  ** added tmSQControl above
} 	tmElements_t, TimeElements, *tmElementsPtr_t;

//convenience macros to convert to and from tm years 
#define  tmYearToCalendar(Y) ((Y) + 1970)  // full four digit year 
#define  CalendarYrToTm(Y)   ((Y) - 1970)
#define  tmYearToY2k(Y)      ((Y) - 30)    // offset is from 2000
#define  y2kYearToTm(Y)      ((Y) + 30)   

typedef time_t(*getExternalTime)();
//typedef void  (*setExternalTime)(const time_t); // not used in this version


/*==============================================================================*/
/* Useful Constants */
#define SECS_PER_MIN  (60UL)
#define SECS_PER_HOUR (3600UL)
#define SECS_PER_DAY  (SECS_PER_HOUR * 24UL)
#define DAYS_PER_WEEK (7UL)
#define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK)
#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL)
#define SECS_YR_2000  (946684800UL) // the time at the start of y2k
 
/* Useful Macros for getting elapsed time */
#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)  
#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) 
#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
#define dayOfWeek(_time_)  ((( _time_ / SECS_PER_DAY + 4)  % DAYS_PER_WEEK)+1) // 1 = Sunday
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY)  // this is number of days since Jan 1 1970
#define elapsedSecsToday(_time_)  (_time_ % SECS_PER_DAY)   // the number of seconds since last midnight 
#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY)  // time at the start of the given day
#define nextMidnight(_time_) ( previousMidnight(_time_)  + SECS_PER_DAY ) // time at the end of the given day 
#define elapsedSecsThisWeek(_time_)  (elapsedSecsToday(_time_) +  (dayOfWeek(_time_) * SECS_PER_DAY) )   

/* Useful Macros for converting elapsed time to a time_t */
#define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN)  
#define hoursToTime_t   ((H)) ( (H) * SECS_PER_HOUR)  
#define daysToTime_t    ((H)) ( (D) * SECS_PER_DAY) 
#define weeksToTime_t   ((W)) ( (W) * SECS_PER_WEEK)   

/*============================================================================*/
/*  time and date functions   */
int     hour();            // the hour now 
int     hour(time_t t);    // the hour for the given time
int     hourFormat12();    // the hour now in 12 hour format
int     hourFormat12(time_t t); // the hour for the given time in 12 hour format
uint8_t isAM();            // returns true if time now is AM
uint8_t isAM(time_t t);    // returns true the given time is AM
uint8_t isPM();            // returns true if time now is PM
uint8_t isPM(time_t t);    // returns true the given time is PM
int     minute();          // the minute now 
int     minute(time_t t);  // the minute for the given time
int     second();          // the second now 
int     second(time_t t);  // the second for the given time
int     day();             // the day now 
int     day(time_t t);     // the day for the given time
int     weekday();         // the weekday now (Sunday is day 1) 
int     weekday(time_t t); // the weekday for the given time 
int     month();           // the month now  (Jan is month 1)
int     month(time_t t);   // the month for the given time
int     year();            // the full four digit year: (2009, 2010 etc) 
int     year(time_t t);    // the year for the given time

time_t now();              // return the current time as seconds since Jan 1 1970 
void    setTime(time_t t);
void    setTime(int hr,int min,int sec,int day, int month, int yr);
void    adjustTime(long adjustment);

/* date strings */ 
#define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null)
char* monthStr(uint8_t month);
char* dayStr(uint8_t day);
char* monthShortStr(uint8_t month);
char* dayShortStr(uint8_t day);
	
/* time sync functions	*/
timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized
void    setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider
void    setSyncInterval(time_t interval); // set the number of seconds between re-sync

/* low level functions to convert to and from system time                     */
void breakTime(time_t time, tmElements_t &tm);  // break time_t into elements
time_t makeTime(tmElements_t &tm);  // convert time elements into time_t


#endif /* _Time_h */

And from the TimeAlarms.h file -- even more possibilities... It requires the time library -- and that coupled with the DS1307 RTC lib means you can do some interesting things....

Maybe turn on the bed vibrator at 3AM the first morning then 5 minutes later every day... Just remember that divorce is not cheap!

TimeAlarmsClass();
  // functions to create alarms and timers
  AlarmID_t alarmRepeat(time_t value, OnTick_t onTickHandler);                    // trigger daily at given time of day
  AlarmID_t alarmRepeat(const int H,  const int M,  const int S, OnTick_t onTickHandler); // as above, with hms arguments
  AlarmID_t alarmRepeat(const timeDayOfWeek_t DOW, const int H,  const int M,  const int S, OnTick_t onTickHandler); // as above, with day of week 
 
  AlarmID_t alarmOnce(time_t value, OnTick_t onTickHandler);                     // trigger once at given time of day
  AlarmID_t alarmOnce( const int H,  const int M,  const int S, OnTick_t onTickHandler);  // as above, with hms arguments
  AlarmID_t alarmOnce(const timeDayOfWeek_t DOW, const int H,  const int M,  const int S, OnTick_t onTickHandler); // as above, with day of week 
  
  AlarmID_t timerOnce(time_t value, OnTick_t onTickHandler);   // trigger once after the given number of seconds 
  AlarmID_t timerOnce(const int H,  const int M,  const int S, OnTick_t onTickHandler);   // As above with HMS arguments
  
  AlarmID_t timerRepeat(time_t value, OnTick_t onTickHandler); // trigger after the given number of seconds continuously
  AlarmID_t timerRepeat(const int H,  const int M,  const int S, OnTick_t onTickHandler);   // As above with HMS arguments
  
  void delay(unsigned long ms);

Have fun thinking of more ideas folks! ]:slight_smile:

Dr. Evil.

I will try to add a little more to this later to help with more complicated -- but still simple sensor projects.

Hopefully it will help a few weekend warriors get a quick start.