Counting and Timing a shock sensor

Errrr, nevermind. I forgot I had added the period in when I did the test for length to add the leading zero and blank space. Here’s what I hope is the real fix.

String TimeConvert(long time)
{
// takes the time as argument and returns the time for that hit as a XX.xxs string
  if (time != 0) // Make sure there's a value...
  {
    String tmp3 = String(int((time + 5)/10)); // round msecs into csecs
    int k = tmp3.length();  // k will be index to last char in padded string
    int km1 = k-1;          // km1 will be index to next to last char
    int km2 = k-2;          // km2 will be position of period
    tmp3 = tmp3 + '0';  // pad the end of the truncated string with a zero
    //now move chars to make space to add a period
    tmp3.setCharAt(k, tmp3.charAt(km1));   // move next-2-last to last position in string
    tmp3.setCharAt(km1, tmp3.charAt(km2)); // move char to next-2-last position
    // now insert period
    tmp3.setCharAt(km2, '.');
    // add a blank or 2 as needed to get 5 digits with time right justified
    if(tmp3.length() < 4)
    {
      tmp3 = " 0" + tmp3;     // must be a sub 1 sec time add a blank and a leading 0
    }
    else
    {
      if (tmp3.length() < 5)
      {
        tmp3 = ' ' + tmp3;    // must be a sub 10 sec time add a blank to justify
      }
    {
    // tmp3 now holds rounded time in secs XX.xx format
    tmp3 += 's':       // add s for seconds
    return tmp3;
  }
  else
  {
    String tmp3 = "      "; // If the time variable is zero pass 6 blanks back as a string...
    return tmp3;
  }
}

What were you saying about drunk college students writing crap code … :oops:

If the above works, then here is a whole new rev that lays the groundwork for implementing “modes”. I have excluded the random time as I’ve yet to T/S that part. We can add that back in later. What I’d like you to do is make sure this works as normal with particular attention to how the display transitions from running to stopped states and how the scroll works with varying numbers of hits. That is with <8 hits it doesn’t scroll. With 20 hits it should auot-stop and scroll btw 3 screens, 1-8, 9-16 and 17-20 hits. If the stop and start and scroll buttons all work then I’d say it’s good to go for adding in modes.

#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
long A_splits[MaxHits];
long B_splits[MaxHits];
long AB_splits[MaxHits];
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

// setup and intialize strings for the display each 20 chars long
String Line1 = "    Shot Timer v1   ";
String Line2 = "      is alive      ";
String Line3 = "                    ";
String Line4 = "                    ";

void setup()
{
  // set up the LCD's number of columns and rows: 
  lcd.begin(20, 4);
  lcd.clear();
  lcd.print("    Shot Timer 1    ");
  lcd.setCursor(0, 1);
  lcd.print("    Initializing    ");
  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);

  // 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);
  lcd.print("      Ready       ");
}


void loop()
{
  if (TimerState) // is timer running
  {
    // timer is running
    // set display according to timer mode
    switch (TimerMode)
    {
      case 1:{
        //this is for A vs B shooter mode
        // set DisplayIndex = something here
        break;}
      case 2:{
        //this is for ??? mode
        // set DisplayIndex = something here
        break;}
      case 3:{
        //this is for ???? mode
        // set DisplayIndex = something here
        break;}
      default: 
        // the default = single A shooter mode
        DisplayIndex = 1;   // display will show count and time of hits
    }
    
    // send data to display if there have been any hits
    if (AupDtFlag == 1 || BupDtFlag == 1)
    {
      CalcTimes();
      FormatData();
      LCDdisplay();
    }
    
    // timer is running so now check for any stop conditions
    if ((digitalRead(StartPin) == LOW) || ((millis() - StartTime) > MaxTime) || A_count >= MaxHits || B_count >= MaxHits)
    {
      // 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();
        LCDdisplay();
      }
      
      // 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
          // set DisplayIndex = something here
          break;}
        case 2:{
          //this is for ??? mode
          // set DisplayIndex = something here
          break;}
        case 3:{
          //this is for ???? mode
          // set DisplayIndex = something here
          break;}
        default: 
          // the default = single A shooter mode
          diMin = 2;
          diMax = int(A_count/8) + dsMin - 1; // find how many whole screens
          if (A_count % 8)
          {
            diMax++ ;          // add another screen if partial exists
          }
          DisplayIndex = 2;    // 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 = diMin;     // wrap around of scrolling action
      }
      // change to new display
      FormatData();
      LCDdisplay();
      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();
      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 = 1;
      
      // now buzz the speaker for 0.5 secs
      tone(BuzzerPin, FreqHi, BuzzTime5);
    }
  }
}


void StopTimer()
{
  //noInterrupts();
  TimerState = 0;
  // 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;
}


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 < MaxHits ; 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)
  {
    // 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 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 4 lines of 20 characters
// switch formatting done based on display state
  switch (DisplayIndex)
  {
    case 1:{
      //this is for single A shooter mode
      // display number of hits so far
      String tmp = "A: # hits = ";
      Line1 = tmp + A_count;
      // now display the time of last hit in secs out to hundreths of secs
      String tmp2 = TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
      Line2 = " Hit  time = " + tmp2;
      tmp2 = TimeConvert(A_splits[A_count-1]); // convert split time
      Line3 = "Split time = " + tmp2;
      Line4 = "                    ";
      break;}
    case 2:{
      // this is A review mode hits 1-8
      String tmp = TimeConvert(A_Times[0]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[4]); // convert hit time to XX.xx format
      Line1 = "A  1:" + tmp + " 5:" + tmp2;
      tmp = TimeConvert(A_Times[1]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[5]); // convert hit time to XX.xx format
      Line2 = "A  2:" + tmp + " 6:" + tmp2;
      tmp = TimeConvert(A_Times[2]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[6]); // convert hit time to XX.xx format
      Line3 = "A  3:" + tmp + " 7:" + tmp2;
      tmp = TimeConvert(A_Times[3]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[7]); // convert hit time to XX.xx format
      Line4 = "A  4:" + tmp + " 8:" + tmp2;
      break;}
    case 3:{
      // this is A review mode hits 9-16
      String tmp = TimeConvert(A_Times[8]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[12]); // convert hit time to XX.xx format
      Line1 = "A  9:" + tmp + "13:" + tmp2;
      tmp = TimeConvert(A_Times[9]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[13]); // convert hit time to XX.xx format
      Line2 = "A 10:" + tmp + "14:" + tmp2;
      tmp = TimeConvert(A_Times[10]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[14]); // convert hit time to XX.xx format
      Line3 = "A 11:" + tmp + "15:" + tmp2;
      tmp = TimeConvert(A_Times[11]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[15]); // convert hit time to XX.xx format
      Line4 = "A 12:" + tmp + "16:" + tmp2;
      break;}
    case 4:{
      // this is A review mode hits 17-20
      String tmp = TimeConvert(A_Times[16]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[18]); // convert hit time to XX.xx format
      Line1 = "A 17:" + tmp + "19:" + tmp2;
      tmp = TimeConvert(A_Times[17]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[19]); // convert hit time to XX.xx format
      Line2 = "A 18:" + tmp + "20:" + tmp2;
      Line3 = "                    ";
      Line4 = "                    ";
      break;}
    default: 
      // do the default = 0
      // 4 string objects
      Line1 = "    Shot Timer v1   ";
      Line2 = "     is running     ";
      Line3 = "      Shooter:      ";
      Line4 = "    --STANDBY--     ";
  }
}


