If you have any luck in getting the above library functions copied into your PC and everything all properly placed, here’s a version of the prior code that has 5 msec waits stuck anywhere I thought they might be needed and an lcd.setCursor(0,0) after every lcd.clear I could find. The new “clear” might reset the cursor, but I figure why not do it explicitly … just in case. Now I’m not sure if the initialization sequence at the link above needs also needs to be in the code explicitly or whether that happens behind the scenes when the (? revised ?) function lcd.begin is called. I guess you’ll find out if it doesn’t work. If not, then add it in. You may have to add in whatever new “includes” are needed by the library functions at the link above.
So perhaps this can return to the goodness it had at at one point.
#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
// initialize the constants
const unsigned long MaxTime = 60001; // the max time the timer can run for = 60 secs
const unsigned long WaitTime = 1000; // 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 = 24; // 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
volatile int secs = 0; // whole seconds of time for display
volatile int frac = 0; // fractional part of time for display
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 unsigned long A_Times[MaxHits]; // array to hold up to MaxHits hit times for target A
volatile unsigned long B_Times[MaxHits]; // array to hold up to MaxHits hit times for target B
byte DisplayIndex = 0; // variable for controlling what's displayed
byte diMax = 99; // Display index maximum
byte StopHits = MaxHits; // Number if hits allowed before stopping
unsigned long StopTime = MaxTime; // Time that the timer will stop at
byte CoF = 0; // initialize the mode to be standard
//byte CoF = 1; // initialize the mode to be quick draw
//byte CoF = 2; // initialize the mode to be 21
byte NumShooters = 1; // initialize the number of shooters to 1
//byte NumShooters = 2; // initialize the number of shooters to 2
byte RanDly = 0; // random delay disabled
//byte RanDly = 1; // random delay enabled
void setup()
{
// set up the LCD's number of columns and rows:
lcd.begin(20, 4);
delay(5);
lcd.clear();
delay(5);
lcd.setCursor(0, 0);
delay(5);
lcd.print(" Shot Timer 1 ");
lcd.setCursor(0, 1);
delay(5);
lcd.print(" Initializing ");
delay(3000);
// 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(TargetAPin, INPUT);
pinMode(TargetBPin, INPUT);
digitalWrite(StartPin, HIGH);
digitalWrite(TargetAPin, HIGH);
digitalWrite(TargetBPin, HIGH);
// for now use pin 6 as button input
pinMode(ScrollPin, INPUT);
digitalWrite(ScrollPin, 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);
lcd.setCursor(0, 1);
delay(5);
lcd.print(" Ready ");
ClearData();
}
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)
{
UpdateLCD();
}
// timer is running so now check for any stop conditions
if ((digitalRead(StartPin) == LOW) || ((millis() - StartTime) >= StopTime) || A_count >= StopHits || B_count >= StopHits)
{
// update the LCD running display one last time
UpdateLCD();
// stop the timer
StopTimer();
tone(BuzzerPin, FreqLo, BuzzTime10);
// just for the moment send times to PC for debug
SendTimes();
// delay enough to debounce stop button
delay(DB_delay);
// Setup the review-stopped state display based on number shooters and hits
if(NumShooters == 1)
{
diMax = byte(A_count/8) - 1; // DI goes 0 to number of displays - 1
if (A_count % 8) // diMax indicates last screen of hits
{
diMax ++; // limit lines per number of hits
}
DisplayIndex = diMax; // display will show list of hit times
}
else
{
diMax = byte(max(A_count,B_count)/4) - 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
}
}
}
else
{
// timer is stopped look for start button or review button pushes
// check for scroll button pushes, change display state as reqd
if (digitalRead(ScrollPin) == LOW)
{
DisplayIndex++; // increment DisplayIndex upon push
if (DisplayIndex > diMax)
{
DisplayIndex = 0; // wrap around of scrolling action
}
// update display
UpdateLCD();
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);
// show the timer has been started
lcd.clear();
delay(5);
lcd.setCursor(0, 0);
delay(5);
lcd.print(" Shot Timer v1 ");
lcd.setCursor(0, 1);
delay(5);
lcd.print(" is running ");
lcd.setCursor(0, 2);
delay(5);
lcd.print(" Shooter: ");
lcd.setCursor(0, 3);
delay(5);
lcd.print(" --STANDBY-- ");
// this will change when config mode is added ?
switch (CoF)
{
case 1:
//this is for quick draw mode
StopTime = 10001;
StopHits = 6;
break;
case 2:
//this is for 21 mode
StopTime = 1501;
StopHits = MaxHits;
break;
default:
// the default = standard timer mode
StopTime = MaxTime;
StopHits = MaxHits;
}
// delay the Wait Time from start button push
randomSeed(millis());
// delay is min delay plus random if enabled
delay(WaitTime); // 1 sec
if(RanDly)
{
delay(random(2000, 4000)); // total delay btw 3 and 5 secs
}
// save the starting time of this run
StartTime = millis();
// set state of timer to running
TimerState = 1;
// turn on interrupts as needed for 1 or 2 shooters
attachInterrupt(0, ISR_A, FALLING);
if (NumShooters > 1)
{
attachInterrupt(1, ISR_B, FALLING);
}
// clear all the prior runs data
ClearData();
// now buzz the speaker for 0.5 secs
tone(BuzzerPin, FreqHi, BuzzTime5);
}
}
}
void StopTimer()
{
// turn off interrupts
detachInterrupt(0);
detachInterrupt(1);
TimerState = 0;
// turn the LED off to show timer is stopped
digitalWrite(LEDPin, LOW);
}
void ClearData()
{
A_count = 0;
B_count = 0;
for (byte i = 0; i < MaxHits; i++)
{
A_Times[i] = 0;
B_Times[i] = 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 : ");
byte i = 0;
unsigned long Split = 0;
for (i = 0; i < A_count; i++)
{
Serial.print(i+1);
Serial.print("\t");
Serial.print(A_Times[i]);
Serial.print("\t");
if (i)
{
Split = A_Times[i] - A_Times[i-1];
Serial.print(Split);
}
Serial.println("\t");
}
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++)
{
Serial.print(i+1);
Serial.print("\t");
Serial.print(B_Times[i]);
Serial.print("\t");
if (i)
{
Split = B_Times[i] - B_Times[i-1];
Serial.print(Split);
}
Serial.println("\t");
}
}
void ISR_A()
{
if(TimerState)
{
// 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
AupDtFlag = 1;
}
}
void ISR_B()
{
if(TimerState)
{
// 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
BupDtFlag = 1;
}
}
void UpdateLCD()
{
AupDtFlag = 0;
BupDtFlag = 0;
// routine to format lines of data to be sent to LCD
// presently assume 4 lines of 20 characters
// formatting based number of shooters and timer state
if(TimerState)
{
// timer is running
// display number of hits so far
lcd.clear();
delay(5);
lcd.setCursor(0, 0);
delay(5);
lcd.print(" A: # hits = ");
if(A_count < 10)
{
lcd.print(' ');
}
lcd.print(A_count);
// now display the time of last hit
lcd.setCursor(0, 1);
delay(5);
lcd.print("A Hit time = ");
if(A_count)
{
TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
}
lcd.setCursor(0, 2);
delay(5);
// Chose 1 or 2 shooter portion of display
if(NumShooters == 1)
{
// this is for single A shooter running display
// display the split time btw last hit and prior hit
unsigned long Split = 0;
if (A_count > 1)
{
Split = A_Times[A_count-1] - A_Times[A_count-2];
}
lcd.print("Split time = ");
TimeConvert(Split); // convert split time
lcd.setCursor(0, 3);
delay(5);
// compute A hit factor
//float HF = A_count/A_Times[A_count - 1];
//lcd.print("Hit factor = ");
//char buf[6];
//PString(buf, sizeof(buf), HF); // convert FP to char
//lcd.print(buf);
}
else
{
// this is for 2 shooter running display
lcd.print(" B: # hits = ");
if(B_count < 10)
{
lcd.print(' ');
}
lcd.print(B_count);
lcd.setCursor(0, 3);
delay(5);
lcd.print("B Hit time = ");
if(B_count)
{
TimeConvert(B_Times[B_count-1]); // Now for B times
}
}
}
else
{
// timer is stopped
if(NumShooters == 1)
{
// this is single shooter review display
lcd.clear();
delay(5);
lcd.setCursor(0, 0);
delay(5);
byte k = 0; // k will hold LCD line number
byte j = DisplayIndex << 3; // index for first hit time displayed
byte jj = (A_count % 8); // jj holds number hits on last display
if (DisplayIndex == diMax) // diMax indicates last screen of hits
{
jj = j + min(jj,4); // limit lines according to number of hits
}
else
{
jj = j + 4; // there are enough hits to fill 4 lines
}
for (byte i = j; i < jj; i++)
{
k = (i % 4);
lcd.setCursor(0,k);
delay(5);
lcd.print('A');
if(i<9)
{
lcd.print(' '); // add a space for single digit numbers
}
lcd.print(i+1); // send hit # to LCD
lcd.print(": ");
TimeConvert(A_Times[i]); // convert hit time to XX.xx format
if(A_Times[i+4]) // if there's a non-zero time
{
lcd.print(' '); // print a space
if((i+4)<9)
{
lcd.print(' '); // add a space for single digit numbers
}
lcd.print(i+5); // send hit # to LCD
lcd.print(": ");
TimeConvert(A_Times[i+4]); // convert hit time to XX.xx format
}
}
}
else
{
// this is 2 shooter review display
lcd.clear();
delay(5);
lcd.setCursor(0, 0);
delay(5);
byte k = 0; // k will hold LCD line number
byte j = DisplayIndex << 2; // index for first hit time displayed
byte jj = max(A_count,B_count); // init jj to max hit count
if (DisplayIndex == diMax) // diMax indicates last screen of hits
{
jj = j + (jj % 4); // limit lines per number of hits
}
else
{
jj = j + 4; // there are enough hits to fill 4 lines
}
for (byte i = j; i < jj; i++)
{
k = (i % 4);
lcd.setCursor(0,k);
delay(5);
if(i<9)
{
lcd.print(' '); // for hits 1-9 put in a leading space
}
lcd.print(i+1); // print hit number
lcd.print(" A: ");
TimeConvert(A_Times[i]); // convert hit time to XX.xx format
if(B_Times[i]);
{
lcd.print(" B: ");
TimeConvert(B_Times[i]); // convert hit time to XX.xx format
}
}
}
}
}
void TimeConvert(unsigned long input)
{
// takes the msecs as argument and sets the time for that hit as a XX.xxs string
if (input != 0) // Make sure there's a value...
{
secs = int((input + 5)/1000L); // round msecs into whole secs
frac = int(((input + 5) % 1000L)/10L); // find fractional rounded secs
if (secs < 10)
{
lcd.print(' '); // for sub 10 sec times add leading space
}
lcd.print(secs); // now add whole secs
lcd.print('.'); // now add period
if (frac < 10)
{
lcd.print('0'); // If frac < 10 then pad with a zero
}
lcd.print(frac); // now add fractional secs
}
else
{
lcd.print(" "); // If the input variable is zero set time to 6 blank spaces
}
}
ps - did those goodies include the 2 axis slidey joystick thingee ?