Timestamp/Counter with settable reference/zero point

Hi all, first time poster so please be gentle! ;)

I'm currently designing my first Arduino project; it will be a combination dashboard/display and data logger for my drag car. It's all only in thin air and on paper yet, nothing has been constructed or coded (waiting for hardware to get here!).

This unit will basically monitor several inputs (engine RPM, driveshaft RPM, oil pressure, fuel pressure pre/post regulator, coolant temp etc etc), display them (probably on a 20x4 LCD untill I find my feet and get a glcd working), and also log this captured data to SD card for review later.

I'm semi-confident I can get the sensors in and scaled correctly (perhaps not the RPMs yet, need to look more into frequency measurement), and semi-confident I can get the display and some kind of writing to SD card working, by using parts of various example sketches.

One thing that has me puzzled, though, is whether there is a way to write a time value (similar to millis) to the data, with the zero point set by an event (ie. input going high or low). Basically, we use the captured data to analyse the car setup, track conditions and tune up info, but this has to be relevant to the run, so simply using millis (who's 0 is when the unit starts) won't work, I need a zero point set the instant the car leaves the starting line on the run. This is because the electronics will all be on and operating for fire up, burnout and staging procedures, but this data is not really needed. I need to be able to plot the graphs (mainly the RPMS and the fuel pressure) against time of the run, not time since the unit started. I have a trigger source (transbrake release - +12v until the transbrake is released and the car launches, where it goes to 0) but don't know how to reference this so in my data, i can see some kind of time reference from that 0 point to the end of the run (9 seconds). Needs to be actual time (not just samples, unless that can be accurately bought back to actual seconds), so when we get the time card for the run, we can see, for example at the 60ft point we reached it in 1.2 seconds into the run, we can look at the data at 1.2 seconds to see what the driveshaft vs engine RPM is. . to judge torque converter efficiency or whether the car spun the tyres etc.

Any help would be muchly appreciated!!

Cheers, Ryan.

need to look more into frequency measurement

Why? You count the number of times that some event occurred (a hall effect sensor was triggered, a LDR was triggered, a spark plug fired, etc) in some period of time, or you measure the time between events. In either case, it's a simple matter of counting and timing.

One thing that has me puzzled, though, is whether there is a way to write a time value (similar to millis) to the data, with the zero point set by an event (ie. input going high or low).

Record when the trigger event occurred, using millis(). Record when the event of interest occurs (like when to write), using millis(). The value of interest is the difference between the two events. Just like using a watch.

I need a zero point set the instant the car leaves the starting line on the run.

Determining when the car leaves the starting line will be the hardest part. What is there on the car that changes when it "leaves the starting line"?

I have a trigger source (transbrake release - +12v until the transbrake is released and the car launches, where it goes to 0)

Should have read ahead, I guess. OK, so you have a trigger event (although the Arduino can't be fed that 12V signal).

Needs to be actual time

Now, this disagrees with what you said earlier. You either want to measure everything with respect to some event (the launch) or you want to measure everything with respect to the earth's rotational point (also known as a clock). When you decide, let us know.

Save the time from millis() when the trigger event occurs. You can write a little routine like:

unsigned ttime(void)
{  return millis() - saved_time;
}

to provide milliseconds elapsed since your trigger event. Don't make a hard problem out of an easy one. :)

For additional precision, you could use micros() throughout instead of millis.

PaulS: Why? You count the number of times that some event occurred (a hall effect sensor was triggered, a LDR was triggered, a spark plug fired, etc) in some period of time, or you measure the time between events. In either case, it's a simple matter of counting and timing.

I'd be using the tacho output from the ignition (12v squarewave, 4 pulses per engine revolution) Engine RPM range is from 0 (well, idle about 1000rpm) up to 8000rpm. This gives me a signal from roughly 66hz up to 533hz. The run only lasts 9 seconds, so I need to sample and calculate RPM at a rate probably around once every 100ms? Any bigger interval than that and I won't get the resolution I need to see spikes and such (wheelspin, engine miss etc etc). What is the best way to sample/log a frequency in that range that quickly? On the hardware side I think I'd use an opto-isolator to keep that 12v signal away from the Arduino, and also so I don't put any artefacts on the tacho line (it also feeds a large conventional tacho in the car which will stay there for now).

Needs to be actual time

Now, this disagrees with what you said earlier. You either want to measure everything with respect to some event (the launch) or you want to measure everything with respect to the earth's rotational point (also known as a clock). When you decide, let us know.

Sorry, I should have been more clear. The time scale needs to be in terms of actual seconds, ie 1.242 seconds, 9.112 seconds. . it can't be an abstract scale such as 'samples' or a counter, as this will have no bearing to the times we crossed each timer on the run. . So I guess to say it another way, it needs to be time (as in seconds, and fractions of), but referenced from when the transbrake releases.

This is a quick output from another brand logger I've got in the car at the moment, this is the type of data I need to get out of this project;

Time , ENGINE RPM,DRIVE SHAFT, -0.5000 , 4979 ,7 , -0.4800 , 4979 ,7 , -0.4600 , 4978 ,7 , -0.4400 , 4979 ,7 , -0.4200 , 4982 ,7 , -0.4000 , 4984 ,7 , -0.3800 , 4985 ,7 , -0.3600 , 4987 ,7 , -0.3400 , 4989 ,7 , -0.3200 , 4991 ,6 , -0.3000 , 4991 ,6 , -0.2800 , 4993 ,6 , -0.2600 , 4994 ,6 , -0.2400 , 4997 ,6 , -0.2200 , 5002 ,6 , -0.2000 , 4998 ,6 , -0.1800 , 4998 ,6 , -0.1600 , 5002 ,6 , -0.1400 , 5003 ,6 , -0.1200 , 4997 ,6 , -0.1000 , 4995 ,6 , -0.0800 , 4994 ,6 , -0.0600 , 4995 ,5 , -0.0400 , 4997 ,5 , -0.0200 , 4997 ,5 , 0.0000 , 4992 ,5 , 0.0200 , 5021 ,5 , 0.0400 , 5118 ,5 , 0.0600 , 5243 ,146 , 0.0800 , 5354 ,473 , 0.1000 , 5454 ,510 , 0.1200 , 5520 ,547 , 0.1400 , 5583 ,571 , 0.1600 , 5643 ,535 , 0.1800 , 5699 ,415 , 0.2000 , 5753 ,294 , 0.2200 , 5791 ,406 , 0.2400 , 5820 ,477 , 0.2600 , 5842 ,518 , 0.2800 , 5856 ,579 , 0.3000 , 5861 ,638 , 0.3200 , 5873 ,701 , 0.3400 , 5885 ,828 , 0.3600 , 5885 ,1001 , 0.3800 , 5870 ,1173 , 0.4000 , 5855 ,1247 , 0.4200 , 5829 ,1305 , 0.4400 , 5809 ,1303 , 0.4600 , 5786 ,1262 , 0.4800 , 5752 ,1209 , 0.5000 , 5746 ,1143 , 0.5200 , 5759 ,1072 , 0.5400 , 5771 ,1135 , 0.5600 , 5783 ,1233 , 0.5800 , 5800 ,1257 , 0.6000 , 5810 ,1261 , 0.6200 , 5818 ,1308 , 0.6400 , 5813 ,1361 , 0.6600 , 5800 ,1410 , 0.6800 , 5785 ,1458 , 0.7000 , 5763 ,1529 , 0.7200 , 5764 ,1617 , 0.7400 , 5766 ,1665 , 0.7600 , 5757 ,1664 , 0.7800 , 5734 ,1630 , 0.8000 , 5717 ,1646 , 0.8200 , 5696 ,1706 , 0.8400 , 5659 ,1732 , 0.8600 , 5614 ,1753 , 0.8800 , 5574 ,1799 , 0.9000 , 5549 ,1815 , 0.9200 , 5565 ,1864 , 0.9400 , 5594 ,1896 , 0.9600 , 5622 ,1921 , 0.9800 , 5638 ,1950 , 1.0000 , 5632 ,1965 , 1.0200 , 5645 ,2010 , 1.0400 , 5662 ,2056 , 1.0600 , 5683 ,2092 , 1.0800 , 5710 ,2112 , 1.1000 , 5731 ,2121 , 1.1200 , 5743 ,2150 , 1.1400 , 5750 ,2186 , 1.1600 , 5758 ,2218 , 1.1800 , 5770 ,2243 , 1.2000 , 5777 ,2259 , 1.2200 , 5791 ,2285 , 1.2400 , 5802 ,2312 ,

This gives me a signal from roughly 66hz up to 533hz.

8000 RPM * 4 pulses per rev = 32000 pulses/minute = 533 pulses/second. You should have no problem using an interrupt to count pulses.

In loop(), you simply keep track of time, and every so often (however often you feel appropriate), you divide the number of pulses received (divided by 4) by the time you've been counting to get revolutions per time period, and reset the clock and the count. You can then adjust that value to RPS or RPM. The hardest part is probably going to be actually getting the data to the SD card as fast as you can collect it.

This is a quick output from another brand logger I've got in the car at the moment, this is the type of data I need to get out of this project;

Time , ENGINE RPM,DRIVE SHAFT, -0.5000 , 4979 ,7 , -0.4800 , 4979 ,7 , -0.4600 , 4978 ,7 , -0.4400 , 4979 ,7 ,

How do you get negative time?

PaulS: How do you get negative time?

Out of the logger, you don't. This data has already passed through the other brand logger's PC software, where you manually set its zero point. You can't see the raw data from it, but when you first do a download (before you set the zero), the zero is the start of the file (when the logger gets turned on). Once you set a zero point in the software, 'negative time' is everything in the data before the zero point.

On another point, what are some good data display/graphing packages that would suit this data? I'm currently working with LiveGraph which seems OK, but will be a bit fiddly if I want to overlay one run over another to compare differences.

Here’s one approach to the problem. I’ve set up two interrupts, one to field the start signal and another to deal with the four pulses per revolution. At each sensor pulse it produces an RPM value and the time (in microseconds) into the run. My guess is that you’ll want to update your display at a lower frequency, and that you’ll want to divide the time down to tenths of a second.

#define START /* TBD */                /* Interrupt number for start signal   */
#define SHAFT /* TBD */                /* Interrupt number for shaft sensor   */
/*----------------------------------------------------------------------------*/
/* Extend micros() for 64-bit up-time (TU scope only)                         */
/*----------------------------------------------------------------------------*/
unsigned long long prev = 0;
unsigned long long start = 0;
unsigned long long data_time;
unsigned char new_data = 0;
unsigned char running = 0;
unsigned data_rpm;
/*----------------------------------------------------------------------------*/
/* Return 64-bit time in microseconds                                         */
/*----------------------------------------------------------------------------*/
static unsigned long long us64(void)
{  static union                        /* tl and ts[] occupy same location    */
   {  unsigned long long tl;           /* tl is 64-bit time in usec           */
      unsigned long ts[2];             /* ts[0] is bits 0-31, ts[1] is 32-63  */
   }  u = { 0 };                       /* initialize to count from RESET time */
   unsigned long t = micros();         /* 32-bit time from micros()           */
   prev = tl;                          /* Save the previous time for delta    */
   if (t < u.ts[0]) ++u.ts[1];         /* If micros() has rolled over, adjust */
   u.ts[0] = t;                        /* Update with new micros() value      */
   return u.tl;                        /* Return 64-bit uptime                */
}                                      /*  end: us64()                        */
/*----------------------------------------------------------------------------*/
/* Start of run interrupt processing (only executed once)                     */
/*----------------------------------------------------------------------------*/
void start_isr(void)
{  start = us64();
   running = ~0;
   detachInterrupt(START);
}
/*----------------------------------------------------------------------------*/
/* Shaft sensor interrupt processing                                          */
/*----------------------------------------------------------------------------*/
void shaft_isr(void)
{  static unsigned long long now,dt;
   now = us(64);
   data_time = now - start;
   dt = now - prev;
   data_rpm = (15000000 + dt/2) / dt;  /* Division with rounding for RPM      */
   new_data = ~0;
}
/*----------------------------------------------------------------------------*/
/* setup() code to support the tachometer                                     */
/*----------------------------------------------------------------------------*/
void setup(void)
{
   attachInterrupt(SHAFT,shaft_isr,RISING); /* Assumes positive-going signal  */
   attachInterrupt(START,start_isr,RISING); /* Assumes positive-going pulse   */
   us64();                             /* To ensure prev variable is primed   */
}
/*----------------------------------------------------------------------------*/
/* loop() code to support the digital tachometer                              */
/*----------------------------------------------------------------------------*/
void loop(void)
{  
   unsigned rpm
   unsigned long long time;
   
   if (new_data)
   {  rpm = data_rpm;                  /* Most current RPM reading            */
      time = data_time;                /* Microseconds since start of run     */
      new_data = 0;                    /* Reset new data flag                 */
      display_RPM(time,rpm);           /* Cockpit display                     */
      if (running)                     /* Only record data after START        */
      {  /* TBD */                     /* Record the data (quickly!) */
      }
   }

}