My question is: how do I read multiple inputs at the same time without using interrupts on an UNO. I'm building an RC drag strip with tree and finish line that will calculate run time as well as calculate each run in mph. I'm using lasers and non modulated laser detectors for the beams and I'm using a 1" beam gap(between 1st beam and 2nd beam) at the finish line(for mph calculation). My concern comes in when two cars come close enough at the finish line for one to break the first beam at set the win flag but the car in the other lane is close enough to cross the 1st beam before the 1st car actually crosses the 2nd beam then the detection loop is going to miss one of them. (just a note: a few of these cars can top 100 mph when crossing the line)
3 Answers
ONE:
What you can do is use a while loop waiting for the first pulse and then wait until the last pulse, for an idea look at this code from Gadjet, he made a chronograph with a similar operation, you would just need to adapt the design, this might not be as accurate for an 1". Also it will be the issue of having ties between cars. This is short as I don't like this option.
TWO:
Another option, although using what you don't want to use, would be using the interrupts on the Arduino. I am basing the following on the fact that you are using two cars.
What you can do is have a finish line
for both vehicles and detect a break with a interrupt, one interrupt pin per lane(UNO has to interrupts).
The trick here is that you place both the outputs from one lane onto a single interrupt pin with a edge detector type of circuit1 to generate a pulse while the vehicle crosses a point, especially important as it will be in the inch area for a while. The code handles the double pulse on one pin. I know this works as I used this on a ballistic chronograph which works in microseconds detecting shots >2000fps over a 0.406 foot distance.
1 This is an example of the circuit you would use at the output of each sensor before the OR gates:
The hardware would just become a duplicate for each lane, and to combine the input on one pin you can use an OR gate IC or you can use diode OR gate:
Interrupts are actually easy to use with Arduinos, and there are a lot of examples out there.
Have a look at the below flow chart to get an idea of operation in the code:
A small thing would be that the code ignores a interrupt once the cars have passed so it doesn't calculate a second time.
Building upon Nick Gammon's excellent answer at How many interrupt pins can an Uno handle? here's some untested code:
const int events = 256; // events to store
unsigned volatile int index;
byte pinStates[events];
unsigned int times[events];
ISR (PCINT1_vect) // Watch for pin changes on the C port (Uno pins A0-A5)
{
if (index == 0) { // start clock
TCCR1B = bit(CS12) | bit (CS10) ; // clock/1024 for <4.19s x 64us
//TCCR1B = bit(CS12); // clock/256 for <1.05s x 16us
//TCCR1B = bit(CS11) | bit (CS10) ; // clock/64 for <0.25s x 4us
//TCCR1B = bit(CS11) ; // clock/8 for <0.03s x 0.5us
//TCCR1B = bit(CS10) ; // clock/1 for <0.004s x 62ns
}
times[index] = TCNT1;
pinStates[index] = PORTC;
index = (index+1) & 0xff; // increment and protect from overflow
} // end of PCINT1_vect
void setup ()
{
// activate pin change interrupts on port C / pins A0-A5
PCICR = bit (PCIE1); // enable PCINT[14:8] pin triggers
PCMSK1 |= bit (PCINT8) |
bit (PCINT9) |
bit (PCINT10) |
bit (PCINT11) |
bit (PCINT12) |
bit (PCINT13); // enable individual pins
// stop and clear 16 bit timer CS1[0-2]=0; TCNT1 = 0;
TCCR1A = 0; // no comparisons, normal WGM mode, stop the counting
TCCR1B = 0;
TCNT1 = 0; // Clear the counter
pinMode (A0, INPUT_PULLUP);
pinMode (A1, INPUT_PULLUP);
pinMode (A2, INPUT_PULLUP);
pinMode (A3, INPUT_PULLUP);
pinMode (A4, INPUT_PULLUP);
pinMode (A5, INPUT_PULLUP);
index = 0 ; // nothing happened yet
pinMode (5, OUTPUT);
} // end of setup
void loop ()
{
// check index, parse and report times, check for arming, etc...
}
The point is to use the pin change interrupt to record state changes on the pins and the times. The clock/1024 prescaler would give a 1/16MhZ*1024*65536= 4.19s window at 64us resolution timing counts, but you could go to higher resolution with a tighter window by using a smaller prescaler.
Also, if you wanted longer timing windows, you could cascade timer1 into timer0 (messing up millis(), etc.,) and get 24 bits of range before overflow, but a better way than cascading timers might be to add another interrupt to increment a variable. For much longer timing windows add another quick interrupt to handle for timer1 overflow.
...
volatile unsigned long int t1Cycles,times[events];
...
ISR(TIMER1_OVF_vect){t1Cycles++;}
void setup(){
...
TIMSK1 = bit(TOIE1);
t1Cycles = 0;
...
}
ISR (PCINT1_vect)
{
...
times[index] = t1Cycles << 16 | TCNT1; // store 32 bit count of clocks
...
}
This is still untested, but it looks like it could be capable of 268 seconds worth of 62ns resolution multichannel timing. With the longer period, you could move the conditional clock-starting code out of the pin-change ISR.
-
1I think I would use a much lower prescaler. Since you have kept the timer stopped until the first pulse, it won't take 4 seconds for the car to cross to the second laser.– Nick Gammon ♦Feb 3, 2016 at 21:53
-
One certainly could use a lower prescaler. I chose the slower speed because my kids just did a pinewood derby race, and wanted to allow plenty of time to measure the slow cars to cross the line.– Dave XFeb 4, 2016 at 2:44
-
1Are you trying to catch all entries? In that case 4s might be too little. You could catch overflows ... wait you just edited that in. Excellent. Tip: make
t1Cycles
volatile. Also I think you want to shiftt1Cycles
left 16, as Timer 1 is a 16-bit timer.– Nick Gammon ♦Feb 4, 2016 at 4:39 -
Yes, catching all entries was what I was aiming at. Thanks for the tips.– Dave XFeb 4, 2016 at 4:52
-
I made more edits and it seems to compile for an Uno. To test it on the AT90usb1286 hardware I've got, I'd need to switch things over to port B.– Dave XFeb 4, 2016 at 5:16
One option, if you want very-real-time processing, for a task like this that break neatly into chunks: Have one Arduino per vehicle.
Arduinos are cheap. If they are too expensive, use AtTiny's - they can be had for about a dollar a piece (depending on your currency). You can share a clock/power between a large number of them.
You can either have a "master" Arduino talk to each individual one, to collect the information, or have daisy-chained bus - each one can pass it's data to the next one in the chain (once is has the data, and the next one in the chain indicates it's ready for it).
-
1I think my objection to this would be synchronizing them. Especially if they are each doing different lanes. If they didn't agree exactly on what the time was, they might report the runner-up as the winner.– Nick Gammon ♦Feb 4, 2016 at 5:22
then the detection loop is going to miss one of them
- how long are the cars? Isn't the bigger issue that the first beam will still be broken, and that the second car won't register at all, regardless of how you program it?