Arduino suitablility for a project

Hi,

Im hoping someone can help me with some advice around the suitability of an Arduino for a project, and potentially some assistance with some design decisions.

Background
Our slotcar club presently uses an old PC (386 sx40), running DOS, with a compiled QBASIC app for lap counting.

There is a photo interrupter embedded in each slot, which the guide on the slot car cuts to count a lap. This then interfaces with some circuitry into the computer via a physically wired keyboard (old AT connector and the wires are soldered to the back of the individual key microswitches). The keyboard then sends key-presses to the s/w to create lap timestamps.

Issues with Current Setup
The PC needs to be replaced, as its getting very tired. The keyboard interface causes us issues as well, due to it being hard-wired and using an AT interface (rather than PS2 or USB). This makes it hard to replace either the keyboard or the PC due to old AT connector.

The main problem with the current system, hardware age and replace-ability issues aside, is just not accurate enough. A good driver can turn consistent laps <= 1/10th of a second, with total laptimes of ~ 3.8 to 6 seconds. We get a LOT of groupings where the computer reports identical lap-times for a driver, and we often get deadheats in qualifying where we need to count back 3 or 4 laptimes to find qualifying placings.

Apparently as a stepping point, I should be able to hack a USB keyboard and wire into the matrix. This would at least take me partially there in terms of resolving the hardware supportability issues.

I initially looked into using Phidgets as an option for providing USB connectivity, but it appears the combination of the digital IO and USB will only provide a resolution of about 8ms. This is probably far better than what the present system gives (research suggests 55ms precision due to software clock in DOS), but if Im going to go to the effort of writing new s/w and a new input system, then I want to do it properly!!

So;

Requirements

  • Support 4 lanes
  • Accurate to 1 millisecond (0.001 sec)
  • Interface with a PC-based race-control system (to be written)
  • New lap-time data needs to arrive in PC s/w with-in 0.5 seconds (preferably < 0.25 sec update times)
  • Able to cope with doing the above for 4 lanes, where lap-times >= 3 secs
  • Controlling things such as turning off the track power at race end

Thoughts / Questions
Im thinking that the PC should be the primary time keeper / controller, and the Arduino should effectively just send timestamps to the PC for new laps, and get instructions back such as ‘turn off power’ (possibly with a second board) etc. Some of this does appear to be a bit of a pain with the serial comms from PC to Arduino.

From what Ive researched so far, I think the Arduino should handle what I want to do, but it seems that the best way to do it would be with interrupts. Since the Uno only has two external interrupts, Im thinking that that would suggest that the Mega would be the right option as it has 6 external interrupts.

What Im concerned about is whether the serial comms will be able to keep up with the dataflows while the lap counter interrupts are going on. The protocol would have to be quite robust to allow it to be interruped and restarted all the time due to the counter interrupts.

Hoping someone can provide some input, thoughts, and direction around this?

Cheers,
Rob.

This sounds like a rather easy and fun project.

Take any Arduino and connect the current measurement gate hardware. This will take some tinkering, but probable is pretty straight forward. If needed, you can make your own measure points too.

For the upload, send the data via USB (as a Serial device) to PC. If this is a dedicated PC, consider using Linux, but Windows is just as fine. You just need to write a program that reads from the correct virtual com port and does with the data whatever necessary. I would go with a simple protocol where per measurement a text string is sent, something like:

lane;lap;time (eg: 1;25;10.354)

For the fun part - if you don't already have it - connect a few 7 segment LED displays to show laps and or lap times and 1 or 5 buttons to reset the lap counter (either just one global or one for each lane one) That way, the thing can be used without PC.

All in all, you will need is an Arduiono Duemilanove or Uno or compatible with an USB on-board, a hand full of cheap components (some 74HC595 or serial 7 segment led drivers if you add a display) and some time to tinker. I would budget some ?50 for the hardware including the Arduino.

Korman

This is an interesting toy... Even if I won't have the pleasure to work on it, I'll throw in some ideas hoping to say something useful...

  • pc sends a "start race" command to the arduino, telling it how long it will last (number of laps or max running time) and which lanes will participate;
  • ardunio turns on the power
  • in "race" mode the arduino doesn't communicate with the pc, it just executes a tight loop where polling for the 4 lanes occurs. Lap counts and timings (possibly via micros()) are kept in memory;
  • at the end of the race, the arduino turns off the power and sends race stats to the pc, which then stores them into the race system software.

