I decided that a beer wasn’t enough. Alas I don’t have the makings for a proper MaiTai at hand so I’m settling for a Black Russian ! :dance:
I’ve changed a few things (LED changes state with start/stop button push, display shouldn’t flicker, max targets = 20) and want to “document” what I think should be a new working baseline for the code. So here it is, give it a try and see if it still works … but better !
#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
// 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 unsigned int MaxHits = 20; // Maximum number if hits allowed
const int RUN = 1; // make code easy to understand
// initialize global variables
volatile int TimerState = 0; // variable for state of timer, running or not running
int DisplayState = 0; // variable for controlling what's displayed
int UpDtFlag = 0; // variable indication a hit has occurred
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 int A_count = 0; // variable to hold the number of A hits
volatile int B_count = 0; // variable to hold the number of B hits
volatile long A_Times[MaxHits];// array to hold up to 10 hit times for target A
volatile long B_Times[MaxHits];// array to hold up to 10 hit times for target B
long A_splits [MaxHits];
long B_splits [MaxHits];
long AB_splits [MaxHits];
int A_Hit_Factor = 0;
int B_Hit_Factor = 0;
// setup and intialize two strings for the display each 16 chars long
String Line1 = "Arduino timer v1";
String Line2 = "says Hello World";
void setup()
{
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
lcd.clear();
lcd.print("Hello World");
delay(5000);
// initialize output pins
pinMode(BuzzerPin, OUTPUT);
pinMode(LEDPin, OUTPUT);
digitalWrite(BuzzerPin, LOW);
digitalWrite(LEDPin, LOW);
// initialize the input pins with internal pullups
pinMode(StartPin, INPUT);
pinMode(UpPin, INPUT);
pinMode(DownPin, INPUT);
pinMode(TargetAPin, INPUT);
pinMode(TargetBPin, INPUT);
digitalWrite(StartPin, HIGH);
digitalWrite(UpPin, HIGH);
digitalWrite(DownPin, HIGH);
digitalWrite(TargetAPin, HIGH);
digitalWrite(TargetBPin, HIGH);
// opens serial port, sets data rate to 9600 bps
Serial.begin(9600);
// setup ext pins as interrupts
attachInterrupt(0, ISR_A, FALLING);
attachInterrupt(1, ISR_B, FALLING);
}
void loop()
{
if (digitalRead(StartPin) == LOW)
{
// start stop button has been pushed, is timer running
if (TimerState == !RUN)
{
// timer is not running so start it
// 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();
FormatData();
LCDdisplay();
// 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 just in case
interrupts();
// save the starting time of this run
StartTime = millis();
// set state of timer to running
TimerState = RUN;
// now buzz the speaker for 0.5 secs
tone(BuzzerPin, FreqHi, BuzzTime5);
}
else
{
// start stop button has been pushed again to stop timer
StopTimer();
tone(BuzzerPin, FreqLo, BuzzTime10);
// delay enough to debounce stop button
delay(DB_delay);
// just for the moment send times to PC
SendTimes();
}
}
// send data to display if there have been any hits
if (UpDtFlag == 1)
{
CalcTimes();
FormatData();
LCDdisplay();
// this is goofy but for now change display state here
DisplayState = 1;
}
//Check for max time out if timer is running
if (TimerState == RUN)
{
if ((millis() - StartTime) > MaxTime)
{
// call the function that does all the things needed to stop
StopTimer();
tone(BuzzerPin, FreqLo, BuzzTime10);
// just for the moment send times to PC
SendTimes();
}
}
}
void StopTimer()
{
//noInterrupts();
TimerState = !RUN;
// turn the LED off to show timer is stopped
digitalWrite(LEDPin, LOW);
}
void ClearData()
{
A_count = 0;
B_count = 0;
for (int i=0; i < MaxHits ; i++)
{
A_Times[i] = 0;
B_Times[i] = 0;
A_splits[i] = 0;
B_splits[i] = 0;
AB_splits[i] = 0;
}
A_Hit_Factor = 0;
B_Hit_Factor = 0;
DisplayState = 0;
}
void SendTimes()
// temp routine to send data to serial monitor
{
Serial.println("Timer is stopped");
Serial.println("Here are the times for Shooter A");
Serial.print("Number of hits : ");
Serial.print("\t");
Serial.println(A_count);
Serial.println("A Hit and split times are : ");
int i = 0;
int k = 0;
for (i=0; i < A_count ; i++)
{
k = i + 1;
//Serial.print(A_count[k]);
Serial.print("\t");
Serial.print(A_Times[i]);
Serial.print("\t");
Serial.println(A_splits[i]);
}
Serial.println("Here are the times for Shooter B");
Serial.print("Number of hits : ");
Serial.print("\t");
Serial.println(B_count);
Serial.println("B Hit and split times are : ");
for (i=0; i < B_count ; i++)
{
k = i + 1;
//Serial.print(B_count[k]);
Serial.print("\t");
Serial.print(B_Times[i]);
Serial.print("\t");
Serial.println(B_splits[i]);
}
}
void ISR_A()
{
if(TimerState == RUN)
{
// store the hit time
A_Times[A_count] = millis() - StartTime;
// increment the hit count and array index
++A_count;
// set the Hit flag so other stuff will update
UpDtFlag = 1;
}
}
void ISR_B()
{
if(TimerState == RUN)
{
// store the hit time
B_Times[B_count] = millis() - StartTime;
// increment the hit count and array index
++B_count;
// set the Hit flag so other stuff will update
UpDtFlag = 1;
}
}
void CalcTimes()
{
// routine to calculate all data and declare winner
// not all calcs having meaning for uses of timer
// calculate A splits and cumlative hit factor
if (A_count > 1)
{
for (int i=1; i < A_count ; i++)
{
A_splits[i] = A_Times[i] - A_Times[i-1];
}
}
else
{
A_splits[0] = A_Times[0];
}
A_Hit_Factor = A_Times[A_count - 1]/A_count;
// calculate B splits and cumlative hit factor
if (B_count > 1)
{
for (int i=1; i < B_count ; i++)
{
B_splits[i] = B_Times[i] - B_Times[i-1];
}
}
else
{
B_splits[0] = B_Times[0];
}
B_Hit_Factor = B_Times[B_count - 1]/B_count;
// Calculate A - B times just in case
int Min_count = min(A_count, B_count);
for (int i=0; i < Min_count ; i++)
{
AB_splits[i] = A_Times[i] - B_Times[i];
}
// add more here for A vs B modes
}
void FormatData()
{
// routine to format lines of data to be sent to LCD
// presently assume 2 lines of 16 characters
// switch formatting done based on display state
switch (DisplayState)
{
case 1:{
//this is for single A shooter
String tmp = "A: # hits = ";
// for loop to create the proper number of padding spaces
// tmp holds 12 characters
// need 3 or less spaces to pad so hit count is right justified
// 3 spaces for hits 0-9, 2 spaces for 10-99, 1 space else
//for (int i=3; i >= sizeof(char(A_count)); i--)
//{
//tmp = tmp + ' '; // add space to end of existing string
//}
// display number of hits so far
Line1 = tmp + A_count ;
// print the time of last hit in secs
String tmp2 = "Time is ";
Line2 = tmp2 + (A_Times[A_count-1]);
break;}
case 2:{
// this is for A vs B mode
// put that display code here
break;
default:
// do the default =0 for now
// 2 string objects for lines 1 and 2
Line1 = "Arduino timer v1";
Line2 = "says Hello World";}
}
}
void LCDdisplay()
{
// sends 2 lines to LCD using library functions
lcd.clear();
lcd.print(Line1);
lcd.setCursor(0, 1);
lcd.print(Line2);
// reset the display update flag
UpDtFlag = 0;
}