gaining accurate time on time sensitive measurements.

Hello,

I am currently using the Arduino UNO with the ADA Fruit CC3000 WIFI shield and the ADA Fruit Ultimate GPS. I am "trying to" use this combination to log weather data from an ultrasonic anemometer, "fancy 3d wind velocity sensor", and a Pyranometer. These are all analog output sensors. The anemometer is 0-5 volt and the Prranometer is 0-2.5 volts. I have been able to write a simple code that reads the analog voltage readings and writes them to a file with a time step equal to my delay. This is not accurate enough for me, I will be reading data at 20hz . So I then tried to use the built in timers. I tried to use Timer 1, but this either locks the arduino, (I have a line that serial writes "Card Found... We will begin recording" but the serial write freezes at "Ca) or throws an internal return. I do not know... I think this has to do with the WIFI shield using Timer1... Is there another way to gain accurate timing? And how would i use the PPS output on the GPS shield to "resync" the time? Or if someone could point me in the right direction, I would also appreciate that.

Thank you. (Also, please let me know what other information would be needed if I did not supply it....)

I'm curious as to why you need to record from a pyranometer and anemometer at 20Hz. Seems fast to me.

For 20Hz, you could use the pulse-per-second signal from the GPS as an interrupt to give an accurate indication of the start of a second, take a reading, and then use a blink-without-delay type of code to trigger the other 19 readings after that.

Pete

I haven’t use a GPS system with an Arduino but my Garmin hand-held GPS displays the time. Isn’t that available from your GPS system?

…R

Given the WiFi shield, it would also be possible to use a network time source.

the reason for the 20hz sample rate is for measuring turbulence. Basically you would have your measured wind velocity U, you then say that U=Mean(U(t))+uprime(t,x). the uprime is your turbulence value.

As for triggering the interrupt with the pps, who would you create your own interrupt? Also, I dont want to use the WIFI if i dont have to... this will be operating by solar power... Mostly the wifi card is there if a bird lands on my sensor, knocks it over, and disconnects something... the sensor will then tweet that it needs help...

as for the blink without delay... have something like

void loop(){
if ping==1{
[record everything 20 times with a while loop]
ping=0;
}
}
[PPS interrupt]{
ping=1;
}
???

Don't use interrupts. They are no help for this problem and will just bring in a whole new layer of problems.

You can use the Arduino's internal clock to time things to the accuracy of the crystal/resonator. The accuracy varies depending on the type of board you have but none of them are especially accurate and certainly not good enough to provide an accurate timestamp over the long term. You will need an external clock source. You could get that from GPS, or from you WiFi network connection. Both of those options will need power. You could also add a real time clock module. That would take negligible power.

would using the clock supplied by the gps, and setting up an if statement with modulo math work (or something else that gives me on the second recording)? The most important part, is that I get the multiple samples every second, on the second.

also, for obtaining information from the gps string... the data comes in as follows: $GPRMC,194509.000,A,4042.6142,N,07400.4168,W,2.03,221.11,160412,,,A*77

since everything is separated by commas, could you grab just the 194509.000 and the A message and assign them to a variable? if so, does it work like a matrix. Say GPS_Info=$GPRMC,194509.000,A,4042.6142,N,07400.4168,W,2.03,221.11,160412,,,A*77 would GPS_Info(2) return the 194509.000 reading?

thank you everyone...

I find your description of your time requirements rather confusing.

You say you need to take measurements 20 times per second - or one every 50millisecs. What is an acceptable range of error - perhaps 50 +/- 2msecs. (By the way there is no such thing as no error).

Then you say "The most important part, is that I get the multiple samples every second, on the second. " Again the question arises what error is acceptable for "on the second".

And there is an associated question - what happens if the specification for "on the second" conflicts with the sample interval?

There should be no trouble extracting the time from the GPS message (assuming that 194509.000 is some sort of time value).

I would be inclined to use millis() as your main timing device and periodically (perhaps once every 5 or 10 minutes) add or subtract a small interval (a bit like the leap-year correction to the calendar) to keep it in sync with the GPS time.

...R

Please post your whole sketch, then we can have a (w)holistic look at it to improve your timing.

Truth be told there really isn’t much to the sketch right now…

  #include <Adafruit_CC3000.h>
  #include <Adafruit_GPS.h>
  #include <SD.h>
  #include <SPI.h>
  #include <string.h>
  #include <SoftwareSerial.h>
  
  #define WLAN_SSID      "myNetwork"
  #define WLAN_PASS      "myPassword"
  #define WLAN_SECURITY  WLAN_SEC_WPA2
  // Security can be the following; 
  // WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA, WLAN_SEC_WPA2
  
  
  void setup()
  {
    Serial.begin(9600);
    delay(2000);
    Serial.println("Hello World");
    delay(1000);
    Serial.print("Initializing SD Card.  Please Wait...");
    
    pinMode(10, OUTPUT);
    
    if (!SD.begin()) {
      Serial.println("Card Failed, or not present...");
      return;
    }
    Serial.println("Card was found");  
  }

  void loop()
  {
    unsigned long TIME=millis()
    if TIME%50==0{
      int ref=analogRead(14);
      float Vin=(1.1/ref)*1023;
      float sensorU=(Vin/1023)*analogRead(0)*12-30;
      float sensorV=(Vin/1023)*analogRead(1)*12-30;
      float sensorW=(Vin/1023)*analogRead(2)*12-30;
      float sensorQ=(Vin/1023)*analogRead(3)*.5*1000;
    
      File dataFile = SD.open("datalog.txt",FILE_WRITE);
      if (dataFile) {
        dataFile.print(TIME);
        dataFile.print(",");
        dataFile.print(sensorU);
        dataFile.print(",");
        dataFile.print(sensorV);
        dataFile.print(",");
        dataFile.print(sensorW);
        dataFile.print(",");
        dataFile.println(sensorQ);
        dataFile.close();
      }
    }
  }

since I’m looking for a 20 hz sampling rate (a reading every 50 millis), if i could have an error of, at most 5 millis, I would be happy with that.

I would like a way to set millis()=0 using the gps. For example, I start it at 11:45 28.143. I would like for the arduino to wait until 11:50 to start collecting data. Since millis is an internal function, I dont believe I can overwrite it.

It is good that the good is not complex, makes it easier too review !

There are a few remarks to be made 1) millis() % 50 == 0 - Very dangerous statement as millis() can skip values under special conditions AND - if your code just happens to check 1 millis too late, it skips a meaurement