Not having serial communication during the lap could increase the time resolution and avoid the burden of developing a fast and robust protocol.

As for int vs. polling, I'd use a hybrid solution: an interrupt tied to each lane; each isr just increases a volatile variable which keeps count of total laps; during race, a tight loop keeps watching those lap counts to see if they increase; when that occurs, the current value of micros() is stored into an array; at the end of the race, the four arrays are transmitted to the pc, which then reconstructs the race history.

As I said, just my 2 cents.

HTH

You should check this:

http://techcobweb.wordpress.com/2009/09/02/slot-car-challenge/

If you google you can find other solutions as well, not all can live up to your specs, but they should be able to provide you with inspiration and a good starting point.

As Mromani pointed out the weakest link in the chain could well be the time it takes to transmit data to a PC over serial. Normally this is not a problem, but if you need 1/100th sec. resolution it is probably better to let arduino handle the timing and send the data to the PC which will do the "main housekeeping"

mromani,

µs timing for this application is probably overdoing it, but even if you want the resolution of 4µs, there’s no reason to stop sending out data on the serial port. You just need to write the program properly and use the available facilities of the system. For the resolution of the order of 1 ms, you can even get away with a rather simplistic programming approach.

Korman

My suggestion of not sending data to the PC in real time came from thinking about this scenario:

event 1: car A passes through its sensor
event 2: car B passes through its sensor
event 1 and event 2 are < 1ms away.

If you notify PC about event 1 as soon as it occurs, you’ll lose precious ms, thus missing event 2. Using interrupts you’ll count event 2 anyway, but you’ll report its time with some ms error.
All of this gets worse when you have 4 events all very close to each other.

Serial transmission is “slow”, see here:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1284458471/0#0

If you notify PC about event 1 as soon as it occurs, you'll lose precious ms, thus missing event 2. Using interrupts you'll count event 2 anyway, but you'll report its time with some ms error.

This is only a problem when you don't bother writing your program properly and just cobble together random fragments found on the internet with proper understanding what those things do. While interrupt collisions might occur, they shouldn't introduce an error in excess of 8µs, which are in this context irrelevant.

Also, considering that at 38400 bps, a character takes about 0.27 ms to put on the line. If you keep your messages short (3 bytes should do in that case), you can even stay below the error margin when you just do a simple poll and send.

Serial transmission is "slow", see here:

Use the Source, Luke! Sure, Serial.write waits until the previous character was sent before putting the next out, but that doesn't mean your program needs to waste its time there. The delay is here for people who don't care because the delay is of no relevance to their application. If you care, you just make sure you don't wait longer than you can afford. That's the beauty of the Arduino, you can use the simple way as long as it's good enough, but you have alternatives available when the simple way isn't good enough any more.

Korman

Guys,

Thanks for all the replies. It shows you have had a good think about it for me, and thats appreciated! You have also come up with a number of the options I considered too, which shows Im probably on the right track.

Just as a side note, Im very new to all this microprocessor stuff (probably shows :) ) but my old man was into HAM radio so I have a little bit of electronics understanding, plus lots of background in perl coding (plus dribs/drabs of other languages like ObjC+Cocoa and Java).

MrOmani - Ive spotted that blog entry previously (Ive spent a number of weeks researching prior to making this post). It looks useful, but not to the level of what I need to achieve. I did have a think about only updating the laptimes at the end, but we need real-time (or near) in some situations such as qualifying (laptimes are called to drivers on each lap). But potentially I COULD cater for that with a LCD screen.

Ideally though, I really want the data in the computer ASAP :)