String TimeConvert(long time)
{
// takes the time as argument and returns the time for that hit as a XX.xxs string
  if (time != 0) // Make sure there's a value...
  {
    String tmp3 = String(int((time + 5)/10)); // round msecs into csecs
    int k = tmp3.length();  // k will be index to last char in padded string
    int km1 = k-1;          // km1 will be index to next to last char
    int km2 = k-2;          // km2 will be position of period
    tmp3 = tmp3 + '0';  // pad the end of the truncated string with a zero
    //now move chars to make space to add a period
    tmp3.setCharAt(k, tmp3.charAt(km1));   // move next-2-last to last position in string
    tmp3.setCharAt(km1, tmp3.charAt(km2)); // move char to next-2-last position
    // now insert period
    tmp3.setCharAt(km2, '.');
    // add a blank or 2 as needed to get 5 digits with time right justified
    if(tmp3.length() < 4)
    {
      tmp3 = " 0" + tmp3;     // must be a sub 1 sec time add a blank and a leading 0
    }
    else
    {
      if (tmp3.length() < 5)
      {
        tmp3 = ' ' + tmp3;    // must be a sub 10 sec time add a blank to justify
      }
    {
    // tmp3 now holds rounded time in secs XX.xx format
    tmp3 += 's':       // add s for seconds
    return tmp3;
  }
  else
  {
    String tmp3 = "      "; // If the time variable is zero pass 6 blanks back as a string...
    return tmp3;
  }
}


void LCDdisplay()
{
  // reset display update flags
  AupDtFlag = 0;
  BupDtFlag = 0;
  // sends 4 lines to LCD using library functions
  lcd.clear();
  lcd.print(Line1);
  lcd.setCursor(0, 1);
  lcd.print(Line2);
  lcd.setCursor(0, 2);
  lcd.print(Line3);
  lcd.setCursor(0, 3);
  lcd.print(Line4);
}

sspbass:
You were correct. I didn’t realize you added that other function in there and copied it twice.

It is working now. The video is uploading and I’ll have it on here shortly.

Never-mind the noisy wife and dog in the background :lol:

http://www.youtube.com/watch?v=7Nf7ELnb … e=youtu.be

I find that funny it didn’t work… It must have been the noob that implemented it. :lol: I just tested the function and it returned good times. I agree with Mac-n-mee there are bigger fish to fry than that.

DirtyD:
I just tested the function and it returned good times.

Was that the “fixed-really-really-this-time” TimeConvert() function or the whole new rev ? Hopefully the new rev of code works just as good as the prior version. If you look at it you can see where I’m building the framework for differing displays based on timer mode and scroll button(s). Each mode will have a base display index that gets you the display used when the timer is running. Other display indexes, off from that index, will get you the displays used when reviewing the data. I think this method is flexible enough to accomodate whatever modes and displays we dream up.

I think the next thing to do is, for the moment, hard code the timer mode input but code up the timer action and displays for an “A vs B” mode of operation. That is you’ll load a sketch for the present single shooter mode or load a sketch for the “A vs B” mode (TimerMode set appropriately in each sketch)) but the software will run as if that mode was entered by the user, making all the runtime and review displays happen as if the mode were not hardcoded. This allows sspbass to use the timer in an A vs B mode and see what “features” await us there while the method of entering modes and storing them gets figured out. The latter is new territory for me as I think it’ll require writing to and reading from EEPROM to do it properly. And I think the timer needs at least 1 more input button, dedicated to get the timer to display a “menu” and enter a user input mode. This would allow the user to select the above (and more) modes and command the timer to be in that mode upon the next start.

Mee_n_Mac:
Errrr, nevermind. I forgot I had added the period in when I did the test for length to add the leading zero and blank space. Here’s what I hope is the real fix.

String TimeConvert(long time)

{
// takes the time as argument and returns the time for that hit as a XX.xxs string
if (time != 0) // Make sure there’s a value…
{
String tmp3 = String(int((time + 5)/10)); // round msecs into csecs
int k = tmp3.length(); // k will be index to last char in padded string
int km1 = k-1; // km1 will be index to next to last char
int km2 = k-2; // km2 will be position of period
tmp3 = tmp3 + ‘0’; // pad the end of the truncated string with a zero
//now move chars to make space to add a period
tmp3.setCharAt(k, tmp3.charAt(km1)); // move next-2-last to last position in string
tmp3.setCharAt(km1, tmp3.charAt(km2)); // move char to next-2-last position
// now insert period
tmp3.setCharAt(km2, ‘.’);
// add a blank or 2 as needed to get 5 digits with time right justified
if(tmp3.length() < 4)
{
tmp3 = " 0" + tmp3; // must be a sub 1 sec time add a blank and a leading 0
}
else
{
if (tmp3.length() < 5)
{
tmp3 = ’ ’ + tmp3; // must be a sub 10 sec time add a blank to justify
}
{
// tmp3 now holds rounded time in secs XX.xx format
tmp3 += ‘s’: // add s for seconds
return tmp3;
}
else
{
String tmp3 = " "; // If the time variable is zero pass 6 blanks back as a string…
return tmp3;
}
}




What were you saying about drunk college students writing crap code .... :oops:

Just got home and was going to try to work with this a little. The code above totally F’ed it up. It worked until it came to registering hits and then the lcd whent totally haywire. I then reloaded the old version and even that was acting weird so I’m tossing in the flag for tonight. I’m not very good at this sober much less after a few beers so it will have to wait until tomorrow. The weather is supposed to be crappy anyhow.

sspbass:
Just got home and was going to try to work with this a little. The code above totally F’ed it up. It worked until it came to registering hits and then the lcd whent totally haywire. I then reloaded the old version and even that was acting weird so I’m tossing in the flag for tonight. I’m not very good at this sober much less after a few beers so it will have to wait until tomorrow. The weather is supposed to be crappy anyhow.

If the same happens again take a little more timer btw the buzz and the 1’st whack. I’m looking at the code now and I realize I have assumed there would be no time less than 100 msecs. That means I thought I’d always have 3 digits minimum to work with in the time convert function. If there’s less than that, things can get goofy. In real life I doubt that times less than 100 msec are possible but perhaps I should add in a check just to be sure. I’m still not sure what the 1’st hit time was in Test Video 7. It looks like .00s but … ???

Now if short short times aren’t the problem and it persists with the new code and it isn’t there with the old … then use the old TimeConvert() with the latest rev (replacing the TimeConvert() that’s included in that rev) I posted above and see what happens. This new TimeConvert() (the really really fixed version) was based on my guess that the reason for the failure of leading zeros and blanks in the earlier version (used in Test Video 7) was that the length of the tmp3 string was not being updated during the time that the convert function was doing it’s calcs. If the real reason was something else (? the LCD.print library software or LCD firmware ?) … then perhaps the new “fixed” version is adding too many blanks and the LCD is therefore getting more that 20 characters to fit on it’s 20 char line. That would probably make things goofy in the review displays. If that’s the case we’ll have to live with the “ugly” display and use the old version of TimeConvert() (the one used in Test Video 7) … at least for now.

What’s odd (and probably a good clue) is that the display when the timer is running shows (in Test Video 7) the hit time and the split time. The split times were always sub 1 sec times. Yet the decimal points line up (as they’re supposed to). This means the convert function was adding in leading blanks/spaces and the LCD was acting on them. And yet the leading zero wasn’t there in the split time. I don’t know how that can be ??? But I can figure out a test to show what’s happening with the LCD. When I get home I’ll put up a small test sketch that does nothing but send some test lines to the LCD to find out what gets displayed and what doesn’t.

Here’s a quick test sketch to see what happens with blanks and leading zeros. If you’ve got something you want to see, replace the last line … go for it. I’d like to see a vid of this from reset and for a cycle or two (assuming the really really fixed code above is still AFU when you try it today).

#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 9, 8);

String Line1 = "    Shot Timer v1   ";
String Line2 = "      is alive      ";
String Line3 = "                    ";
String Line4 = "                    ";


void setup()
{
  // set up the LCD's number of columns and rows: 
  lcd.begin(20, 4);
  lcd.clear();
  lcd.print("    Shot Timer 1    ");
  lcd.setCursor(0, 1);
  lcd.print("    Initializing    ");
  delay(5000);
}

void loop()
{
  lcd.clear();
  lcd.print("00.00 s 01.11 s 0.12");
  lcd.setCursor(0, 1);
  lcd.print(" 00.00 s 0.11 s .123");
  lcd.setCursor(0, 2);
  lcd.print("  .00 s 0.0.0 s  123");
  lcd.setCursor(0, 3);
  lcd.print("123456789 0123456789");
  delay(5000);
  lcd.clear();
  lcd.print(Line1);
  lcd.setCursor(0, 1);
  lcd.print(Line2);
  lcd.setCursor(0, 2);
  lcd.print(Line3);
  lcd.setCursor(0, 3);
  lcd.print(Line4);
  delay(5000);
  Line1 = "00.00 s 01.11 s 0.12";
  Line2 = " 00.00 s 0.11 s .123";
  Line3 = "  .00 s 0.0.0 s  123";
  Line4 = "123456789 0123456789";
  delay(5000);
  lcd.clear();
  lcd.print(Line1);
  lcd.setCursor(0, 1);
  lcd.print(Line2);
  lcd.setCursor(0, 2);
  lcd.print(Line3);
  lcd.setCursor(0, 3);
  lcd.print(Line4);
  delay(5000);  
}

Mee_n_Mac:
Errrr, nevermind. I forgot I had added the period in when I did the test for length to add the leading zero and blank space. Here’s what I hope is the real fix.

String TimeConvert(long time)

{
// takes the time as argument and returns the time for that hit as a XX.xxs string
if (time != 0) // Make sure there’s a value…
{
String tmp3 = String(int((time + 5)/10)); // round msecs into csecs
int k = tmp3.length(); // k will be index to last char in padded string
int km1 = k-1; // km1 will be index to next to last char
int km2 = k-2; // km2 will be position of period
tmp3 = tmp3 + ‘0’; // pad the end of the truncated string with a zero
//now move chars to make space to add a period
tmp3.setCharAt(k, tmp3.charAt(km1)); // move next-2-last to last position in string
tmp3.setCharAt(km1, tmp3.charAt(km2)); // move char to next-2-last position
// now insert period
tmp3.setCharAt(km2, ‘.’);
// add a blank or 2 as needed to get 5 digits with time right justified
if(tmp3.length() < 4)
{
tmp3 = " 0" + tmp3; // must be a sub 1 sec time add a blank and a leading 0
}
else
{
if (tmp3.length() < 5)
{
tmp3 = ’ ’ + tmp3; // must be a sub 10 sec time add a blank to justify
}
{
// tmp3 now holds rounded time in secs XX.xx format
tmp3 += ‘s’: // add s for seconds
return tmp3;
}
else
{
String tmp3 = " "; // If the time variable is zero pass 6 blanks back as a string…
return tmp3;
}
}




What were you saying about drunk college students writing crap code .... :oops:

Got the previous rev to work again and then tried the code above once more and it messed it up again.

http://www.youtube.com/watch?v=ZN5uJ0c9 … e=youtu.be

And here is the video of your test code.

http://www.youtube.com/watch?v=lQK8_iW2 … e=youtu.be

Well the LCD test code makes a display (vid #9) just as it should. Leading zeros and/or blanks don’t get lost. I assume that when left to run longer it eventually settles to display the same “number soup” screen seen in vid #9. That means the TimeConvert() function isn’t adding blanks and a zero as expected but the “fix” is wrong as well. The FU’ed vid (#8), if you stop it and look btw hits, shows stuff is going to the wrong lines on the LCD. The only way I can see this happening is with longer than 20 char strings being sent to the LCD. It must “wrap over” to the next line … though I don’t see crap on line #4. Which means somehow it’s the String arrays of Line1-Line3 that are getting hosed ??? All in all very confusing.

For now I think the thing to do is use the old TimeConvert() in the new “mode” code and proceed ahead until someone has a Eureka moment … cuz I’m lost. Here is that code … Hopefully it works the same as the working revision you have now, with the same less than perfectly pretty display upon review.

#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
long A_splits[MaxHits];
long B_splits[MaxHits];
long AB_splits[MaxHits];
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

// setup and intialize strings for the display each 20 chars long
String Line1 = "    Shot Timer v1   ";
String Line2 = "      is alive      ";
String Line3 = "                    ";
String Line4 = "                    ";

void setup()
{
  // set up the LCD's number of columns and rows: 
  lcd.begin(20, 4);
  lcd.clear();
  lcd.print("    Shot Timer 1    ");
  lcd.setCursor(0, 1);
  lcd.print("    Initializing    ");
  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);

  // 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);
  lcd.print("      Ready       ");
}


void loop()
{
  if (TimerState) // is timer running
  {
    // timer is running
    // set display according to timer mode
    switch (TimerMode)
    {
      case 1:{
        //this is for A vs B shooter mode
        // set DisplayIndex = something here
        break;}
      case 2:{
        //this is for ??? mode
        // set DisplayIndex = something here
        break;}
      case 3:{
        //this is for ???? mode
        // set DisplayIndex = something here
        break;}
      default: 
        // the default = single A shooter mode
        DisplayIndex = 1;   // display will show count and time of hits
    }
    
    // send data to display if there have been any hits
    if (AupDtFlag == 1 || BupDtFlag == 1)
    {
      CalcTimes();
      FormatData();
      LCDdisplay();
    }
    
    // timer is running so now check for any stop conditions
    if ((digitalRead(StartPin) == LOW) || ((millis() - StartTime) > MaxTime) || A_count >= MaxHits || B_count >= MaxHits)
    {
      // 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();
        LCDdisplay();
      }
      
      // 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
          // set DisplayIndex = something here
          break;}
        case 2:{
          //this is for ??? mode
          // set DisplayIndex = something here
          break;}
        case 3:{
          //this is for ???? mode
          // set DisplayIndex = something here
          break;}
        default: 
          // the default = single A shooter mode
          diMin = 2;
          diMax = int(A_count/8) + dsMin - 1; // find how many whole screens
          if (A_count % 8)
          {
            diMax++ ;          // add another screen if partial exists
          }
          DisplayIndex = 2;    // 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 = diMin;     // wrap around of scrolling action
      }
      // change to new display
      FormatData();
      LCDdisplay();
      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();
      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 = 1;
      
      // now buzz the speaker for 0.5 secs
      tone(BuzzerPin, FreqHi, BuzzTime5);
    }
  }
}


void StopTimer()
{
  //noInterrupts();
  TimerState = 0;
  // 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;
}


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 < MaxHits ; 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)
  {
    // 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 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 4 lines of 20 characters
// switch formatting done based on display state
  switch (DisplayIndex)
  {
    case 1:{
      //this is for single A shooter mode
      // display number of hits so far
      String tmp = "A: # hits = ";
      Line1 = tmp + A_count;
      // now display the time of last hit in secs out to hundreths of secs
      String tmp2 = TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
      Line2 = " Hit  time = " + tmp2;
      tmp2 = TimeConvert(A_splits[A_count-1]); // convert split time
      Line3 = "Split time = " + tmp2;
      Line4 = "                    ";
      break;}
    case 2:{
      // this is A review mode hits 1-8
      String tmp = TimeConvert(A_Times[0]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[4]); // convert hit time to XX.xx format
      Line1 = "A  1:" + tmp + " 5:" + tmp2;
      tmp = TimeConvert(A_Times[1]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[5]); // convert hit time to XX.xx format
      Line2 = "A  2:" + tmp + " 6:" + tmp2;
      tmp = TimeConvert(A_Times[2]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[6]); // convert hit time to XX.xx format
      Line3 = "A  3:" + tmp + " 7:" + tmp2;
      tmp = TimeConvert(A_Times[3]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[7]); // convert hit time to XX.xx format
      Line4 = "A  4:" + tmp + " 8:" + tmp2;
      break;}
    case 3:{
      // this is A review mode hits 9-16
      String tmp = TimeConvert(A_Times[8]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[12]); // convert hit time to XX.xx format
      Line1 = "A  9:" + tmp + "13:" + tmp2;
      tmp = TimeConvert(A_Times[9]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[13]); // convert hit time to XX.xx format
      Line2 = "A 10:" + tmp + "14:" + tmp2;
      tmp = TimeConvert(A_Times[10]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[14]); // convert hit time to XX.xx format
      Line3 = "A 11:" + tmp + "15:" + tmp2;
      tmp = TimeConvert(A_Times[11]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[15]); // convert hit time to XX.xx format
      Line4 = "A 12:" + tmp + "16:" + tmp2;
      break;}
    case 4:{
      // this is A review mode hits 17-20
      String tmp = TimeConvert(A_Times[16]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[18]); // convert hit time to XX.xx format
      Line1 = "A 17:" + tmp + "19:" + tmp2;
      tmp = TimeConvert(A_Times[17]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[19]); // convert hit time to XX.xx format
      Line2 = "A 18:" + tmp + "20:" + tmp2;
      Line3 = "                    ";
      Line4 = "                    ";
      break;}
    default: 
      // do the default = 0
      // 4 string objects
      Line1 = "    Shot Timer v1   ";
      Line2 = "     is running     ";
      Line3 = "      Shooter:      ";
      Line4 = "    --STANDBY--     ";
  }
}


String TimeConvert(long time)
{
// takes the time as argument and returns the time for that hit as a XX.xxs string
  if (time != 0) // Make sure there's a value...
  {
    String tmp3 = String(int((time + 5)/10)); // round msecs into csecs
    int k = tmp3.length();  // k will be index to last char in padded string
    int km1 = k-1;          // km1 will be index to next to last char
    int km2 = k-2;          // km2 will be position of period
    tmp3 = tmp3 + '0';  // pad the end of the truncated string with a zero
    //now move chars to make space to add a period
    tmp3.setCharAt(k, tmp3.charAt(km1));   // move next-2-last to last position in string
    tmp3.setCharAt(km1, tmp3.charAt(km2)); // move char to next-2-last position
    // now insert period
    tmp3.setCharAt(km2, '.');
    // add a blank or 2 as needed to get 5 digits with time right justified
    if(tmp3.length() < 3)
    {
      tmp3 = " 0" + tmp3;     // must be a sub 1 sec time add a blank and a leading 0
    }
    else
    {
      if (tmp3.length() < 4)
      {
        tmp3 = ' ' + tmp3;    // must be a sub 10 sec time add a blank to justify
      }
    {
    // tmp3 now holds rounded time in secs XX.xx format
    tmp3 += 's':       // add s for seconds
    return tmp3;
  }
  else
  {
    String tmp3 = "      "; // If the time variable is zero pass 6 blanks back as a string...
    return tmp3;
  }
}


void LCDdisplay()
{
  // reset display update flags
  AupDtFlag = 0;
  BupDtFlag = 0;
  // sends 4 lines to LCD using library functions
  lcd.clear();
  lcd.print(Line1);
  lcd.setCursor(0, 1);
  lcd.print(Line2);
  lcd.setCursor(0, 2);
  lcd.print(Line3);
  lcd.setCursor(0, 3);
  lcd.print(Line4);
}

The code works, I like how you have it set to not automatically go into the review mode upon completion of “shooting”.

The 1st page of review looks great but the 2nd is where it gets goofy… as you already know.

Here is the code that compiled so I don’t need to keep fixing the same simple errors.

#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
long A_splits[MaxHits];
long B_splits[MaxHits];
long AB_splits[MaxHits];
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

// setup and intialize strings for the display each 20 chars long
String Line1 = "    Shot Timer v1   ";
String Line2 = "      is alive      ";
String Line3 = "                    ";
String Line4 = "                    ";

void setup()
{
  // set up the LCD's number of columns and rows: 
  lcd.begin(20, 4);
  lcd.clear();
  lcd.print("    Shot Timer 1    ");
  lcd.setCursor(0, 1);
  lcd.print("    Initializing    ");
  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);

  // 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);
  lcd.print("      Ready       ");
}


void loop()
{
  if (TimerState) // is timer running
  {
    // timer is running
    // set display according to timer mode
    switch (TimerMode)
    {
      case 1:{
        //this is for A vs B shooter mode
        // set DisplayIndex = something here
        break;}
      case 2:{
        //this is for ??? mode
        // set DisplayIndex = something here
        break;}
      case 3:{
        //this is for ???? mode
        // set DisplayIndex = something here
        break;}
      default: 
        // the default = single A shooter mode
        DisplayIndex = 1;   // display will show count and time of hits
    }
    
    // send data to display if there have been any hits
    if (AupDtFlag == 1 || BupDtFlag == 1)
    {
      CalcTimes();
      FormatData();
      LCDdisplay();
    }
    
    // timer is running so now check for any stop conditions
    if ((digitalRead(StartPin) == LOW) || ((millis() - StartTime) > MaxTime) || A_count >= MaxHits || B_count >= MaxHits)
    {
      // 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();
        LCDdisplay();
      }
      
      // 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
          // set DisplayIndex = something here
          break;}
        case 2:{
          //this is for ??? mode
          // set DisplayIndex = something here
          break;}
        case 3:{
          //this is for ???? mode
          // set DisplayIndex = something here
          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 = 2;    // 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 = diMin;     // wrap around of scrolling action
      }
      // change to new display
      FormatData();
      LCDdisplay();
      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();
      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 = 1;
      
      // now buzz the speaker for 0.5 secs
      tone(BuzzerPin, FreqHi, BuzzTime5);
    }
  }
}