2) It might be wise to remove all floating point operations and store the raw data instead. floating point operations are emulated on Arduino and costs substantial time.

removing the floats could look like:

#include <Adafruit_CC3000.h>
#include <Adafruit_GPS.h>
#include <SD.h>
#include <SPI.h>
#include <string.h>
#include <SoftwareSerial.h>

#define WLAN_SSID      "myNetwork"
#define WLAN_PASS      "myPassword"
#define WLAN_SECURITY  WLAN_SEC_WPA2
// Security can be the following; 
// WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA, WLAN_SEC_WPA2

unsigned long lastTime = 0;
const unsigned long interval = 20;

long Vin;  // <<<<<<<<<<< made global

void setup()
{
  Serial.begin(9600);
  delay(2000);
  Serial.println("Hello World");
  delay(1000);
  Serial.print("Initializing SD Card.  Please Wait...");

  pinMode(10, OUTPUT);

  if (!SD.begin()) 
  {
    Serial.println("Card Failed, or not present...");

    while(1);  // blocks the script. return continues with loop!
  }
  Serial.println("Card was found"); 

  int ref = analogRead(14);
  Vin = 1125300 / ref ;             // Vin in millisVolts !  (iso floating points)  calculate only once..
}

void loop()
{
  unsigned long time = millis();

  if (time - lastTime >= interval) // by comparing you will never miss a read and subtraction is far faster than % operation
  {
    lastTime = time; // remember
    
    long sensorU = (Vin * 12 * analogRead(0))/1023000 - 30;
    long sensorV = (Vin * 12 * analogRead(1))/1023000 - 30;
    long sensorW = (Vin * 12 * analogRead(2))/1023000 - 30;
    long sensorQ = Vin * analogRead(3)* 500000;

    File dataFile = SD.open("datalog.txt", FILE_WRITE);
    if (dataFile) 
    {
      dataFile.print(time);
      dataFile.print(",");
      dataFile.print(sensorU);
      dataFile.print(",");
      dataFile.print(sensorV);
      dataFile.print(",");
      dataFile.print(sensorW);
      dataFile.print(",");
      dataFile.println(sensorQ);
      
      dataFile.close();
    }
  }
}

// code not tested, might contain some errors but you should get the idea

thanks for the reply… i’ve been messing with it but i have ran into a different snag…

my sketch seems to not be creating my data log file… here is the code…

