Mee_n_Mac:
DirtyD:
I’m pretty sure String object variables are dynamically allocated. This is usually a good thing in normal applications but embedded ones could cause issues with limited RAM. Thinking about it now since they are dynamically allocated they are probably getting fragmented.
The more I read about people having problems with Strings and concatenation, the more I’m inclined to believe that fragmentation leading to some odd overflow condition is the root cause. Also there seems to be some difference amongst Arduino environments, and thus libraries, as to how well memory is dynamically allocated and released. Apparently the last Arduino release, 1.0, is supposed to be better in these respects.
@sspbass : What Arduino release are you using ?
FWIW I revised the last POS along the lines I mentioned. There’s fewer Strings and fewer hits allowed and so perhaps there will be more free RAM. Even if it does work, it’s not a fix but rather a data point. But if it does run, it’ll allow some progress to be made on getting AvsB working. So give it a try and let’s see what happens.
#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 = 10; // Maximum number if hits allowed
const unsigned long LCDtime = 100; // Set min time btw writes to LCD to 100 msecs
// initialize global variables
volatile byte TimerState = 0; // variable for state of timer, running or not running
volatile byte AupDtFlag = 0; // variable indication an A hit has occurred
volatile byte BupDtFlag = 0; // variable indication a B hit has occurred
byte DisplayIndex = 0; // variable for controlling what’s displayed
unsigned long StartTime = 0; // variable to hold the start time
unsigned long BuzzTime = 500; // variable to hold the buzzer on time
unsigned int Freq = 2000; // variable for high or low buzzer tone
volatile byte A_count = 0; // variable to hold the number of A hits
volatile byte B_count = 0; // variable to hold the number of B hits
volatile long A_Times[MaxHits]; // array to hold up to MaxHits hit times for target A
volatile long B_Times[MaxHits]; // array to hold up to MaxHits hit times for target B
unsigned long A_splits[MaxHits];
unsigned long B_splits[MaxHits];
long AB_splits[MaxHits];
unsigned long LastTime = 0; // initialize last LCD display time to zero for 1st pass
float A_Hit_Factor = 0;
float B_Hit_Factor = 0;
byte S1 = 0; // Switch 1 init to zero
byte S2 = 0; // Switch 2 init to zero
unsigned int diMin = 0; // Display index mininmim
unsigned int diMax = 99; // Display index maximum
unsigned int TimerMode = 0; // initialize the mode to be single shooter
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
DisplayIndex = 5;
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();
}
// 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();
}
// just for the moment send times to PC for debug
SendTimes();
// delay enough to debounce stop button
delay(DB_delay);
// Change to review-stopped state display based on timer mode
switch (TimerMode)
{
case 1:
//this is for A vs B shooter mode
diMin = 6;
diMax = int(max(A_count,B_count)/4) + diMin - 1; // find how many whole screens
if (max(A_count,B_count) % 4)
{
diMax++ ; // add another screen if partial exists
}
DisplayIndex = diMax; // display will show list of hit times
break;
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();
delay(DB_delay);
}
// check to see if start button has been pushed
if (digitalRead(StartPin) == LOW)
{
// start button pushed
// Do the following debug code only to get system running
// This will send message to PC to show button was pushed
Serial.println("Timer is running");
// turn on the LED to show timer is running
digitalWrite(LEDPin, HIGH);
// clear all the prior runs data, reset the display
ClearData();
DisplayIndex = 0;
FormatData();
// delay the Wait Time from start button push
// this delay is for debounce purposes and should stay
delay(DB_delay);
// this delay will change to random later on
delay(WaitTime);
// enable the interrupts 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 = “;
String Line = tmp + A_count;
lcd.clear();
lcd.print(Line);
// now display the time of last hit in secs out to hundreths of secs
tmp = TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
Line = " Hit time = " + tmp;
lcd.setCursor(0, 1);
lcd.print(Line);
tmp = TimeConvert(A_splits[A_count-1]); // convert split time
Line = “Split time = " + tmp;
lcd.setCursor(0, 2);
lcd.print(Line);
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
String Line = “A 1:” + tmp + " 5:” + tmp2;
lcd.clear();
lcd.print(Line)
tmp = TimeConvert(A_Times[1]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[5]); // convert hit time to XX.xx format
Line = “A 2:” + tmp + " 6:” + tmp2;
lcd.setCursor(0, 1);
lcd.print(Line);
tmp = TimeConvert(A_Times[2]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[6]); // convert hit time to XX.xx format
Line = “A 3:” + tmp + " 7:” + tmp2;
lcd.setCursor(0, 2);
lcd.print(Line);
tmp = TimeConvert(A_Times[3]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[7]); // convert hit time to XX.xx format
Line = “A 4:” + tmp + " 8:" + tmp2;
lcd.setCursor(0, 3);
lcd.print(Line);
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
String Line = “A 9:” + tmp + " 13:" + tmp2;
lcd.clear();
lcd.print(Line)
tmp = TimeConvert(A_Times[9]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[13]); // convert hit time to XX.xx format
Line = “A10:” + tmp + " 14:" + tmp2;
lcd.setCursor(0, 1);
lcd.print(Line);
tmp = TimeConvert(A_Times[10]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[14]); // convert hit time to XX.xx format
Line = “A11:” + tmp + " 15:" + tmp2;
lcd.setCursor(0, 2);
lcd.print(Line);
tmp = TimeConvert(A_Times[11]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[15]); // convert hit time to XX.xx format
Line = “A12:” + tmp + " 16:" + tmp2;
lcd.setCursor(0, 3);
lcd.print(Line);
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
String Line = “A17:” + tmp + " 19:" + tmp2;
lcd.clear();
lcd.print(Line)
tmp = TimeConvert(A_Times[17]); // convert hit time to XX.xx format
tmp2 = TimeConvert(A_Times[19]); // convert hit time to XX.xx format
Line = “A18:” + tmp + " 20:" + tmp2;
lcd.setCursor(0, 1);
lcd.print(Line);
break;
case 5:
//this is for AvsB shooter mode
// display number of hits so far
String tmp = "A: # hits = ";
String Line = tmp + A_count;
lcd.clear();
lcd.print(Line)
// now display the time of last hit in secs out to hundreths of secs
tmp = TimeConvert(A_Times[A_count-1]); // convert hit time to XX.xx format
Line = "A Hit time = " + tmp;
lcd.setCursor(0, 1);
lcd.print(Line);
tmp = "B: # hits = ";
Line = tmp + B_count;
lcd.setCursor(0, 2);
lcd.print(Line);
// now display the time of last hit in secs out to hundreths of secs
tmp = TimeConvert(B_Times[B_count-1]); // convert hit time to XX.xx format
Line = “B Hit time = " + tmp;
lcd.setCursor(0, 3);
lcd.print(Line);
break;
case 6:
// 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
String Line = “A 1:” + tmp + “B 1:” + tmp2;
lcd.clear();
lcd.print(Line);
tmp = TimeConvert(A_Times[1]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[1]); // convert hit time to XX.xx format
Line = “A 2:” + tmp + “B 2:” + tmp2;
lcd.setCursor(0, 1);
lcd.print(Line);
tmp = TimeConvert(A_Times[2]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[2]); // convert hit time to XX.xx format
Line = “A 3:” + tmp + “B 3:” + tmp2;
lcd.setCursor(0, 2);
lcd.print(Line);
tmp = TimeConvert(A_Times[3]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[3]); // convert hit time to XX.xx format
Line = “A 4:” + tmp + “B 4:” + tmp2;
lcd.setCursor(0, 3);
lcd.print(Line);
break;
case 7:
// 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
String Line = “A 5:” + tmp + “B 5:” + tmp2;
lcd.clear();
lcd.print(Line);
tmp = TimeConvert(A_Times[5]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[5]); // convert hit time to XX.xx format
Line = “A 6:” + tmp + “B 6:” + tmp2;
lcd.setCursor(0, 1);
lcd.print(Line);
tmp = TimeConvert(A_Times[6]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[6]); // convert hit time to XX.xx format
Line = “A 7:” + tmp + “B 7:” + tmp2;
lcd.setCursor(0, 2);
lcd.print(Line);
tmp = TimeConvert(A_Times[7]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[7]); // convert hit time to XX.xx format
Line = “A 8:” + tmp + “B 8:” + tmp2;
lcd.setCursor(0, 3);
lcd.print(Line);
break;
case 8:
// 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
String Line = “A 9:” + tmp + “B 9:” + tmp2;
lcd.clear();
lcd.print(Line);
tmp = TimeConvert(A_Times[9]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[9]); // convert hit time to XX.xx format
Line = “A10:” + tmp + “B10:” + tmp2;
lcd.setCursor(0, 1);
lcd.print(Line);
tmp = TimeConvert(A_Times[10]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[10]); // convert hit time to XX.xx format
Line = “A11:” + tmp + “B11:” + tmp2;
lcd.setCursor(0, 2);
lcd.print(Line);
tmp = TimeConvert(A_Times[11]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[11]); // convert hit time to XX.xx format
Line = “A12:” + tmp + “B12:” + tmp2;
lcd.setCursor(0, 3);
lcd.print(Line);
break;
case 9:
// 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
String Line = “A13:” + tmp + “B13:” + tmp2;
lcd.clear();
lcd.print(Line);
tmp = TimeConvert(A_Times[13]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[13]); // convert hit time to XX.xx format
Line = “A14:” + tmp + “B14:” + tmp2;
lcd.setCursor(0, 1);
lcd.print(Line);
tmp = TimeConvert(A_Times[14]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[14]); // convert hit time to XX.xx format
Line = “A15:” + tmp + “B15:” + tmp2;
lcd.setCursor(0, 2);
lcd.print(Line);
tmp = TimeConvert(A_Times[15]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[15]); // convert hit time to XX.xx format
Line = “A16:” + tmp + “B16:” + tmp2;
lcd.setCursor(0, 3);
lcd.print(Line);
break;
case 10:
// 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
String Line = “A17:” + tmp + “B17:” + tmp2;
lcd.clear();
lcd.print(Line);
tmp = TimeConvert(A_Times[17]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[17]); // convert hit time to XX.xx format
Line = “A18:” + tmp + “B18:” + tmp2;
lcd.setCursor(0, 1);
lcd.print(Line);
tmp = TimeConvert(A_Times[18]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[18]); // convert hit time to XX.xx format
Line = “A19:” + tmp + “B19:” + tmp2;
lcd.setCursor(0, 2);
lcd.print(Line);
tmp = TimeConvert(A_Times[19]); // convert hit time to XX.xx format
tmp2 = TimeConvert(B_Times[19]); // convert hit time to XX.xx format
Line = “A20:” + tmp + “B20:” + tmp2;
lcd.setCursor(0, 3);
lcd.print(Line);
break;
default:
// do the default = 0
// 4 string objects
lcd.clear();
lcd.print(” Shot Timer v1 “);
lcd.setCursor(0, 1);
lcd.print(” is running “);
lcd.setCursor(0, 2);
lcd.print(” Shooter: “);
lcd.setCursor(0, 3);
lcd.print(” --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 (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;
}
}
This code registers the first hit but no time and it locks up.
Starts like normal, buzzer goes. Strike the sensor and the screen changes to “A: # hits = 1”
and that’s it. it doesn’t time out, it won’t max out hits. The serial monitor just displays “Timer is running”.