dkulinski:
Alright, code with the timer running. No review done just yet.
#include <LiquidCrystal.h>
// Enable debugging code
#define DEBUG
LiquidCrystal *lcd;
// 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 = 10; // Maximum number if hits allowed
const unsigned long LCDtime = 100; // Set min time btw writes to LCD to 100 msecs
// 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
unsigned int TimerMode = 0; // initialize the mode to be single shooter
volatile int secs = 0;
volatile int frac = 0;
// char time = " ";
// char buffer[7]; // save 7 empty spaces
// Strings for formatting
char *hitCount = “^: #hits = ^”;
char *hitTime = “Hit time = ^”;
char *hitSplit = “Split time = ^”;
// Mode enumeration
enum state { INIT, READY, ARMING, RUNNING, REVIEW };
enum mode { ONEPLAYER, TWOPLAYER };
enum readyState { MAIN_MENU, PLAYER_SELECT};
enum state currentState;
enum readyState readyState = MAIN_MENU;
int numShooters = 1;
void setup()
{
// Initialization state
currentState = INIT;
// The LCD display and associated pins
lcd = new LiquidCrystal(12, 11, 5, 4, 9, 8);
// Initialize the LCD
// 20 columns and 4 rows
lcd->begin(20, 4);
// Display a start up message while unit is initialized
// Wait 5 seconds at the end to make sure the message is visible
lcd->clear();
lcd->print(" Shot Timer 1 “);
lcd->setCursor(0, 1);
lcd->print(” Initializing ");
delay(5000);
// Set the data direction of output pins
// Initialize the values to low
pinMode(BuzzerPin, OUTPUT);
pinMode(LEDPin, OUTPUT);
digitalWrite(BuzzerPin, LOW);
digitalWrite(LEDPin, LOW);
// Set the input pins and turn on pull-up resistors
pinMode(StartPin, INPUT);
pinMode(TargetAPin, INPUT);
pinMode(TargetBPin, INPUT);
digitalWrite(StartPin, HIGH);
digitalWrite(TargetAPin, HIGH);
digitalWrite(TargetBPin, HIGH);
#ifdef DEBUG
// Diagnostic information to be sent to PC over Serial
Serial.begin(9600);
Serial.print(“Shot Timer v1 Ready…”);
#endif
// Set display to ready
lcd->setCursor(0, 1);
lcd->print(" Ready ");
delay(5000);
// All initialization finished, set state to ready
currentState = READY;
}
void loop()
{
int startCycles = 0;
switch(currentState)
{
case INIT: // Do nothing in this state, we should never be here
break;
case READY: // Wait for the start button to be pressed and the mode selected
// Check if the button is down, if so increment the counter
if( !digitalRead(StartPin) )
{
startCycles++;
} else {
startCycles = 0;
}
delay(100);
readyMenu();
if(startCycles >= 10) // Button was held 1+ seconds, move to arming
{
startCycles = 0;
currentState = ARMING;
tone(BuzzerPin, 1000, 100);
lcd->noBlink();
break;
}
if(startCycles >= 2)
{
if( digitalRead(StartPin) ) // Don't do anything if the button is still down
break;
if(numShooters == 1) // Toggle number of shooters
numShooters = 2;
else
numShooters = 1;
tone(BuzzerPin, 2000, 100);
startCycles = 0;
}
break;
case ARMING: // Enable interrupts, set random start time, turn LED on, when countdown expires sound buzzer
randomSeed(millis()); // Seed the random number generator with the current millisecond count
// Turn LED on to inform user that timer is running
digitalWrite(LEDPin, HIGH);
// Get the shooters ready
lcd->clear();
lcd->setCursor(0,0);
lcd->print(" Get Ready!!!");
attachInterrupt(0,TargetAHit, FALLING); // Start interrupts for target A
if(numShooters > 1) // Check if there are two players
attachInterrupt(1,TargetBHit, FALLING);
delay(WaitTime + random(3000)); // Delay a random starting time
StartTime = millis(); // Set the start time for later usage
tone(BuzzerPin, 1000, 500); // Sound buzzer for 1/2 a second
lcd->setCursor(0,0);
lcd->print(" GO!!! ");
currentState = RUNNING;
break;
case RUNNING: // Update display on a hit
// Wait until the time is up, buzz again and move to review
if((millis() - StartTime) > MaxTime)
{
tone(BuzzerPin, 1000, 500);
currentState = REVIEW;
}
break;
case REVIEW: // Disable interrupts, change to review mode
// Timer no longer running, turn off LED
digitalWrite(LEDPin, LOW);
detachInterrupt(0);
detachInterrupt(1);
break;
}
}
void readyMenu()
{
switch(readyState)
{
case MAIN_MENU: // Select number of shooters, short press toggles between 1 and 2
lcd->setCursor(0, 1);
lcd->print(“To start,hold 1 sec”);
lcd->setCursor(0, 2);
lcd->print(“Number of shooters:”);
lcd->setCursor(0, 3);
lcd->print(numShooters);
lcd->blink();
}
}
void updateLcd()
{
lcd->clear();
lcd->print("A hits: ");
lcd->print(A_count);
lcd->setCursor(0,1);
lcd->print(formatTime(A_Times[A_count - 1] - StartTime));
if(numShooters > 1)
{
lcd->setCursor(0, 2);
lcd->print("B hits: ");
lcd->print(B_count);
lcd->setCursor(0,3);
lcd->print(formatTime(B_Times[B_count - 1] - StartTime));
}
}
void TargetAHit()
{
A_Times[A_count++] = millis();
updateLcd();
}
void TargetBHit()
{
B_Times[B_count++] = millis();
updateLcd();
}
char * formatTime(long time)
{
char * temp = “00.00”;
int seconds = time/1000;
int milliseconds = time%1000;
milliseconds /= 100; // Only 2 digits of precision
if(seconds >= 10)
temp[0] = seconds/10+48;
temp[1] = seconds%10+48;
if(milliseconds >= 10)
temp[3] = milliseconds/10+48;
temp[4] = milliseconds%10+48;
return temp;
}
For the moment I am done tonight. I will post review screen code tomorrow.
Quick question, is running two shooters simultaneous?
Dan
This code loads fine but won’t start even with the 1 second button push it requests.