#include <SPI.h>
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <SD.h>
#include <string.h>
#include <Adafruit_CC3000.h>
#include <ccspi.h>
//SoftwareSerial mySerial(8, 7);
//Adafruit_GPS GPS(&mySerial);
//#define chipSelect 10
int interval = 100;
unsigned long previousMillis=0;
unsigned long time=0;
File logfile;
long Vin;
void setup(){
  Serial.begin(9600);
  delay(1000);
  Serial.print("Finding the SD Card....");
  pinMode(10,OUTPUT);
  delay(1000);
  if (!SD.begin(10)) {
    Serial.println("Couldn't find it..... Sorry.");
    //return;
  }
  Serial.println("FOUNTD IT!!!!  Lets start recording.");
  File dataFile=SD.open("testfortime1",FILE_WRITE);
  if (dataFile){
    dataFile.print("TIME");
    dataFile.print(",");
    dataFile.println("SENSOR Q");
    dataFile.close();
  }
  int ref = analogRead(14);
  Vin = 1125300 / ref ; 
}
void loop() {
  unsigned long currentMillis = millis();
  if ((unsigned long)(currentMillis-previousMillis)>=interval) {
    long sensorQ = Vin * analogRead(0)* 500000;
    File dataFile=SD.open("testfortime1",FILE_WRITE);
    if(dataFile){
      dataFile.print(currentMillis);
      dataFile.print(",");
      dataFile.println(sensorQ);
      dataFile.close();
    }
    //Serial.print(time);
    //Serial.print(",");
    //Serial.println(currentMillis);
    previousMillis=currentMillis;
    //time=time+100;
  }
}

sorry for all the trouble and thanks for all the help

What is the message printed?

small code tip

long sensorQ = Vin * analogRead(0)* 500000;
==>
long sensorQ = Vin * analogRead(0)* 500000L;  // add L to indicate 32 bit long  -- use UL for Unsigned Long

Hello, Figured out the issue.... file name was too large and forgot the .txt....

but I started to get a weird reading on my analog input (negative numbers....) so i decided to go back to some basics... i am running the following code

int ref;
float Vin;

void setup(){
  Serial.begin(9600);
  delay(1000);
}
void loop(){
  ref=analogRead(14);
  Vin=1.1*1023/ref;
  Serial.print(Vin);
  Serial.print(",");
  Serial.println(ref);
  delay(100);
}

when pull up the Serial Monitor I recieve the following Volts=~3.25-3.27 converted int =~ 346-344

from my understanding, should i not be getting around 5 volts???

ref = analogRead(14) reads Analog pin 0 - that should give you a value between 0 and 1023

if you connect GND to A0 ==> you should read 0 if you connect 5V to A0 ==> you should get 1023

The right formula is

Voltage = 5.0 * ref / 1023; // there is always discussion if this should be 1023 or 1024, in practice the diff is less than the noise

Only if you set the - http://arduino.cc/en/Reference/AnalogReference - to 1.1 volt the formula should be

Voltage = 1.1 * ref / 1023;

in terms of code // use spaces and empty lines to improve readability

int ref;
float Vin;

void setup()
{
  Serial.begin(9600);  // you can use 115200 12x faster!
  delay(1000);
}
void loop()
{
  ref = analogRead(14);   // or A0
  Vin = 5.0 * ref / 1023;

  Serial.print(Vin);
  Serial.print(",");
  Serial.println(ref);

  delay(100);
}

my sketch seems to not be creating my data log file.... here is the code...

My view on SD logging... http://forum.arduino.cc/index.php?topic=232233.msg1678240#msg1678240

My approach is simple, if SD offends you, cut it off! Build or buy commercial, critical timing code and SD in same uC will drive non-professional programmers bananas... Do not do it.

Ray

well i have figured somethings out and have what i want working… however, my time jumps every once and a while… does anything jump out that screams dont do that, that slows everything down???

#include <Adafruit_GPS.h>  //Call Libraries
#include <SoftwareSerial.h>
#include<SPI.h>
#include<SD.h>

SoftwareSerial mySerial(8, 7);  //Set pins on GPS unit
Adafruit_GPS GPS(&mySerial);