void StopTimer()
{
  //noInterrupts();
  TimerState = 0;
  // 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;
}


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 < MaxHits ; 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)
  {
    // 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 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 4 lines of 20 characters
// switch formatting done based on display state
  switch (DisplayIndex)
  {
    case 1:{
      //this is for single A shooter mode
      // display number of hits so far
      String tmp = "A: # hits = ";
      Line1 = tmp + A_count;
      // now display the time of last hit in secs out to hundreths of secs
      String tmp2 = TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
      Line2 = " Hit  time = " + tmp2;
      tmp2 = TimeConvert(A_splits[A_count-1]); // convert split time
      Line3 = "Split time = " + tmp2;
      Line4 = "                    ";
      break;}
    case 2:{
      // this is A review mode hits 1-8
      String tmp = TimeConvert(A_Times[0]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[4]); // convert hit time to XX.xx format
      Line1 = "A  1:" + tmp + " 5:" + tmp2;
      tmp = TimeConvert(A_Times[1]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[5]); // convert hit time to XX.xx format
      Line2 = "A  2:" + tmp + " 6:" + tmp2;
      tmp = TimeConvert(A_Times[2]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[6]); // convert hit time to XX.xx format
      Line3 = "A  3:" + tmp + " 7:" + tmp2;
      tmp = TimeConvert(A_Times[3]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[7]); // convert hit time to XX.xx format
      Line4 = "A  4:" + tmp + " 8:" + tmp2;
      break;}
    case 3:{
      // this is A review mode hits 9-16
      String tmp = TimeConvert(A_Times[8]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[12]); // convert hit time to XX.xx format
      Line1 = "A  9:" + tmp + "13:" + tmp2;
      tmp = TimeConvert(A_Times[9]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[13]); // convert hit time to XX.xx format
      Line2 = "A 10:" + tmp + "14:" + tmp2;
      tmp = TimeConvert(A_Times[10]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[14]); // convert hit time to XX.xx format
      Line3 = "A 11:" + tmp + "15:" + tmp2;
      tmp = TimeConvert(A_Times[11]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[15]); // convert hit time to XX.xx format
      Line4 = "A 12:" + tmp + "16:" + tmp2;
      break;}
    case 4:{
      // this is A review mode hits 17-20
      String tmp = TimeConvert(A_Times[16]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[18]); // convert hit time to XX.xx format
      Line1 = "A 17:" + tmp + "19:" + tmp2;
      tmp = TimeConvert(A_Times[17]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[19]); // convert hit time to XX.xx format
      Line2 = "A 18:" + tmp + "20:" + tmp2;
      Line3 = "                    ";
      Line4 = "                    ";
      break;}
    default: 
      // do the default = 0
      // 4 string objects
      Line1 = "    Shot Timer v1   ";
      Line2 = "     is running     ";
      Line3 = "      Shooter:      ";
      Line4 = "    --STANDBY--     ";
  }
}