Any thoughts regards Uno versus Mega (I'll get 'proper' Arduinos) as Id like to order today if possible so can play on weekend :)

My thoughts; 1. For that serial comms format, Id be thinking something like; sLane(int);lap(int);time(long)e as I need to cater for a laptime longer than 65550 millis (max for int). eg s1;25;123500s

By my calcs, that will typically be around 13 bytes of ascii, and thats more than 1ms of serial transfer time at 57600 (or is there a serial send buffer as well which I could fill more quickly ??)

If im using arrays of longs to store laptimes, then 2k on the UNO is only going to give me a max of 512 laps (eg ~125 laps per lane) and thats not enough to cater for some races.

(I also looked into using microsd cards, but it looks like they have horriffic latency for file writes).

I'll talk about the various use-cases a little later to provide some more background, but some of the race types we have to deal with are enduros, which typically are 12 hours, and have a time between lane-changes of 1 or 2 hours. We typically do up to 800 laps per hour, per lane, in an enduro, so I dont think even the Mega will have sufficient RAM to cope with storing all the laptimes until the end of the race.

  1. ISRs or tight loop? If ISR's, then the only way I could see an UNO doing this is to have ALL the lanes fire the same ISR (or 2 lanes per ISR) and then poll the state of all the lane inputs in the ISR (which I understand is quite a slow process). UNO has 2 external ISR's, Mega has 6.

Korman. Thanks for all your thoughts. I had a rat around under the track last night, and its looking better and better. The guy who did the last setup has helpfully used DB25's for connecting the lanes to the circuitry, and then has an output for a blinky light box, as well as the keyboard. What that means is that I should be able to run my system in tandem with the current system during testing (and use it to do initial data captures for testing under race conditions), and those are all connected up with DB9s :D

Additionally, it looks like they have already done a hardware based de-bounce circuit as theres a board with some caps + resisters and 555 chips plus another chip which I forget the number of.

That board then feeds into another board which feeds the keyboard (presumably does the logic to provide correct voltage / phasing to the keyboard switches).

So yeah, all looking promising on the hardware side, I should be able to make up my own input circuitry for testing (which is what I had intended), and swap and match bits to suit AND test alongside the production system :)

If your input data is fed into a circular buffer and the output to the PC reads from that buffer, everything interrupt driven (or one half is a tight loop), then as long as the overall throughput can be handled by the serial PC link you have no problems.

The 2000 bytes of RAM shouldn't be an issue then.

You can use pin-change interrupts, the ISR does nothing but store the new state of the pins in a small circular buffer (say 256 bytes) with a time stamp, increments the buffer's write pointer, then exits.

Meanwhile a tight loop checks for buffer_write_ptr != buffer_read_ptr which indicates that there is data in the buffer, it grabs the data one byte at a time and transmits it. The transmission has no tangable affect on the data capture as the main loop (and presemably the serial output) do not block interrupts.

Same data format as suggested above but the "lane" byte is a bit field to allow simultaneous events.


Rob

As Rob above wrote, you just buffer the data for a while and make sure no measure is lost. If I understood it correctly, at worst you have 4 simultaneous lap times and then at least one second before the first intermediate time measure can occur. That's long enough to send out all data over the serial port at 9600 bps in your proposed format.

The Arduino Uno is good enough for this, there no real need to a Mega.

Do you also have a display board and starter lamps installed and how are those driven?

Korman

Thanks guys. With a lot of googling and reading based on your comments, you've just filled in the 'blank spots' that I was mentally struggling with.

@GrayNomad:

You can use pin-change interrupts, the ISR does nothing but store the new state of the pins in a small circular buffer (say 256 bytes) with a time stamp, increments the buffer's write pointer, then exits.

Perfect, I was under the impression you could only fire ISRs from the pins that were designated as such. I do suspect that the Mega would make my job easier, as it looks quite a bit more complex to do Pin Change Interrupts (to this noob anyway), but Im sure I can figure it out with enough time and grey matter.

This does, however, also help with a longer-term goal of making this s/w and h/w usable with 8 lane tracks. My intention, if it all goes well, is to make it available as OS, as clubs and individuals are always looking for cheap/accurate options for lap counting & timing / race-control.

I couldnt find a lot of stuff about setting up a circular buffer with an Arduino. Would this be something that is just a standard C routine that I can research?

Same data format as suggested above but the "lane" byte is a bit field to allow simultaneous events.