File logfile;
uint32_t timer = millis();
int n=0;
void setup()  
{
  Serial.begin(115200);
  Serial.println("Hello. Let' start the test.");
  GPS.begin(9600);
  pinMode(10,OUTPUT);
  if(!SD.begin(10)){
    Serial.println("SD card not found... Ending program.");
    return;
  }
  
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);   
  GPS.sendCommand(PGCMD_ANTENNA);
  delay(1000);
  mySerial.println(PMTK_Q_RELEASE);
  while(n==0){
    char c = GPS.read();
    if (GPS.newNMEAreceived()) {  
      if (!GPS.parse(GPS.lastNMEA()))
        n=0;
    }
    if (millis() - timer > 2000) { 
      timer = millis(); // reset the timer
      if(GPS.hour!=0 && GPS.day!=0){
        Serial.println("Let us get started...");
        File dataFile=SD.open("Log02.txt",FILE_WRITE);
        if(dataFile){
          dataFile.print("\nTime: ");
          dataFile.print(GPS.hour, DEC); dataFile.print(':');
          dataFile.print(GPS.minute, DEC); dataFile.print(':');
          dataFile.print(GPS.seconds, DEC); dataFile.print('.');
          dataFile.println(GPS.milliseconds);
          dataFile.print("Date: ");
          dataFile.print(GPS.day, DEC); dataFile.print('/');
          dataFile.print(GPS.month, DEC); dataFile.print("/20");
          dataFile.println(GPS.year, DEC);
          dataFile.println("Reading,Time,Sensor1,Sensor2,Sensor3,Sensor4");
          dataFile.close();
          Serial.println("Begin");
          n=5;
        }
      }
    }
  }
}
int S1=0;
int S2=0;
int S3=0;
int S4=0;
unsigned int reading=0;
void loop(){
  if(n==5){
   if(millis()-timer>=50){
     timer=millis();
     S1=analogRead(0);
     S2=analogRead(1);
     S3=analogRead(2);
     S4=analogRead(3);
     reading=reading+1;
     File dataFile=SD.open("Log02.txt",FILE_WRITE);
     if(dataFile){
     dataFile.print(reading);
     dataFile.print(",");
     dataFile.print(timer);
     dataFile.print(",");
     dataFile.print(S1);
     dataFile.print(",");
     dataFile.print(S2);
     dataFile.print(",");
     dataFile.print(S3);
     dataFile.print(",");
     dataFile.println(S4);
     dataFile.close();}
    }
  }
}

Since you are using the Adafruit GPS object, you may want to 'lean" the lib a bit since you are not concerned about LAT/LON but just time stamping.

I have done this in my GLCD clock if you want to use that, or roll your own solution.

or
http://www.hackster.io/rayburne/5-billion-arduino-gps-clock-for-25

My code changes to the Adafruit lib (snippet) :

/*
See rights and use declaration in License.h
This library has been modified from original by Ray Burnette 12/2013
*/
#include "Adafruit_GPS.h"

// how long are max NMEA lines to parse?
#define MAXLINELENGTH 120

// we double buffer: read one line in and leave one for the main program
volatile char line1[MAXLINELENGTH];
volatile char line2[MAXLINELENGTH];
// our index into filling the current line
volatile uint8_t lineidx=0;
// pointers to the double buffers
volatile char *currentline;
volatile char *lastline;
volatile boolean recvdflag;
volatile boolean inStandbyMode;
volatile boolean ColdGPS = true ;


boolean Adafruit_GPS::parse(char *nmea) {
  // do checksum check
  // first look if we even have one
  if (nmea[strlen(nmea)-4] == '*') {
    uint16_t sum = parseHex(nmea[strlen(nmea)-3]) * 16;
    sum += parseHex(nmea[strlen(nmea)-2]);
    
    // check checksum 
    for (uint8_t i=1; i < (strlen(nmea)-4); i++) {
      sum ^= nmea[i];
    }
    
    if (sum != 0) {
      // bad checksum :(
      //return false;
    }
  }


  if (strstr(nmea, "$GPRMC")) {
   // found RMC
    char *p = nmea;

    // get time
    p = strchr(p, ',')+1;
    float timef = atof(p);
    uint32_t time = timef;
    hour = time / 10000;
    minute = (time % 10000) / 100;
    seconds = (time % 100);

    milliseconds = fmod(timef, 1.0) * 1000;
    p = strchr(p, ',')+1;  // A/V?
    p = strchr(p, ',')+1;  // lat
    p = strchr(p, ',')+1;  // N/S?
    p = strchr(p, ',')+1;  // lon
    p = strchr(p, ',')+1;  // E/W?
    p = strchr(p, ',')+1;  // speed
    p = strchr(p, ',')+1;  // angle

    p = strchr(p, ',')+1;
    uint32_t fulldate = atof(p);
    day = fulldate / 10000;
    month = (fulldate % 10000) / 100;
    year = (fulldate % 100);
    // some older GPS modules only send GGA messages until full-GPS sync
    // so, only want to use the GGA sentences until RMC is available as
    // GGA only has time info and not date
    ColdGPS = false ;
    return true;
  }
  else if (ColdGPS && strstr(nmea, "$GPGGA")) {
    // found GGA
    char *p = nmea;
    // get time
    p = strchr(p, ',')+1;
    float timef = atof(p);
    uint32_t time = timef;
    hour = time / 10000;
    minute = (time % 10000) / 100;
    seconds = (time % 100);
    milliseconds = fmod(timef, 1.0) * 1000;
    return true;
  }
    return false;
}