String TimeConvert(long time)
{
// takes the time as argument and returns the time for that hit as a XX.xxs string
  if (time != 0) // Make sure there's a value...
  {
    String tmp3 = String(int((time + 5)/10)); // round msecs into csecs
    int k = tmp3.length();  // k will be index to last char in padded string
    int km1 = k-1;          // km1 will be index to next to last char
    int km2 = k-2;          // km2 will be position of period
    tmp3 = tmp3 + '0';  // pad the end of the truncated string with a zero
    //now move chars to make space to add a period
    tmp3.setCharAt(k, tmp3.charAt(km1));   // move next-2-last to last position in string
    tmp3.setCharAt(km1, tmp3.charAt(km2)); // move char to next-2-last position
    // now insert period
    tmp3.setCharAt(km2, '.');
    // add a blank or 2 as needed to get 5 digits with time right justified
    if(tmp3.length() < 3)
    {
      tmp3 = " 0" + tmp3;     // must be a sub 1 sec time add a blank and a leading 0
    }
    else
    {
      if (tmp3.length() < 4)
      {
        tmp3 = ' ' + tmp3;    // must be a sub 10 sec time add a blank to justify
      }
    {
    // tmp3 now holds rounded time in secs XX.xx format
    tmp3 += 's';       // add s for seconds
    return tmp3;
  }
    }
  }
  else
  {
    String tmp3 = "      "; // If the time variable is zero pass 6 blanks back as a string...
    return tmp3;
  }
}


void LCDdisplay()
{
  // reset display update flags
  AupDtFlag = 0;
  BupDtFlag = 0;
  // sends 4 lines to LCD using library functions
  lcd.clear();
  lcd.print(Line1);
  lcd.setCursor(0, 1);
  lcd.print(Line2);
  lcd.setCursor(0, 2);
  lcd.print(Line3);
  lcd.setCursor(0, 3);
  lcd.print(Line4);
}

Here’s my thinking and explanation for how TimeConvert() should work and a version of it that should do what I think it should.

Start with nearly smallest time thought possible (in msecs) to illustrate the problem

123

Now add 5 (msecs)

128

Now divide by 10 (get FP result)

12.8

Now truncate by converting to an integer

12

Now convert to a String object (array of ASCII chars)

12

Now find length and set k = length and other indicies

k=2

km1=1

km2=0

Now add a 0 (a placeholder) to the end of the String

120

Note that a 1 is @ index = 0, a 2 @ 1 and a 0 @ 3

Move numbers to insert period, seond from last to last

122

Now third from last to second from last

112

Now replace third from last with period

.12

This is the result we see on the LCD prior to other stuff being added, so we know this works

What we want for this number is :

_0.12s (note the space and zero added to front and an s at the tail, due to forum display I’ll use an _ to show a space)

So if k was = 2 we should add a space and a 0 to front to make (space)0.xx

_0.12

If k = 3 then just add a space, time is already X.xx, will become (space)X.xx

_5.12

If k = 4 then don’t add anything, time is already XX.xx format

55.12