Im not 100% sure of the reasoning behind this? As far as I can gather, a bit-field is effectively a group of 0/1's which designate state. The only tie-in I can think of is that the in the ISR there is a lower-level way of accessing the pin states, which is done via a bit-field.

@Korman:

... you just buffer the data for a while and make sure no measure is lost. If I understood it correctly, at worst you have 4 simultaneous lap times and then at least one second before the first intermediate time measure can occur...

Yup, at worst 4 simultaneous lap counts, which would then potentially occur typically ~4.5 - 6 sec later.

The more likely scenario is that you will get the cars crossing in fairly rapid sequence. EG 4.601s, 4.691s, 4.723s, 5.941s (last guy fell off in that lap hehehe). This will typically happen for the first few laps of the race, and is also the scenario that Im most concerned will mess up the serial comms.

Do you also have a display board and starter lamps installed and how are those driven?

Race control lights, and track power are manually controlled at the moment by the race controller. The start switch electrically somehow turns off the start light, and also signals the computer to start counting time (its wired to the spacebar on keyboard).

All race data is displayed via computer to the person running race control (who also needs to do things like adding / removing laps when cars jump lanes and cross the counters in the wrong lane). The race controller also presently manually switches the power off at the end of the race, and then manually turns on the start light again as well (red light goes out to start race).

Im certainly wanting to look at automating the start light and track power at some point. We do power-on starts at present due to the manual aspect of the power control, and Id want to keep that as it means there is more driver skill in the start (otherwise with a power-off start, they'd just sit there with finger on trigger).

Race Scenarios; 1. Practise - Computer just displays a rolling list of laptimes for each lane

2. Qualifying - Computer sits in practice mode, and a stopwatch is used to to time the driver for 1 minute. Driver tries to achieve fastest lap possible, and the fastest 2 laps are manually identified for providing qualifying order. When 1 minute ends, driver is allowed to complete current lap.

Races are usually comprised of 4 heats. Drivers rotate lanes between heats so that they get a run on each lane.

3. Trophy Race - System used for club championship + national-level races. Races are done by time, with XXX seconds racetime per heat (typically 3 - 5 mins). Laps counts are noted at end of each heat and manually put on a board, and then segments (100ths of a lap) are entered into the computer for each car.

All Laptimes saved for review by driver if interested, or to assist with disputes. Drivers then rotate to next lane, and it all happens again. Race results are ordered by total laps completed in race across all heats (done manually at present, computer just tracks current race). Computer provides total laps for race at end, and positions at end of each heat.

3a. Clubnight Handicaps - Drivers have 100 secs to complete 10 laps. Drivers are handicapped based on previous best performance for that particular class of car (records maintained in computer). The idea of handicaps is that all drivers should finish at almost the same time if they are driving well. It also allows cars of varying performance to compete equally.

Drivers are verbally advised their starting order, and are verbally released slowest handicap to fastest. EG slow driver will have 20 sec handicap which means it takes him 80 secs to do 10 laps. Fast driver will have 53 sec handicap, which means it takes him 47 secs to do 10 laps.

Computer provides the handicap order, and prompts race-controller with a 'READY' 1 sec prior to release time, and then a 'GO' when release time for that driver arrives. Race controller verbally calls these out to the drivers. When driver has finished 10 laps, their lane colour is called and they drive back to drivers podium and stop. Driver with fastest heat-time wins the heat. Points are allocated per heat.

Times typically will be over the 100 sec mark, and any time under 100 secs means that the drivers handicap will be increased. The computer usually starts the clock at the nearest 10 sec mark relating to handicaps. EG if we have 53, 51, 49, 35 then the computer will start the clock at 30 secs and do the necessary maths (add 30 secs to timer at start).

3b. Clubnight Grades - Drivers are seeded into grade races based on their handicap times. Grade races are for 10 laps. Drivers all start together when light goes out (instead of sequentially as in handicaps). Driver with fastest heat-time to 10 laps wins the heat. Points are allocated per heat.

it looks quite a bit more complex to do Pin Change Interrupts

It can be because you only get one interrupt per 8 inputs so you then have to figure out which input(s) changed. In this case though I’m suggesting that the ISR just reads the entire port (8 bits) and that’s the value that gets sent to the PC with the time stamp. For example,

00001000 = lane 4 changed
01000000 = lane 7 changed
00010001 = lanes 1 and 5 changed at the same time (or within the resulotion of the system)

So this scales perfectly to 8 lanes with no change to the Arduino software, or indeed the PC software because you would write that to handle 8 lanes from the start.

a bit-field is effectively a group of 0/1’s which designate state

Correct, if you send 2, 3 etc for the lane number you A) have to figure out which pins changed in the ISR and B) can’t have multiple lanes represented in a single buffer entry. By sending a bit field in a single byte you offload that decision to the PC and can have simultaneous lane events.

