Here is an update on my latest version of an onboard IR-based lap timer, suitable for cars, karts, motorcycles, bikes, etc. This one has no display, and simply records individual lap times for later downloading to a PC. It utilizes a SparkFun uLog data logger and a 38KHz IR receiver (like a GP1UX311QS) and can be powered from a 110mAh LiPo battery. The result is a very compact unit.
µLogTimer with 110mAH LiPo Battery in the palm of my hand highlighting its small size.
Note in the picture above, the IR detector receives its power supply from the uLog and is simply soldered into the 3.3V, GND, and X pin holes (per the datasheet, a more robust installation would apply a 0.1uf cap between VCC/GND and 100ohm resistor in the VCC line, but hey…).
Functionality
The uLog with 2M of dataflash memory has plenty of storage space (2M translates into potentially 540,672 laps of storage). The µLogTimer will require a device like the FTDI Friend to download lap times. The communication protocol is 8N1 with no handshake at 38,400 baud. The µLogTimer responds to 3 simple commands:
- ‘e’ – Erase lap time memory.
- ‘d’ – Download lap times.
- ‘a’ – Download the entire dataflash memory contents.
This example uses Termite RS-232, however any terminal program would be suitable.
Trackside Beacon/Transmitter
The µLogTimer resides in or on the vehicle and responds to a stationary AIM trackside beacon. The beacon continuously transmits a coded IR pulse which the µLogTimer receives and uses to define the start/finish line. The µLogTimer simply needs to be positioned such that the IR detector has an unobstructed view of the transmitter. AIM beacons are prolific and it’s hard to imagine there not being one at any well-attended track event. However, see below how easy it is to convert an adafruit TV-B-Gone Kit into a beacon transmitter.
AIM Beacon Transmitter
Source Code
It was a challenge to fit all of the features into the memory confines of the ATtiny24 onboard the uLog, so the program is not rich in features. I will admit to some “oddness” in the code in order to reduce its size. The program is built upon the confusing tangle of outdated and very poorly commented original uLog source code available for download on the uLog webpage.
The µLogTimer program is available here.
Accuracy
Since the uLog ATtiny24 is running on the internal 8MHz RC, the timer accuracy will vary by chip and ambient temperature. The internal RC can be calibrated to improve accuracy by comparison to a valid external source. I have performed this calibration using a 32KHz DS3231 TXCO (ChronoDot), see this forum post for details.
adafruit TV-B-Gone as Transmitter
If an AIM transmitter beacon is not available, or if one wants to run this system on a “private coded” IR signal which prevents interference with others, you could easily make your own transmitter. One solution is to modify the program on an adafruit TV-B-Gone. An example of a private IR beacon code is included in the source code.
adafruit TV-B-Gone
Here is the modified ATtiny85 TV-B-Gone program:
Code: Select all
/*
AIM Lap Trigger Beacon using TV-B-Gone Hardware
Portions (c) by:
TV-B-Gone Firmware version 1.2
for use with ATtiny85v and v1.2 hardware
(c) Mitch Altman + Limor Fried 2009
Last edits, August 16 2009
With some code from:
Kevin Timmerman & Damien Good 7-Dec-07
Distributed under Creative Commons 2.5 -- Attib & Share Alike
*/
#include <avr/io.h>
// What pins do what
#define LED PB2
#define IRLED PB0
// Shortcut to insert single, non-optimized-out nop
#define NOP __asm__ __volatile__ ("nop")
#define freq_to_timerval(x) ((F_CPU/x - 1)/2)
//for 8MHz we want to delay 8 cycles per microsecond
//this code is tweaked to give about that amount
void delay_us(uint16_t us) {
while (us != 0) {
NOP;
NOP;
us--;
}
}
//This function is the 'workhorse' of transmitting IR codes.
void xmitCodeElement(uint16_t ontime, uint16_t offtime) {
//start outputting the carrier frequency on OC0A, PB0, pin 5
TCNT0 = 0; //reset the timers so they are aligned
TIFR = 0; //clean out the timer flags
//timer0 mode #2 CTC, with OCRA as TOP, toggling OC0A(PB0) on compare match, no prescaling
TCCR0A =_BV(COM0A0) | _BV(WGM01);
TCCR0B = _BV(CS00);
delay_us(ontime);
//turn off timer0
TCCR0A = 0;
TCCR0B = 0;
//make sure that the IR LED is off since timer may have stopped while the LED is on
PORTB |= _BV(IRLED);
delay_us(offtime);
}
int main(void) {
uint16_t flash;
DDRB = _BV(LED) | _BV(IRLED); //set the visible and IR LED pins to outputs
PORTB = _BV(LED) | _BV(IRLED); //LEDs are off when pins are high
OCR0A = (uint8_t)freq_to_timerval(38000); //value for 38kHz
TCCR0A = 0; //stop timer0
TCCR0B = 0;
flash = 0;
while(1) {
flash++;
if (flash == 2000)
PORTB &= ~_BV(LED); //turn on visible LED at PB0 by pulling pin to ground
//measured AIM beacon pattern [inverted by PNA4602M]:
//high 6ms/low 624us/high 1.2ms/low 624us/high 1.2ms/low 624us [repeat]
//Alternative Private Beacon Code:
//300us ON / 1200us OFF / 300us ON / 1200us OFF / 300us ON / 6000us OFF
//900us ON/9300us = 9.7% duty time
xmitCodeElement(622, 1195); //timing tweaked via AVR Studio Stopwatch
xmitCodeElement(622, 1195);
xmitCodeElement(622, 5994); //IRLED on 1.872ms/off 8.4ms = 18.2% on time
if (flash == 2005) {
PORTB |= _BV(LED); //turn off visible LED
flash = 0;
}
}
}
---
Oddly, Sparkfun spells uLog or "micro log" with a u, when I think they really meant to use µ. I followed SparkFun's convention throughout, however, it's really a µLog.