char Adafruit_GPS::read(void) {
  char c = 0;
  
  if (paused) return c;

    if(!gpsSwSerial->available()) return c;
    c = gpsSwSerial->read();


  if (c == '\n' || c == '\r' || c == '

Because I use the Adafruit code unchanged for other projects, I left the library version unchanged and moved the .h and .cpp files into the sketch directory… Note the tabs on the Arduino GUI.

Ray

) {
    currentline[lineidx] = 0;
    lineidx = 0;
    recvdflag = true;

if (currentline == line1) {
      currentline = line2;
      lastline = line1;
    } else {
      currentline = line1;
      lastline = line2;
    }
  }

currentline[lineidx++] = c;
  if (lineidx >= MAXLINELENGTH)
    lineidx = MAXLINELENGTH-1;

return c;
}

Adafruit_GPS::Adafruit_GPS(SoftwareSerial *ser)
{
  common_init();    // Set everything to common state, then…
  gpsSwSerial = ser; // …override gpsSwSerial with value passed.
}

// Initialization code used by all constructor types
void Adafruit_GPS::common_init(void) {
  gpsSwSerial = NULL; // Set both to NULL, then override correct
  recvdflag  = false;
  paused      = false;
  lineidx    = 0;
  currentline = line1;
  lastline    = line2;
  hour = minute = seconds = year = month = day = fixquality = satellites = 0; // uint8_t
  milliseconds = 0; // uint16_t
}

void Adafruit_GPS::begin(uint16_t baud) {
  if(gpsSwSerial) gpsSwSerial->begin(baud);
  delay(10);
}

boolean Adafruit_GPS::newNMEAreceived(void) {
  return recvdflag;
}

void Adafruit_GPS::pause(boolean p) {
  paused = p;
}

char *Adafruit_GPS::lastNMEA(void) {
  recvdflag = false;
  return (char *)lastline;
}

// read a Hex value and return the decimal equivalent
uint8_t Adafruit_GPS::parseHex(char c) {
    if (c < ‘0’)
      return 0;
    if (c <= ‘9’)
      return c - ‘0’;
    if (c < ‘A’)
      return 0;
    if (c <= ‘F’)
      return (c - ‘A’)+10;
}

boolean Adafruit_GPS::waitForSentence(char *wait4me, uint8_t max) {
  char str[20];
  uint8_t i=0;
  while (i < max) {
    if (newNMEAreceived()) {
      char *nmea = lastNMEA();
      strncpy(str, nmea, 20);
      str[19] = 0;
      i++;

if (strstr(str, wait4me))
return true;
    }
  }

return false;
}


Because I use the Adafruit code unchanged for other projects, I left the library version unchanged and moved the .h and .cpp files into the sketch directory... Note the tabs on the Arduino GUI.

Ray

![GPS_Clock.jpg|761x604](upload://sVkB6nmtUUl2apitXLXyCgLureS.jpeg)

When you say you need accurate timing does that mean the intervals between your 20 Hz times have to be accurate or are do you mean the absolute time of the measurement (day, hour, min, sec) has to accurate? These are two different issues and need to be addresses separately.

Address the interval timing issue: You say you want the error/uncertainty small, you should do the analysis to see how small this has to be first and post this metric. Maybe a standard external crystal will be accurate enough to meet your needs or maybe you need a temperature compensated oscillator or something more accurate. Without knowledge of how accurate it has to be you can spend a lot of time and money coming up with a result that is two orders of magnitude better than what you needed. Possibly using the PPS signal from the GPS to zero the timer as a means of reducing drift would be enough.

The absolute timing issue: In this case I assume you are concerned about making sure your measurements where the time stamps compare very well with the time stamp on measurements collected from another instrument located in a different location. This case can be helped dramatically by the PPS signal from the GPS, I have used this when making acoustic measurements over an RF networked group of sensors.

Hope this helps,