in the ISR there is a lower-level way of accessing the pin states

Yep you just read the port.

circular buffer

Fancy phrase for an array with read and write pointers that “wrap around”. The basic idea is like this

#define BUFFER_SIZE  256
#define ENTRY_SIZE 4
#define MAX_ENTIRES BUFFER_SIZE / ENTRY_SIZE

unsigned char buffer[BUFFER_SIZE];
int rd_ptr = 0;
int wr_ptr = 0;
int n_entries = 0;

write_entry (unsigned char lanes, long timestamp) {   // assumes 24-bit timestamp
  if (n_entries < MAX_ENTRIES {
      buffer[(wr_ptr++) & 0xff] = lanes;
      buffer[(wr_ptr++) & 0xff] = timestamp >> 16;  // save MS byte
      buffer[(wr_ptr++) & 0xff] = (timestamp >> 8) & 0x0000ff; // save middle byte
      buffer[(wr_ptr++) & 0xff] = timestamp & 0x0000ff;  // save LS byte
      n_entries++;
  } else {
      // oops we're not quick enuff :-(
  }
}

read_and_tx_entry () {
 if (n_entires != 0) {
      serial_tx ((buffer[rd_ptr++) & 0xff)
      serial_tx ((buffer[rd_ptr++) & 0xff)
      serial_tx ((buffer[rd_ptr++) & 0xff)
      serial_tx ((buffer[rd_ptr++) & 0xff)  }}

There’s probably a 1000 errors there but that’s the general idea. You write to the wr_ptr offset in the array and read from the rd_ptr offset. The (wr_ptr++) & 0xff clears any overflow past 8 bits, so the offset wraps around from 255 to 0 rather than counting on to 256. That’s faster than

if (wr_ptr > xx)
wr_ptr = 0;

And the same technique will work with any power of 2 so you can have a buffer size of 16, 32, 64, 128 etc. Just change the mask.


Rob

Actually with thought it can be made faster because it should be safe to increment four times per call before forcing the wrap around.

read_and_tx_entry () {

 if (n_entires != 0) {

   serial_tx (buffer[rd_ptr++]);
   serial_tx (buffer[rd_ptr++]);
   serial_tx (buffer[rd_ptr++]);
   serial_tx (buffer[rd_ptr++]) ;
      
   rd_ptr &= 0x00ff;
}

Rob

Circular buffer: have a look at EventQueue class here:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1279102180/9

the functions you may want to read are enqueueEvent and dequeueEvent.

HTH

You guys are just awesome. The stuff you have posted has been a huge help. Not only for filling in the gaps, but giving me enough tips to point me in the right direction to go and research further :)

Board arrived on Friday. Read 'Getting Started'over the weekend, and thumbed through 'Practical Arduino' as well. Got s/w and stuff set up and did some basic stuff Monday night to get a feel for things (blinky LED etc).

Stuck a photo interrupter on the breadboard last night, and managed to promptly fry that one by mis-reading the data sheet (thought it was bottom up, not top down view... DOH!). Anyway, the PI's I got have a schmitt trigger in them, which made life easy.

Got a sketch up and running using an interrupt on pin3, and a tight output loop for the serial, as well as some extra timing such as printing a new timestamp ~ every 1 sec. Just a single PI, but my total laptime count was only about 20 millis out from the total millis count, after about 10 mins and 300 test lap events, and no dropped serial data.

Its all just really getting used to things and unit-testing the various concepts and 'areas' on the projects at this point. I'll post my code tonight if you feel like a laugh.

Rob