Finally add s to tail to get XX.xxs or (space)X.xxs or (space)0.xxs, all are 6 chars in length

So the following should do the above (look through it). I did change something (now use k) that I think might have been the problem so —

TaaDaaa the really-really-really-really-fixed-this-time code :

String TimeConvert(long time)
{
// takes the time as argument and returns the time for that hit as a XX.xxs string
  if (time != 0) // Make sure there's a value...
  {
    String tmp3 = String(int((time + 5)/10)); // round msecs into csecs
    int k = tmp3.length();  // k will be index to last char in padded string
    int km1 = k-1;          // km1 will be index to next to last char
    int km2 = k-2;          // km2 will be position of period
    tmp3 = tmp3 + '0';  // pad the end of the truncated string with a zero
    //now move chars to make space to add a period
    tmp3.setCharAt(k, tmp3.charAt(km1));   // move next-2-last to last position in string
    tmp3.setCharAt(km1, tmp3.charAt(km2)); // move char to next-2-last position
    // now insert period
    tmp3.setCharAt(km2, '.');
    // add a blank or 2 as needed to get 5 digits with time right justified
    if (k == 2)
    {
      tmp3 = " 0" + tmp3;     // must be a sub 1 sec time so add a blank and a leading 0
    }
    else
    {
      if (k == 3)
      {
        tmp3 = ' ' + tmp3;    // must be a sub 10 sec time so add a blank to justify
      }
    {
    // tmp3 now holds rounded time in secs XX.xx format
    tmp3 += 's':       // add s for seconds
    return tmp3;
  }
  else
  {
    String tmp3 = "      "; // If the time variable is zero pass 6 blanks back as a string...
    return tmp3;
  }
}

sspbass:
The code works, I like how you have it set to not automatically go into the review mode upon completion of “shooting”.

Errrrrrr OK. :oops: It was supposed to. I’ll have to look into why it didn’t.

I can make it work either way, what’s your preference ?

EDIT : That was quick. Yes, it’ll sit on the last hit time display until you press a scroll button. It’s a consequence of never changing the display unless something (a time or button push) has changed from last display update. In the prior code I tried to have the best of both worlds, and have the last shot time linger for a second or so, before the automatic transition to the review display. I lost that (unintentionally) in the move to the “mode” code. I can bring that back if you like or leave it as is ??

i liked it when I had to push a button to review. When practicing I do the “search and asses” routine, de-cock my pistol, and then holster it.

This gives me an opportunity to see how many hits were made in how much time. In the review mode I can only see how many hits were made and the split times, not the overall time.

The really really really fixed this time messed it up bad the first time I ran it (just like video 8 ) but I figured something out.

It only goes haywire when I hit the shock sensor too fast. If I hit at a rate of 1 hit per 1.5 seconds it works good. Any faster than that

and it gets AFU. The formatting itself does look good though.

sspbass:
… but I figured something out. It only goes haywire when I hit the shock sensor too fast. If I hit at a rate of 1 hit per 1.5 seconds it works good. Any faster than that and it gets AFU. The formatting itself does look good though.

Well that’s good and not so good. Good that the formatting is working as designed. Now you can look at it and see what you want it to be. Perhaps no space btw the A and the hit numbers in the 1’st column ? Perhaps no s ? Perhaps …???

Did you notice what display came up when you hit the scroll button ? Looking at the code it probably wasn’t the first, hits 1-8. It should be, shouldn’t it ? I think it was hits 9-18. That can be fixed too (if that’s what happens).

As for the hit rate screwing up the display … that ain’t good. Especially if it’s something like needing a second btw hits to be OK. Real times will occur more rapidly than that. I can think of 2 ways that’s happening. Both are due to a hit coming in while the display is being updated, which is likely since that update takes a long time (remember the flickering when the display was being updated every loop?). Either the ISR is screwing up some critical timing of the data transfer to the LCD (I doubt this, the ISR runs for a very short time) or the next write to the LCD occurs before the prior one is done and that messes up the either the LCD.print driver or the LCD itself (most likely). In either case, if this is the cause, we can insert a time test for writing to the LCD. If at least some XYZ msecs haven’t passed since the last write, then don’t do the pending write until those msecs have passed. I don’t think there’s even a need for a buffer. If the hits are happening too fast to be displayed it won’t matter if you don’t send hits 2-4 and end up seeing #5.

I wonder why we didn’t experience this before now ?

Lastly I do think I should add in some short time safety code into the TimeConvert() just in case there’s ever a sub 99 msec time difference between hits. I don’t think a real split could ever be that short (short of a full auto burst) but just in case …

So 3 fixes needed at some point and perhaps a mod to the display to make it puuurty. :mrgreen:

Here’s a trial fix for the “too quick” problem. I added a new variable (LastTime), a new constant (LCDtime) and a test in the LCD write routine. Now if a write to the LCD tries to happen sooner than LCDtime (presently 500 msec) it doesn’t happen. Instead it get’s put off until LCDtime has passed. If a new hit comes in while that write is pending, it get’s dropped and the newer write happens. See if this still works in non-stressing conditions and then stress it with rapid fire and see if the LCD problem comes back.

Just the parts with the 2 new items.

// 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
const unsigned long LCDtime = 500;       // Set min time btw writes to LCD to 500 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
long A_splits[MaxHits];
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

And the new LCD “write” …

void LCDdisplay()
{
  if (millis() - LastTime > LCDtime)
  {
    LastTime = millis();
    // reset display update flags
    AupDtFlag = 0;
    BupDtFlag = 0;
    // sends 4 lines to LCD using library functions
    lcd.clear();
    lcd.print(Line1);
    lcd.setCursor(0, 1);
    lcd.print(Line2);
    lcd.setCursor(0, 2);
    lcd.print(Line3);
    lcd.setCursor(0, 3);
    lcd.print(Line4);
  }
}

Assuming the above works (or even if it doesn’t) here’s a cut at an “A vs B” mode. Replace whatever loop() and FormatData() are working and try these in their place. See if it still works for just A hits first. See if you notice the display difference and if you like it. Then find the line that says :

unsigned int TimerMode = 0; // initialize the mode to be single shooter

… and change TimerMode = 1.

unsigned int TimerMode = 1; // initialize the mode to be A vs B

Now it’ll be in that mode. Try just A hits first to see if that still works. Then perhaps just some B hits (change wiring from hit detector going to pin2 to go to pin3 instead). Check out the usual; start and stop and scrolling. Then it’ll be very interesting to wire your hit detector into both pins 2 and 3 simultaneously. Now you’ve got two robotically matched shooters. :mrgreen: If you have a separate hit detector give that a try as well (to get differing A vs B times and counts). If you don’t have another hit detector, you can simulate one by shorting pin3 to ground intermittantly.

void loop()
{
  if (TimerState) // is timer running
  {
    // timer is running
    // set display according to timer mode
    switch (TimerMode)
    {
      case 1:{
        //this is for A vs B shooter mode
        DisplayIndex = 11;
        break;}
      case 2:{
        //this is for ??? mode
        // set DisplayIndex = something here
        break;}
      case 3:{
        //this is for ???? mode
        // set DisplayIndex = something here
        break;}
      default: 
        // the default = single A shooter mode
        DisplayIndex = 1;   // display will show count and time of hits
    }
    
    // send data to display if there have been any hits
    if (AupDtFlag == 1 || BupDtFlag == 1)
    {
      CalcTimes();
      FormatData();
      LCDdisplay();
    }
    
    // timer is running so now check for any stop conditions
    if ((digitalRead(StartPin) == LOW) || ((millis() - StartTime) > MaxTime) || A_count >= MaxHits || B_count >= MaxHits)
    {
      // 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();
        LCDdisplay();
      }
      
      // 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 = 11;
          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 ??? mode
          // set DisplayIndex = something here
          break;}
        case 3:{
          //this is for ???? mode
          // set DisplayIndex = something here
          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(ScrollPin) == LOW)
    {
      DisplayIndex++;             // increment DisplayIndex upon push
      if (DisplayIndex > diMax)
      {
        DisplayIndex = diMin;     // wrap around of scrolling action
      }
      // change to new display
      FormatData();
      LCDdisplay();
      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();
      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 = 1;
      
      // now buzz the speaker for 0.5 secs
      tone(BuzzerPin, FreqHi, BuzzTime5);
    }
  }
}
void FormatData()
{
// routine to format lines of data to be sent to LCD
// presently assume 4 lines of 20 characters
// switch formatting done based on display state
  switch (DisplayIndex)
  {
    case 1:{
      //this is for single A shooter mode
      // display number of hits so far
      String tmp = "A: # hits = ";
      Line1 = tmp + A_count;
      // now display the time of last hit in secs out to hundreths of secs
      String tmp2 = TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
      Line2 = " Hit  time = " + tmp2;
      tmp2 = TimeConvert(A_splits[A_count-1]); // convert split time
      Line3 = "Split time = " + tmp2;
      Line4 = "                    ";
      break;}
    case 2:{
      // this is A review mode hits 1-8
      String tmp = TimeConvert(A_Times[0]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[4]); // convert hit time to XX.xx format
      Line1 = "A 1:" + tmp + "  5:" + tmp2;
      tmp = TimeConvert(A_Times[1]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[5]); // convert hit time to XX.xx format
      Line2 = "A 2:" + tmp + "  6:" + tmp2;
      tmp = TimeConvert(A_Times[2]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[6]); // convert hit time to XX.xx format
      Line3 = "A 3:" + tmp + "  7:" + tmp2;
      tmp = TimeConvert(A_Times[3]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[7]); // convert hit time to XX.xx format
      Line4 = "A 4:" + tmp + "  8:" + tmp2;
      break;}
    case 3:{
      // this is A review mode hits 9-16
      String tmp = TimeConvert(A_Times[8]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[12]); // convert hit time to XX.xx format
      Line1 = "A 9:" + tmp + " 13:" + tmp2;
      tmp = TimeConvert(A_Times[9]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[13]); // convert hit time to XX.xx format
      Line2 = "A10:" + tmp + " 14:" + tmp2;
      tmp = TimeConvert(A_Times[10]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[14]); // convert hit time to XX.xx format
      Line3 = "A11:" + tmp + " 15:" + tmp2;
      tmp = TimeConvert(A_Times[11]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[15]); // convert hit time to XX.xx format
      Line4 = "A12:" + tmp + " 16:" + tmp2;
      break;}
    case 4:{
      // this is A review mode hits 17-20
      String tmp = TimeConvert(A_Times[16]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[18]); // convert hit time to XX.xx format
      Line1 = "A17:" + tmp + " 19:" + tmp2;
      tmp = TimeConvert(A_Times[17]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[19]); // convert hit time to XX.xx format
      Line2 = "A18:" + tmp + " 20:" + tmp2;
      Line3 = "                    ";
      Line4 = "                    ";
      break;}
    case 10:{
      //this is for AvsB shooter mode
      // display number of hits so far
      String tmp = "A: # hits = ";
      Line1 = tmp + A_count;
      // now display the time of last hit in secs out to hundreths of secs
      String tmp2 = TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
      Line2 = "A Hit time = " + tmp2;
      tmp2 = TimeConvert(A_splits[A_count-1]); // convert split time
      String tmp = "B: # hits = ";
      Line3 = tmp + B_count;
      // now display the time of last hit in secs out to hundreths of secs
      String tmp2 = TimeConvert(B_Times[B_count-1]); // convert hit time to XX.xx format
      Line4 = "B Hit time = " + tmp2;
      break;}
    case 11:{
      // this is AvsB review mode hits 1-4
      String tmp = TimeConvert(A_Times[0]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(B_Times[0]); // convert hit time to XX.xx format
      Line1 = "A 1:" + tmp + "B 1:" + tmp2;
      tmp = TimeConvert(A_Times[1]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[1]); // convert hit time to XX.xx format
      Line2 = "A 2:" + tmp + "B 2:" + tmp2;
      tmp = TimeConvert(A_Times[2]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[2]); // convert hit time to XX.xx format
      Line3 = "A 3:" + tmp + "B 3:" + tmp2;
      tmp = TimeConvert(A_Times[3]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[3]); // convert hit time to XX.xx format
      Line4 = "A 4:" + tmp + "B 4:" + tmp2;
      break;}
    case 12:{
      // this is A review mode hits 5-8
      String tmp = TimeConvert(A_Times[4]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(B_Times[4]); // convert hit time to XX.xx format
      Line1 = "A 5:" + tmp + "B 5:" + tmp2;
      tmp = TimeConvert(A_Times[5]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[5]); // convert hit time to XX.xx format
      Line2 = "A 6:" + tmp + "B 6:" + tmp2;
      tmp = TimeConvert(A_Times[6]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[6]); // convert hit time to XX.xx format
      Line3 = "A 7:" + tmp + "B 7:" + tmp2;
      tmp = TimeConvert(A_Times[7]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[7]); // convert hit time to XX.xx format
      Line4 = "A 8:" + tmp + "B 8:" + tmp2;
      break;}
    case 13:{
      // this is A review mode hits 9-12
      String tmp = TimeConvert(A_Times[8]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(B_Times[8]); // convert hit time to XX.xx format
      Line1 = "A 9:" + tmp + "B 9:" + tmp2;
      tmp = TimeConvert(A_Times[9]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[9]); // convert hit time to XX.xx format
      Line2 = "A10:" + tmp + "B10:" + tmp2;
      tmp = TimeConvert(A_Times[10]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[10]); // convert hit time to XX.xx format
      Line3 = "A11:" + tmp + "B11:" + tmp2;
      tmp = TimeConvert(A_Times[11]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[11]); // convert hit time to XX.xx format
      Line4 = "A12:" + tmp + "B12:" + tmp2;
      break;}
    case 14:{
      // this is A review mode hits 13-16
      String tmp = TimeConvert(A_Times[12]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(B_Times[12]); // convert hit time to XX.xx format
      Line1 = "A13:" + tmp + "B13:" + tmp2;
      tmp = TimeConvert(A_Times[13]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[13]); // convert hit time to XX.xx format
      Line2 = "A14:" + tmp + "B14:" + tmp2;
      tmp = TimeConvert(A_Times[14]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[14]); // convert hit time to XX.xx format
      Line3 = "A15:" + tmp + "B15:" + tmp2;
      tmp = TimeConvert(A_Times[15]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[15]); // convert hit time to XX.xx format
      Line4 = "A16:" + tmp + "B16:" + tmp2;
      break;}
    case 15:{
      // this is A review mode hits 17-20
      String tmp = TimeConvert(A_Times[16]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(B_Times[16]); // convert hit time to XX.xx format
      Line1 = "A17:" + tmp + "B17:" + tmp2;
      tmp = TimeConvert(A_Times[17]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[17]); // convert hit time to XX.xx format
      Line2 = "A18:" + tmp + "B18:" + tmp2;
      tmp = TimeConvert(A_Times[18]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[18]); // convert hit time to XX.xx format
      Line3 = "A19:" + tmp + "B19:" + tmp2;
      tmp = TimeConvert(A_Times[19]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[19]); // convert hit time to XX.xx format
      Line4 = "A20:" + tmp + "B20:" + tmp2;
      break;}
    default: 
      // do the default = 0
      // 4 string objects
      Line1 = "    Shot Timer v1   ";
      Line2 = "     is running     ";
      Line3 = "      Shooter:      ";
      Line4 = "    --STANDBY--     ";
  }
}

I think I’ll be having some pizza and beer !

OK a few beers down and it occured to me …

Back in Vid7 you were whacking on the sensor like a monkey on crack throws poo … very rapidly indeed ! Back then the LCD did not get FU’ed despite hit splits much less than 1 sec. Now it does. What changed was the revision to implement “modes”. So I don’t think it’s the LCD or it’s driver that’s the root cause but some else inherent in the new arrangement of code. Perhaps another few beers will make it clear what it was that made this happen.

In any case give the AvsB a shot.

Mee_n_Mac:
Here’s a trial fix for the “too quick” problem. I added a new variable (LastTime), a new constant (LCDtime) and a test in the LCD write routine. Now if a write to the LCD tries to happen sooner than LCDtime (presently 500 msec) it doesn’t happen. Instead it get’s put off until LCDtime has passed. If a new hit comes in while that write is pending, it get’s dropped and the newer write happens. See if this still works in non-stressing conditions and then stress it with rapid fire and see if the LCD problem comes back.

Just the parts with the 2 new items.

// 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
const unsigned long LCDtime = 500; // Set min time btw writes to LCD to 500 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
long A_splits[MaxHits];
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




And the new LCD "write" ...


void LCDdisplay()
{
if (millis() - LastTime > LCDtime)
{
LastTime = millis();
// reset display update flags
AupDtFlag = 0;
BupDtFlag = 0;
// sends 4 lines to LCD using library functions
lcd.clear();
lcd.print(Line1);
lcd.setCursor(0, 1);
lcd.print(Line2);
lcd.setCursor(0, 2);
lcd.print(Line3);
lcd.setCursor(0, 3);
lcd.print(Line4);
}
}

This still has the speed issue.

Mee_n_Mac:
Assuming the above works (or even if it doesn’t) here’s a cut at an “A vs B” mode. Replace whatever loop() and FormatData() are working and try these in their place. See if it still works for just A hits first. See if you notice the display difference and if you like it. Then find the line that says :

unsigned int TimerMode = 0; // initialize the mode to be single shooter

… and change TimerMode = 1.

unsigned int TimerMode = 1; // initialize the mode to be A vs B

Now it’ll be in that mode. Try just A hits first to see if that still works. Then perhaps just some B hits (change wiring from hit detector going to pin2 to go to pin3 instead). Check out the usual; start and stop and scrolling. Then it’ll be very interesting to wire your hit detector into both pins 2 and 3 simultaneously. Now you’ve got two robotically matched shooters. :mrgreen: If you have a separate hit detector give that a try as well (to get differing A vs B times and counts). If you don’t have another hit detector, you can simulate one by shorting pin3 to ground intermittantly.

void loop()

{
if (TimerState) // is timer running
{
// timer is running
// set display according to timer mode
switch (TimerMode)
{
case 1:{
//this is for A vs B shooter mode
DisplayIndex = 11;
break;}
case 2:{
//this is for ??? mode
// set DisplayIndex = something here
break;}
case 3:{
//this is for ??? mode
// set DisplayIndex = something here
break;}
default:
// the default = single A shooter mode
DisplayIndex = 1; // display will show count and time of hits
}

// send data to display if there have been any hits
if (AupDtFlag == 1 || BupDtFlag == 1)
{
  CalcTimes();
  FormatData();
  LCDdisplay();
}

// timer is running so now check for any stop conditions
if ((digitalRead(StartPin) == LOW) || ((millis() - StartTime) > MaxTime) || A_count >= MaxHits || B_count >= MaxHits)
{
  // 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();
    LCDdisplay();
  }
  
  // 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 = 11;
      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 ??? mode
      // set DisplayIndex = something here
      break;}
    case 3:{
      //this is for ???? mode
      // set DisplayIndex = something here
      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(ScrollPin) == LOW)
{
  DisplayIndex++;             // increment DisplayIndex upon push
  if (DisplayIndex > diMax)
  {
    DisplayIndex = diMin;     // wrap around of scrolling action
  }
  // change to new display
  FormatData();
  LCDdisplay();
  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();
  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 = 1;
  
  // now buzz the speaker for 0.5 secs
  tone(BuzzerPin, FreqHi, BuzzTime5);
}

}
}






void FormatData()
{
// routine to format lines of data to be sent to LCD
// presently assume 4 lines of 20 characters
// switch formatting done based on display state
switch (DisplayIndex)
{
case 1:{
//this is for single A shooter mode
// display number of hits so far
String tmp = “A: # hits = “;
Line1 = tmp + A_count;
// now display the time of last hit in secs out to hundreths of secs
String tmp2 = TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
Line2 = " Hit time = " + tmp2;
tmp2 = TimeConvert(A_splits[A_count-1]); // convert split time
Line3 = “Split time = " + tmp2;
Line4 = " “;
break;}
case 2:{
// this is A review mode hits 1-8
String tmp = TimeConvert(A_Times[0]); // convert hit time to XX.xx format
String tmp2 = TimeConvert(A_Times[4]); // convert hit time to XX.xx format
Line1 = “A 1:” + tmp + " 5:” + tmp2;
tmp = TimeConvert(A_Times[1]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[5]); // convert hit time to XX.xx format
Line2 = “A 2:” + tmp + " 6:” + tmp2;
tmp = TimeConvert(A_Times[2]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[6]); // convert hit time to XX.xx format
Line3 = “A 3:” + tmp + " 7:” + tmp2;
tmp = TimeConvert(A_Times[3]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[7]); // convert hit time to XX.xx format
Line4 = “A 4:” + tmp + " 8:” + tmp2;
break;}
case 3:{
// this is A review mode hits 9-16
String tmp = TimeConvert(A_Times[8]); // convert hit time to XX.xx format
String tmp2 = TimeConvert(A_Times[12]); // convert hit time to XX.xx format
Line1 = “A 9:” + tmp + " 13:" + tmp2;
tmp = TimeConvert(A_Times[9]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[13]); // convert hit time to XX.xx format
Line2 = “A10:” + tmp + " 14:" + tmp2;
tmp = TimeConvert(A_Times[10]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[14]); // convert hit time to XX.xx format
Line3 = “A11:” + tmp + " 15:" + tmp2;
tmp = TimeConvert(A_Times[11]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[15]); // convert hit time to XX.xx format
Line4 = “A12:” + tmp + " 16:" + tmp2;
break;}
case 4:{
// this is A review mode hits 17-20
String tmp = TimeConvert(A_Times[16]); // convert hit time to XX.xx format
String tmp2 = TimeConvert(A_Times[18]); // convert hit time to XX.xx format
Line1 = “A17:” + tmp + " 19:" + tmp2;
tmp = TimeConvert(A_Times[17]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[19]); // convert hit time to XX.xx format
Line2 = “A18:” + tmp + " 20:" + tmp2;
Line3 = " ";
Line4 = " ";
break;}
case 10:{
//this is for AvsB shooter mode
// display number of hits so far
String tmp = "A: # hits = ";
Line1 = tmp + A_count;
// now display the time of last hit in secs out to hundreths of secs
String tmp2 = TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
Line2 = "A Hit time = " + tmp2;
tmp2 = TimeConvert(A_splits[A_count-1]); // convert split time
String tmp = "B: # hits = ";
Line3 = tmp + B_count;
// now display the time of last hit in secs out to hundreths of secs
String tmp2 = TimeConvert(B_Times[B_count-1]); // convert hit time to XX.xx format
Line4 = "B Hit time = " + tmp2;
break;}
case 11:{
// this is AvsB review mode hits 1-4
String tmp = TimeConvert(A_Times[0]); // convert hit time to XX.xx format
String tmp2 = TimeConvert(B_Times[0]); // convert hit time to XX.xx format
Line1 = “A 1:” + tmp + “B 1:” + tmp2;
tmp = TimeConvert(A_Times[1]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[1]); // convert hit time to XX.xx format
Line2 = “A 2:” + tmp + “B 2:” + tmp2;
tmp = TimeConvert(A_Times[2]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[2]); // convert hit time to XX.xx format
Line3 = “A 3:” + tmp + “B 3:” + tmp2;
tmp = TimeConvert(A_Times[3]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[3]); // convert hit time to XX.xx format
Line4 = “A 4:” + tmp + “B 4:” + tmp2;
break;}
case 12:{
// this is A review mode hits 5-8
String tmp = TimeConvert(A_Times[4]); // convert hit time to XX.xx format
String tmp2 = TimeConvert(B_Times[4]); // convert hit time to XX.xx format
Line1 = “A 5:” + tmp + “B 5:” + tmp2;
tmp = TimeConvert(A_Times[5]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[5]); // convert hit time to XX.xx format
Line2 = “A 6:” + tmp + “B 6:” + tmp2;
tmp = TimeConvert(A_Times[6]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[6]); // convert hit time to XX.xx format
Line3 = “A 7:” + tmp + “B 7:” + tmp2;
tmp = TimeConvert(A_Times[7]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[7]); // convert hit time to XX.xx format
Line4 = “A 8:” + tmp + “B 8:” + tmp2;
break;}
case 13:{
// this is A review mode hits 9-12
String tmp = TimeConvert(A_Times[8]); // convert hit time to XX.xx format
String tmp2 = TimeConvert(B_Times[8]); // convert hit time to XX.xx format
Line1 = “A 9:” + tmp + “B 9:” + tmp2;
tmp = TimeConvert(A_Times[9]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[9]); // convert hit time to XX.xx format
Line2 = “A10:” + tmp + “B10:” + tmp2;
tmp = TimeConvert(A_Times[10]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[10]); // convert hit time to XX.xx format
Line3 = “A11:” + tmp + “B11:” + tmp2;
tmp = TimeConvert(A_Times[11]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[11]); // convert hit time to XX.xx format
Line4 = “A12:” + tmp + “B12:” + tmp2;
break;}
case 14:{
// this is A review mode hits 13-16
String tmp = TimeConvert(A_Times[12]); // convert hit time to XX.xx format
String tmp2 = TimeConvert(B_Times[12]); // convert hit time to XX.xx format
Line1 = “A13:” + tmp + “B13:” + tmp2;
tmp = TimeConvert(A_Times[13]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[13]); // convert hit time to XX.xx format
Line2 = “A14:” + tmp + “B14:” + tmp2;
tmp = TimeConvert(A_Times[14]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[14]); // convert hit time to XX.xx format
Line3 = “A15:” + tmp + “B15:” + tmp2;
tmp = TimeConvert(A_Times[15]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[15]); // convert hit time to XX.xx format
Line4 = “A16:” + tmp + “B16:” + tmp2;
break;}
case 15:{
// this is A review mode hits 17-20
String tmp = TimeConvert(A_Times[16]); // convert hit time to XX.xx format
String tmp2 = TimeConvert(B_Times[16]); // convert hit time to XX.xx format
Line1 = “A17:” + tmp + “B17:” + tmp2;
tmp = TimeConvert(A_Times[17]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[17]); // convert hit time to XX.xx format
Line2 = “A18:” + tmp + “B18:” + tmp2;
tmp = TimeConvert(A_Times[18]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[18]); // convert hit time to XX.xx format
Line3 = “A19:” + tmp + “B19:” + tmp2;
tmp = TimeConvert(A_Times[19]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[19]); // convert hit time to XX.xx format
Line4 = “A20:” + tmp + “B20:” + tmp2;
break;}
default:
// do the default = 0
// 4 string objects
Line1 = " Shot Timer v1 ";
Line2 = " is running ";
Line3 = " Shooter: ";
Line4 = " --STANDBY-- ";
}
}



I think I'll be having some pizza and beer !

Back to this compile error. I thought we (you) took care of this?

sspbass:
Back to this compile error. I thought we (you) took care of this?

Yup. That’s a copy’n’paste error that came in with the new AvsB displays. The following should remedy that. I still don’t know what to think about the speed problem. That came in with what I thought would be a minor change, certainly not something I can see causing the problem we’re seeing. But let’s get the AvsB working whilst that’s stewing. I may offer up a rev that reverts back a step just to pin down the statements that I think are causing that problem.

The fix for the compiler error …

void FormatData()
{
// routine to format lines of data to be sent to LCD
// presently assume 4 lines of 20 characters
// switch formatting done based on display state
  switch (DisplayIndex)
  {
    case 1:{
      //this is for single A shooter mode
      // display number of hits so far
      String tmp = "A: # hits = ";
      Line1 = tmp + A_count;
      // now display the time of last hit in secs out to hundreths of secs
      String tmp2 = TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
      Line2 = " Hit  time = " + tmp2;
      tmp2 = TimeConvert(A_splits[A_count-1]); // convert split time
      Line3 = "Split time = " + tmp2;
      Line4 = "                    ";
      break;}
    case 2:{
      // this is A review mode hits 1-8
      String tmp = TimeConvert(A_Times[0]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[4]); // convert hit time to XX.xx format
      Line1 = "A 1:" + tmp + "  5:" + tmp2;
      tmp = TimeConvert(A_Times[1]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[5]); // convert hit time to XX.xx format
      Line2 = "A 2:" + tmp + "  6:" + tmp2;
      tmp = TimeConvert(A_Times[2]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[6]); // convert hit time to XX.xx format
      Line3 = "A 3:" + tmp + "  7:" + tmp2;
      tmp = TimeConvert(A_Times[3]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[7]); // convert hit time to XX.xx format
      Line4 = "A 4:" + tmp + "  8:" + tmp2;
      break;}
    case 3:{
      // this is A review mode hits 9-16
      String tmp = TimeConvert(A_Times[8]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[12]); // convert hit time to XX.xx format
      Line1 = "A 9:" + tmp + " 13:" + tmp2;
      tmp = TimeConvert(A_Times[9]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[13]); // convert hit time to XX.xx format
      Line2 = "A10:" + tmp + " 14:" + tmp2;
      tmp = TimeConvert(A_Times[10]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[14]); // convert hit time to XX.xx format
      Line3 = "A11:" + tmp + " 15:" + tmp2;
      tmp = TimeConvert(A_Times[11]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[15]); // convert hit time to XX.xx format
      Line4 = "A12:" + tmp + " 16:" + tmp2;
      break;}
    case 4:{
      // this is A review mode hits 17-20
      String tmp = TimeConvert(A_Times[16]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(A_Times[18]); // convert hit time to XX.xx format
      Line1 = "A17:" + tmp + " 19:" + tmp2;
      tmp = TimeConvert(A_Times[17]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(A_Times[19]); // convert hit time to XX.xx format
      Line2 = "A18:" + tmp + " 20:" + tmp2;
      Line3 = "                    ";
      Line4 = "                    ";
      break;}
    case 10:{
      //this is for AvsB shooter mode
      // display number of hits so far
      String tmp = "A: # hits = ";
      Line1 = tmp + A_count;
      // now display the time of last hit in secs out to hundreths of secs
      String tmp2 = TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
      Line2 = "A Hit time = " + tmp2;
      tmp2 = TimeConvert(A_splits[A_count-1]); // convert split time
      tmp = "B: # hits = ";
      Line3 = tmp + B_count;
      // now display the time of last hit in secs out to hundreths of secs
      tmp2 = TimeConvert(B_Times[B_count-1]); // convert hit time to XX.xx format
      Line4 = "B Hit time = " + tmp2;
      break;}
    case 11:{
      // this is AvsB review mode hits 1-4
      String tmp = TimeConvert(A_Times[0]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(B_Times[0]); // convert hit time to XX.xx format
      Line1 = "A 1:" + tmp + "B 1:" + tmp2;
      tmp = TimeConvert(A_Times[1]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[1]); // convert hit time to XX.xx format
      Line2 = "A 2:" + tmp + "B 2:" + tmp2;
      tmp = TimeConvert(A_Times[2]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[2]); // convert hit time to XX.xx format
      Line3 = "A 3:" + tmp + "B 3:" + tmp2;
      tmp = TimeConvert(A_Times[3]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[3]); // convert hit time to XX.xx format
      Line4 = "A 4:" + tmp + "B 4:" + tmp2;
      break;}
    case 12:{
      // this is A review mode hits 5-8
      String tmp = TimeConvert(A_Times[4]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(B_Times[4]); // convert hit time to XX.xx format
      Line1 = "A 5:" + tmp + "B 5:" + tmp2;
      tmp = TimeConvert(A_Times[5]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[5]); // convert hit time to XX.xx format
      Line2 = "A 6:" + tmp + "B 6:" + tmp2;
      tmp = TimeConvert(A_Times[6]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[6]); // convert hit time to XX.xx format
      Line3 = "A 7:" + tmp + "B 7:" + tmp2;
      tmp = TimeConvert(A_Times[7]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[7]); // convert hit time to XX.xx format
      Line4 = "A 8:" + tmp + "B 8:" + tmp2;
      break;}
    case 13:{
      // this is A review mode hits 9-12
      String tmp = TimeConvert(A_Times[8]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(B_Times[8]); // convert hit time to XX.xx format
      Line1 = "A 9:" + tmp + "B 9:" + tmp2;
      tmp = TimeConvert(A_Times[9]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[9]); // convert hit time to XX.xx format
      Line2 = "A10:" + tmp + "B10:" + tmp2;
      tmp = TimeConvert(A_Times[10]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[10]); // convert hit time to XX.xx format
      Line3 = "A11:" + tmp + "B11:" + tmp2;
      tmp = TimeConvert(A_Times[11]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[11]); // convert hit time to XX.xx format
      Line4 = "A12:" + tmp + "B12:" + tmp2;
      break;}
    case 14:{
      // this is A review mode hits 13-16
      String tmp = TimeConvert(A_Times[12]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(B_Times[12]); // convert hit time to XX.xx format
      Line1 = "A13:" + tmp + "B13:" + tmp2;
      tmp = TimeConvert(A_Times[13]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[13]); // convert hit time to XX.xx format
      Line2 = "A14:" + tmp + "B14:" + tmp2;
      tmp = TimeConvert(A_Times[14]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[14]); // convert hit time to XX.xx format
      Line3 = "A15:" + tmp + "B15:" + tmp2;
      tmp = TimeConvert(A_Times[15]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[15]); // convert hit time to XX.xx format
      Line4 = "A16:" + tmp + "B16:" + tmp2;
      break;}
    case 15:{
      // this is A review mode hits 17-20
      String tmp = TimeConvert(A_Times[16]); // convert hit time to XX.xx format
      String tmp2 = TimeConvert(B_Times[16]); // convert hit time to XX.xx format
      Line1 = "A17:" + tmp + "B17:" + tmp2;
      tmp = TimeConvert(A_Times[17]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[17]); // convert hit time to XX.xx format
      Line2 = "A18:" + tmp + "B18:" + tmp2;
      tmp = TimeConvert(A_Times[18]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[18]); // convert hit time to XX.xx format
      Line3 = "A19:" + tmp + "B19:" + tmp2;
      tmp = TimeConvert(A_Times[19]); // convert hit time to XX.xx format
      tmp2 = TimeConvert(B_Times[19]); // convert hit time to XX.xx format
      Line4 = "A20:" + tmp + "B20:" + tmp2;
      break;}
    default: 
      // do the default = 0
      // 4 string objects
      Line1 = "    Shot Timer v1   ";
      Line2 = "     is running     ";
      Line3 = "      Shooter:      ";
      Line4 = "    --STANDBY--     ";
  }
}