From prior post …
Mee_n_Mac:
sspbass:
I think that ultimately the hit sensor will have to be a separate unit from the control/display device. So we’ll use another arduino of some type (probably a smaller one) and have that register the hits and then when the round is up have it send all of the elapsed times back in millis to the main unit that the shooter is holding for calculations converting and display. That will free up 4 pins on the main unit as an added bonus. Check out the Xbee wireless units for the Arduinos.That sounds do-able. Having each target be wirelessly tied to the timer is a bit harder.
I discussed this with an co-worker who knows Arduino stuff and the theory is that each shock sensor module would get a signal when the start buzzer sounds and would keep track of the elapsed time at the unit. Then when the round was finished they would relay back toe elapsed times in millis for the main unit to do the formatting. Each hit sensor would need to have some sort of toggle switch that would tell it which shooter it belonged to. This way there is no lag in sending signals back for each hit, everything is sent all at once at the end.
I think the harder part is getting the devices to sync up reliably. There will have to be some negotiation to make sure they received the signal before the buzzer sounds.
I like the part about local storage until a final collection is needed. That is a wise decision that will cut down on the need to ship packets back and forth when timing is critical in receiving the hits. Any idea which wireless technology you plan to use?
Dan
One of the Xbee modules. I don’t know enough about them yet to pick a specific one though.
Check them out, they look pretty cool.
sspbass:
I discussed this with an co-worker who knows Arduino stuff and the theory is that each shock sensor module would get a signal when the start buzzer sounds and would keep track of the elapsed time at the unit. Then when the round was finished they would relay back toe elapsed times in millis for the main unit to do the formatting. Each hit sensor would need to have some sort of toggle switch that would tell it which shooter it belonged to. This way there is no lag in sending signals back for each hit, everything is sent all at once at the end.
Hmmm, I’m not sure I understand. From the above it sounds like each target is getting it’s own wireless connection to a “timer”. Each gets a "start sync signal via RF and then each times individually from there on out, presumable until they get a “stop” RF signal. When a hit occurs the sensor stores the local time of the hit. I think the sync’ing and matching of all the individual clocks can be done to some degree of accuracy, probably good enough over a 60 or so sec max time period. You just need to keep each target’s local clock sync’ed to better than 1 part in 10000 to get the desired 0.01 sec precision. But if you want some central controler to declare a winner (or that the CoF is complete) based on hit count, then these targets will need to relay their hit info to the controller within some reasonable amount of time after the hit. XBee’s are good in that they have some built in packet collision detection and avoidance, so multiple targets (really only 2 for 2 shooters) reporting nearly simultaneous hits can be made to work. Sending everything back a once is simpler but limits you to a timed course. Doing the above, you may not need a centralized Ardiono-based timer/controller. An XBee Explorer connected to your PC might allow your PC to be that device (though I’m not a all sure about a good timebase then).
This is a lot like the wired system I’m doing. Each target rides a common bus and reports hits along with the local time of the hit to a central controller/timer. In turn the plan is to have that controller talk to a PC, which can then command each target to “be killed” depending on the hit(s). Each target was to be able to have 4 zones, just like the old standard IPSC target. So if the CoF called for a single A zone hit or 2 C zone hits or … you get the idea … for a target to be declared dead, the CoF is then immediately reactive to the shooter. This lends itself to all sorts of games and competitions. Because I don’t want to have to drag a PC out all the time, I want the controller/timer to be somewhat smart and be able to do some CoF’s all by itself. A lot like this project.
Since work is slooooow tonight, I have a chance to muse aloud on modes and configure operations. My thinking is at present thus :
We use pin 6 as a “Configure” button input and have pins 2 and 3 do double duty as scroll up and down button inputs. Pressing the Configure button for 1 sec gets you a list of predefined modes of operations (of Courses of Fire if you wish). Scroll buttons will have a list of 3 or 4 (out of N) such “modes” rotate through the display, with one being highlighted in some fashion. When the “mode” desired is highlighted, the user can select it by pressing some button, perhaps the Configure button again ? If there are no options that can be set, the timer returns to the ready state and awaits the start push, now in the selected mode of operation. It might be nice (and useful) to have the ready display show that mode as well. That mode persists until a new mode is choosen (or the timer is reset ??).
If the mode selected has some options that the user can set, a new list is displayed on the LCD after the “select” push. That list is “Exit” (pre-highlighted) plus whatever other options there may be (# hits to stop, time to stop, ???). Scrolling to the option and selection of the option is as before. Selecting an option to change will probably mean implementing a stupid number rolling scheme, like your digital clock does, to increase or decrease a numeric input (ie - # of hits or time to nearest sec). Kinda ugly but the best I can think of with mere buttons.
Then again instead of scroll buttons, perhaps something like this …
http://www.sparkfun.com/products/9074
… might be used, like an old iPod scrolling input ??
Of course any optional parameters input by the user get stored in EEPROM and used every time that mode is selected, until/unless they are changed.
Given the above, I wonder if single or double shooters should be mode dependant (some CoF only make sense w/2 shooters) or selectable independantly, outside of the whole mode configuration process ? If there were more input pins (and there can be using the A/D voltage technique), it might be one of those things that deserves it’s own dedicated switch input, like certain functions on your digital camera have (so there’s no having to dig through menu layer after menu layer to select it).
And since I’m in info-overload mode, here one more thing to ponder as time passes by. If you wanted to distinguish hits on one target from hits on another, you could have each target have it’s pulse be a different width. This width could be measured by the Arduino and used to determine which target had been hit. This could be used to ensure all “N” targets had been hit as opposed to just detecting “N” hits. Or to ensure each target had been double tapped instead of just “2N” hits. Or that they had been hit in a pre-specified order. Or …
Given a reasonable time between shots for each shooter and constraining the measurement be made by the Arduino using the millis() function, I think you could have somewhere between 6 and 10 targets on each A and B channel and be able to tell one from the next via PW measurement. In the following I’ve been conservative about setting the true initial PW and how it might drift with time and temp and how well the Arduino might measure it. Thus I’ve left a 2 msec gap between each target’s worst case measured PWs. Now if the true PWs can be held to tighter tolerances and perhaps the micros() function used to measure the PWs, you might fit more targets closer together in the same range of PWs and thus get more than 6-10 targets per channel (the longest pulse must be done before the next hit happens).
How about we use 4 buttons all ran on one analog pin. Up, Down, Left & Right.
Arduino turns on and is ready to go
How many shooters → Up & Down key to increment number. Right key goes to next menu, Left key does nothing.
CoF? —> Up & Down key scrolls through different modes. Right key selects the highlighted CoF, Left key goes back to previous screen.
If CoF needs more info the Up & Down buttons will increment either a time or a number of hits. Right button selects the current value entered and
the Left button goes back one menu.
Self timing or Partner timing? (not sure how to word that) —> Up, Down highlight one. Right selects it and Left goes back. Self timing would set
random start time between 2 & 5 seconds, Partner timing would star immediately when start button is pushed.
Shooter Ready —> Start button is pushed and round commences. Start button can be linked in series with the Right button & either or could be hit.
This would just make it easier for the shooter to grasp as well as easier to hit when it’s attached to your belt.
After round is done is shows the main display screen and then can scroll through split time screens. Start button/Right button can be pushed again to re-initiate the same round again. When the shooter want’s to do a different round they just hit the Left button to go back one screen or push and hold for 2 seconds to go back to the initial “How many shooters?” screen.
What do you think about this?
Of course if you have the digital ports open, put a button per port. No need to complicate it. Sometimes pre-optimization is the root of all evil
Dan
sspbass:
How about we use 4 buttons all ran on one analog pin. Up, Down, Left & Right.…
What do you think about this?
It sounds reasonable. Somehow I’ve got a switch, hardwired for 1/2 shooters, stuck in my brain ATM. User interface is really what you think works for you, as it’s your timer. I just wish there were a better way to do things than wading through a layer of menus. If you want we can continue to kick the topic around and see if something uber-good pops to the surface. Your scheme gave me an idea. You have the up & down buttons picking the # of shooters. What if there were a button for each “thing” to be choosen, like on a dSLR. There’s a button for #shoooters and hit that and it yeilds a display of # shooters, 1, 2 and then the up or down to chose which is wanted. Hitting a separate “mode” button brings up a list of modes/CoFs and up/down then scrolls amongst them until the one you want is highlighted. Perhaps pushing the mode again chooses that mode and sets up the timer or, if there are options to be set, brings you to that modes options. Another “configure” button might bring to another menu for picking timer options like backlight on/off and wired/wireless and store times to SD card or ??? Hmmmm, I’m no sure how this all pans out. I guess I’m following the lead of the camera people, who have a mixture of menu picked items (for lesser used things) and dedicated buttons plus scrolling for more used items. The idea being you generally try to avoid “hiding” options too many layers of menus deep.
In any case more buttons R good. If the A and B inputs can do double duty, that gives us 3. Using the A/D can give … well a lot more but I’d only use those for buttons/functions to be used when the timer is not running (to avoid any A/D conversion time penalty). Did you get a chance to try the code above and see if the B hit button now scrolls after the timer is stopped ? That a whack on the hit sensor causes a scroll in the opposite direction ? Just out of curiousity I’d like to see if that works.
And it occured to me (I know I can overcomplicate this timer if I try hard enough :twisted: ) that with 2 unequal shooters it might be nice to have a “handicap”. That is the timer declares a winner based on quickest time + the handicap. If I’m a 3.5 sec (for 5 pins) shooter and I want to race a 5 sec person, I get a 1.5 sec handicap. It gets entered into the timer and if I shoot a 3.5 vs their 5.1, the timer says I win. If they shoot a 4.9, they win {4.9 < (3.5+1.5)}.
So how does on highlight an item on the LCD ? Blink it ? I know the LCD library has blink() function. I don’t know how that works with this display. If you send a blink() to the LCD, does every character written thereafter blink (until the noblink() is sent) ? Or does the cursor, wherever it presently is, just blink ? I didn’t see a reverse video command but I do believe the display can do more than I’ve seen in the Arduino LCD library. Perhaps it’s time for some (blinking) experiments to see how the LCD works and what looks good before this gets coded ?
The cursor blinks. You could always program reverse text into the character RAM on the display.
Dan
dkulinski:
The cursor blinks. You could always program reverse text into the character RAM on the display.Dan
So a better blinking highlight might be to write the line, wait, clear the line, wait, write … etc, etc.
FWIW here’s vid on the “pro” timer CED7000
http://www.youtube.com/watch?v=lJtj-_qYwj4
And one not so “pro”
http://www.youtube.com/watch?v=bsMQ_AlF … re=related
And one for your iPhone
Or, you can do something tricky. Change the CG RAM for the characters. You can easily load in a new font and the LCD will actually reflect the change on the display. Of course you don’t want to do the whole of the font, just whatever characters you are interested in. I haven’t tried this but I did find it on a web page about animations on the HD44780 devices.
Dan
That CED 700 pro has a ton of features but when all you have is one input there is a lot less to worry about.
I guess we could probably put the number of shooters and start time delay features under a settings menu.
My opinion is that it is inevitable to have a few levels of menus to go through, we just need to make it intuitive.
We could set one set of settings as the default so it can start as soon as the unit starts but that would be assuming a lot.
I hear where your coming from on the buttons but I think it adds unnecessary hardware with not enough functionality.
Perhaps there is some common ground where there was one button that could bet set to recall a “favorite match setting” whatever that may be for the shooter. Perhaps that is several orders of magnitude more complicated but it would be cool.
All I’m thinking is around 10 seconds of setup time tops.
Turns on
How many shooters?
CoF?
Number of hits/Round duration?
Start
Rinse/repeat until the shooter wants a different mode and then just start at the beginning.
i’m not sure about that Par time stuff. Do you use that feature at all?
Last time I was “tricky” it took a week to get back to working ! :mrgreen:
Haha, no (+1, like, RT) buttons on these posts! Yes, I avoid tricky. The CED 700 is pretty nice but it uses audio to determine the shot? It doesn’t have a hit sensor? You could put a color LCD behind this and get a more flexible display. Those Nokia clones are cheap. I also like the round resistor wheel you posted a few posts back. Makes for an interesting interface. However, I would stick with a simple input design. I think sspbass is right, KISS is a good principle on the user interface (and in general).
Dan
Mee_n_Mac:
I occurs to me that using DK’s method of attaching and detaching the interrupts might mean we can have pins 2 and 3 do double duty. When the timer is running they are interrupt pins, getting hits from the targets. When the timer is stopped and the interrupts are disabled, I don’t see why they can’t be used as scroll button up and down inputs. The buttons would be wired in parallel to the hit sensors and be normally open, momentary short-to-ground type switches, which is what I think you’ve used so far for all the switches. Then the present scroll button could be used as the menu or configure button. I like DK’s idea to have a timer on the button so it has to be held for a bit to get into configure mode, this would prevent accidental activations.So to see if pins 2 and 3 can be shared, I’ve changed the single scroll action we’ve had to date to use the button I think you have on the B input now. Let’s see if that works to move through the review displays. Heck I s’pose you could even use the shock sensor as a button. In fact, give it a try and see what happens (a scroll down perhaps). But now you’ll have to be careful about post stop hits.
void loop()
{
if (TimerState) // is timer running
{
// timer is running
// send data to display if there have been any hits
if (AupDtFlag == 1 || BupDtFlag == 1)
{
CalcTimes();
FormatData();
}
// timer is running so now check for any stop conditions
if ((digitalRead(StartPin) == LOW) || ((millis() - StartTime) > StopTime) || A_count >= StopHits || B_count >=
StopHits)
{
// stop the timer change the display
StopTimer();
tone(BuzzerPin, FreqLo, BuzzTime10);
// update display just in case last msec hit came in
if (AupDtFlag == 1 || BupDtFlag == 1)
{
CalcTimes();
FormatData();
}
// just for the moment send times to PC for debug
SendTimes();
// delay enough to debounce stop button
delay(DB_delay);
// Change to review-stopped state display based on timer mode
switch (TimerMode)
{
case 1:
//this is for A vs B shooter mode
diMin = 6;
diMax = int(max(A_count,B_count)/4) + diMin - 1; // find how many whole screens
if (max(A_count,B_count) % 4)
{
diMax++ ; // add another screen if partial exists
}
DisplayIndex = diMax; // display will show list of hit times
break;
case 2:
//this is for quick draw mode
diMin = 6;
diMax = int(max(A_count,B_count)/4) + diMin - 1; // find how many whole screens
if (max(A_count,B_count) % 4)
{
diMax++ ; // add another screen if partial exists
}
DisplayIndex = diMax; // display will show list of hit times
break;
case 3:
//this is for 21 mode
diMin = 6;
diMax = int(max(A_count,B_count)/4) + diMin - 1; // find how many whole screens
if (max(A_count,B_count) % 4)
{
diMax++ ; // add another screen if partial exists
}
DisplayIndex = diMax; // display will show list of hit times
break;
default:
// the default = single A shooter mode
diMin = 2;
diMax = int(A_count/8) + diMin - 1; // find how many whole screens
if (A_count % 8)
{
diMax++ ; // add another screen if partial exists
}
DisplayIndex = diMax; // display will show list of hit times
}
}
}
else
{
// timer is stopped look for start button or review button pushes
// check for scroll button pushes, change display state as reqd
if (digitalRead(TargetBPin) == LOW)
{
DisplayIndex++; // increment DisplayIndex upon push
if (DisplayIndex > diMax)
{
DisplayIndex = diMin; // wrap around of scrolling action
}
// change to new display
FormatData();
delay(DB_delay);
}
if (digitalRead(TargetAPin) == LOW)
{
DisplayIndex--; // decrement DisplayIndex upon push
if (DisplayIndex < diMin)
{
DisplayIndex = diMax; // wrap around of scrolling action
}
// change to new display
FormatData();
delay(DB_delay);
}
// check to see if start button has been pushed
if (digitalRead(StartPin) == LOW)
{
// start button pushed
// Do the following debug code only to get system running
// This will send message to PC to show button was pushed
Serial.println("Timer is running");
// turn on the LED to show timer is running
digitalWrite(LEDPin, HIGH);
// clear all the prior runs data, reset the display
ClearData();
DisplayIndex = 0;
FormatData();
// delay the Wait Time from start button push
// this delay is for debounce purposes and should stay
delay(DB_delay);
// this delay will change to random later on
delay(WaitTime);
// enable the interrupts based on mode here ATM
// this will change when config mode is added ?
switch (TimerMode)
{
case 1:
//this is for A vs B shooter mode
attachInterrupt(0, ISR_A, FALLING);
attachInterrupt(1, ISR_B, FALLING);
DisplayIndex = 5; // display will show count and time of hits
break;
case 2:
//this is for quick draw mode
attachInterrupt(0, ISR_A, FALLING);
attachInterrupt(1, ISR_B, FALLING);
DisplayIndex = 5; // display will show count and time of hits
StopTime = 10000;
StopHits = 6;
break;
case 3:
//this is for 21 mode
attachInterrupt(0, ISR_A, FALLING);
attachInterrupt(1, ISR_B, FALLING);
DisplayIndex = 5; // display will show count and time of hits
StopTime = 1500;
StopHits = MaxHits;
break;
default:
// the default = single A shooter mode
attachInterrupt(0, ISR_A, FALLING);
DisplayIndex = 1; // display will show count and time of hits
}
// save the starting time of this run
StartTime = millis();
// set state of timer to running
TimerState = 1;
// now buzz the speaker for 0.5 secs
tone(BuzzerPin, FreqHi, BuzzTime5);
}
}
}
I've also changed/added some mode stuff for your "21" and "Quick draw" modes. You may see the new variables StopHits and StopTime. These serve as variable versions of MaxHits and MaxTimes. The latter are the true maximums the timer can have, due to memory contraints or whatever. The former will serve as mode dependant limits to stop the timer when the prerequisite of hits have occurred or the CoF time has elapsed. If you want you can try those out as well but be forewarned that I've not put a lot of thought into making them work as of yet. YMMV, past results are no guarantee of future performance, etc, etc. Later on they'll be properly named/enumerated in the code.
This code won’t compile as the new variables StopHits and StopTime are not declared.
I thought the CED7000 would be an interesting comparison. I see no real need for the scoring (A,C,D) input, frankly that's something I'd use a PC for. I do like the ability to save the times for later review. I've never had any use for the par time function.sspbass:
That CED 700 pro has a ton of features but when all you have is one input there is a lot less to worry about.…
i’m not sure about that Par time stuff. Do you use that feature at all?
So then a 4-way switch “matrix” with up, down, right and left. Perhaps a center enter button if it can be done ? Pretty much standard fare with camera and TV remotes and even on the CED timers.
Left/right switches among the various “top” menus, up/down switches among the choices in a “top” menu, enter selects the highlighted choice … which might or might not lead to another menu to customize it’s optional inputs. Like a good camera, pressing the start button immediately brings the timer back to ready or the last review display.
sspbass:
This code won’t compile as the new variables StopHits and StopTime are not declared.
Guess it might have helped if I have given this part then …
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 9, 8);
// set pin numbers
const int StartPin = 7; // the number of the Start pushbutton pin
const int TargetAPin = 2; // the number of the A targets input pin
const int TargetBPin = 3; // the number of the B targets input pin
const int BuzzerPin = 10; // the number of the buzzer output pin
const int LEDPin = 13; // the number of the LED output pin
const int ScrollPin = 6; // the number of the scroll button input pin
//const int ButtonInPin = A0; // the pin used for the analog buttons
// initialize the constants
const unsigned long MaxTime = 30000; // the max time the timer can run for = 30 secs
const unsigned long WaitTime = 2000; // wait time btw start and timer running = 2 secs plus debounce
const unsigned long DB_delay = 1000; // set a debounce wait time of 1000 msecs
const unsigned long BuzzTime5 = 500; // set the on time for the buzzer, 500 msecs
const unsigned long BuzzTime2 = 200; // set the on time for the buzzer, 200 msecs
const unsigned long BuzzTime10 = 1000; // set the on time for the buzzer, 1000 msecs
const unsigned int FreqHi = 2000; // High frequency of buzzer tone
const unsigned int FreqLo = 1000; // Low frequency of buzzer tone
const byte MaxHits = 20; // Maximum number if hits allowed
// initialize global variables
volatile byte TimerState = 0; // variable for state of timer, running or not running
volatile byte AupDtFlag = 0; // variable indication an A hit has occurred
volatile byte BupDtFlag = 0; // variable indication a B hit has occurred
byte DisplayIndex = 0; // variable for controlling what's displayed
unsigned long StartTime = 0; // variable to hold the start time
unsigned long BuzzTime = 500; // variable to hold the buzzer on time
unsigned int Freq = 2000; // variable for high or low buzzer tone
volatile byte A_count = 0; // variable to hold the number of A hits
volatile byte B_count = 0; // variable to hold the number of B hits
volatile long A_Times[MaxHits]; // array to hold up to MaxHits hit times for target A
volatile long B_Times[MaxHits]; // array to hold up to MaxHits hit times for target B
unsigned long A_splits[MaxHits];
unsigned long B_splits[MaxHits];
long AB_splits[MaxHits];
unsigned long LastTime = 0; // initialize last LCD display time to zero for 1st pass
float A_Hit_Factor = 0;
float B_Hit_Factor = 0;
byte S1 = 0; // Switch 1 init to zero
byte S2 = 0; // Switch 2 init to zero
unsigned int diMin = 0; // Display index mininmim
unsigned int diMax = 99; // Display index maximum
volatile int secs = 0;
volatile int frac = 0;
byte StopHits = 20; // Number if hits allowed before stopping
unsigned long StopTime = 30000; // Time that the timer will stop at
unsigned int TimerMode = 0; // initialize the mode to be single shooter
//unsigned int TimerMode = 1; // initialize the mode to be A vs B
//unsigned int TimerMode = 2; // initialize the mode to be quick draw
//unsigned int TimerMode = 3; // initialize the mode to be 21
:doh:
Note you can uncomment and comment out TImerMode to choose some of your CoFs. Not sure if they are all kosher ATM but give 'em a try.