Here is my entire sketch. This is my first Arduino project. I included all of it because I don't know what's relevant. I am printing out to a 4-digit 7 segment display. To do that I use sprintf to format the contents of a char[] called tempString and that gets sent to the display. I declared a new local char[] each time I needed one. I had a problem with the Arduino Pro Mini running out of memory. I tried declaring a global chart[] and reusing it when I needed it, but that changed the way the program executed. That was the only change I made.
After I made that change, the Arduino would always evaluate this true:
if (countDownTimer.TimeCheck(0, 0, 0))
The CountDownTimer was always evaluating to a time of 0, even after it had been assigned a valid time and the timer hadn't started running. I verified I was setting it correctly, but by the time the conditional was reached, the Timer was back to 0.
If I remove the global declaration for tempString[] and go back to locals the logic works as designed. I made tempString[] smaller as a local and that seems to have fixed the memory issue but I would like to know why declaring a global caused a problem.
#include <SoftwareSerial.h>
#include <CountUpDownTimer.h>
#include <EEPROM.h> //Needed to access the eeprom read write functions
//values for EEPROM
int stepsToWrite = 0;
int stepsToRead = 0;
int priorValue = 0;
// addresses to write to and read from
const int thirtySecondAddress = 0; // constant
const int threeMinuteAddress = 4; // constant
int addressToUse = 0; // this will change depending on time user selects
// set uo the serial display
const int displayTx = 5; // the pins we need to create the software serial instance
const int displayRx = 7; // we will only use Tx
SoftwareSerial s7s(displayRx, displayTx);
//set up the timer
CountUpDownTimer countDownTimer(DOWN);
const long displaychangeInterval = 10000; // in milliseconds
const long countdownInterval = 1000; // in milliseconds
long previousMillis = 0; // for changing between time left and step count
long previousTimerMillis = 0; // for updating the display with the new time
const int thirtySecondTime = 30;
const int threeMinuteTime = 180; // 3 minutes expressed as seconds
int timerStartValue = 0; // needed for some control logic
// booleans to control what is displayed
bool doDisplayStepCount = false;
bool doDisplayCountDown = false;
bool doDisplayHighScore = true; // we want to display this at startup
// set up the buttons
const int timerbuttonPin = 6; // the number of the timer pushbutton pin
const int resetbuttonPin = 4; // the number of the reset button pin
int timerbuttonPushCounter = 0; // counter for the number of button presses
int timerbuttonState = 0; // current state of the button
int timerlastButtonState = 0; // previous state of the button
int resetbuttonPushCounter = 0; // counter for the number of button presses
int resetbuttonState = 0; // current state of the button
int resetlastButtonState = 0; // previous state of the button
int stepsTaken = 0; // the number of steps (jumps)
boolean timerStarted = false;
// for the force resistor
const int FSR_PIN = A0; // Pin connected to FSR/resistor divider
const float VCC = 5.10; // Measured voltage of Ardunio 5V line
const float R_DIV = 3265.0; // Measured resistance of 3.3k resistor
double currentForce = 0;
double previousForce = 0;
boolean previousPos = false;
boolean currentPos = false;
boolean doChangeDisplay = false;
char tempString[4];
void setup() {
boolean debug = true;
if (debug)
{
EEPROMWriteInt(thirtySecondAddress, 0); // zero out EEPROM
EEPROMWriteInt(threeMinuteAddress, 0); // zero out EEPROM
}
// start serial connection
Serial.begin(9600);
// set up pins
pinMode(FSR_PIN, INPUT);
pinMode(timerbuttonPin, INPUT);
pinMode(resetbuttonPin, INPUT);
// serial display
// Must begin s7s software serial at the correct baud rate.
// Clear the display
// The default of the s7s is 9600.
s7s.begin(9600);
//clearDisplay(); // Clears display, resets cursor
// s7s.print("-HI-");
// delay(1000);
displayHighScores();
}
void loop() {
// Serial.print("freeMemory()=");
// Serial.println(freeMemory());
// Serial.println("Starting loop");
// button pushes
// if tinmerbuttonPushCounter = 1, set timerValue to 30 seconds
// if 2 pushes, set to 3 minutes and reset push counter to 0
//Serial.println("Reading timer button, state is: " + timerbuttonState);
timerbuttonState = digitalRead(timerbuttonPin);
// Serial.println("Reading reset button, state is: " + resetbuttonState);
resetbuttonState = digitalRead(resetbuttonPin);
// compare the buttonState to its previous state
if (timerbuttonState != timerlastButtonState)
{
Serial.println("selecting time");
if (timerbuttonState == HIGH)
{
doDisplayHighScore = false;
timerbuttonPushCounter++;
// if the current state is HIGH then the timer button went from off to on:
if (timerbuttonPushCounter == 1)
{
doChangeDisplay = false;
doDisplayCountDown = true;
timerStartValue = thirtySecondTime;
addressToUse = thirtySecondAddress;
Serial.println("timer button pushed: " + String(timerbuttonPushCounter));
Serial.println("start value: " + String(timerStartValue));
clearDisplay();
setDecimals(0b010000); // Turn on colon
sprintf(tempString, "%04d", thirtySecondTime);
Serial.println("Timer set to: " + String(tempString));
s7s.print(tempString);
}
else if (timerbuttonPushCounter == 2)
{
timerStartValue = threeMinuteTime;
addressToUse = threeMinuteAddress;
doChangeDisplay = true;
int timerinminutes = timerStartValue / 60;
Serial.println("timer button pushed: " + timerbuttonPushCounter);
Serial.println("start value " + timerStartValue);
timerbuttonPushCounter = 0;
clearDisplay();
setDecimals(0b010000); // Turn on colon
sprintf(tempString, "0%d00", timerinminutes);
Serial.println("Timer set to: " + String(tempString));
s7s.print(tempString);
}
} // end of if timer state high
// Delay a little bit to avoid bouncing
delay(50);
}
// save the current state as the last state, for next time through the loop
timerlastButtonState = timerbuttonState;
// for reset button, logic is much simpler
// if state is high, button has been pushed so reset everything
if (resetbuttonState != resetlastButtonState)
{
if (resetbuttonState == HIGH)
{
Serial.println("Reset button pushed!");
doDisplayHighScore = true;
resetStepCounter();
// Delay a little bit to avoid bouncing
delay(50);
}
}
resetlastButtonState = resetbuttonState;
// step pad logic
int fsrADC = analogRead(FSR_PIN);
previousForce = currentForce;
currentForce = calculateForce(fsrADC);
// trying this using force values
// when we step force is always positive
// when we lift our foot force is always negative
if (currentForce > 0)
{
currentPos = true;
Serial.println("Force detected!");
delay(1000);
}
if (previousForce < 0)
{
previousPos = true;
}
if (currentPos && previousPos)
{
// start the timer on the first step
if (!timerStarted)
{
Serial.println("First steop!");
timerStarted = true;
// Serial.println("Timer start value: " +String(timerStartValue));
countDownTimer.SetTimer(0, 0, timerStartValue);
countDownTimer.StartTimer();
// char tempstring[10];
sprintf(tempString, "%4d", countDownTimer.ShowMinutes(), countDownTimer.ShowSeconds());
Serial.println("Timer set to: " + String(tempString));
// Serial.println("Timer started");
// delay(2000);
doDisplayCountDown = true;
}
stepsTaken++;
Serial.println("Step Detected!");
currentPos = false;
previousPos = false;
}
// if timer is 0 we have run out of time
// this needs to be done here as it must always be done and can't depend
// on displayCountDown being true
if (timerStarted)
{
if (countDownTimer.TimeCheck(0, 0, 0))
{
//Serial.println("out of time");
//delay(500);
timerStarted = false; // set timerRunning to false
countDownTimer.StopTimer();
if (timerStartValue == thirtySecondTime)
{
WriteHighScore(thirtySecondAddress, stepsTaken);
}
if (timerStartValue == threeMinuteTime)
{
WriteHighScore(threeMinuteAddress, stepsTaken);
}
Serial.println("wrote high score");
delay(500);
// clearDisplay();
//displayStepCount();
// resetStepCounter();
// displayCountDownTimer();
}
//else
if (!(countDownTimer.TimeCheck(0, 0, 0))) // && timerStarted)
{
countDownTimer.Timer(); // run the timer
Serial.println("Timer is running");
// timer logic
// every 10 seconds, change booleans on what gets displayed
unsigned long currentMillis = millis();
// the last 10 seconds only display the step count
//if (countDownTimer.TimeCheck(0, 0, 10))
// {
//
// displayStepCount();
// }
// else
// {
if (doChangeDisplay)
{
if ((currentMillis - previousMillis) >= displaychangeInterval)
{
// save the last time we checked the interval
// Serial.println("10 second interval!");
previousMillis = currentMillis;
// if this is true, set it to false
// set the countdown display to true
if (doDisplayStepCount)
{
doDisplayStepCount = false;
doDisplayCountDown = true;
}
else
// countdown display is true, so set it to false
// and set stepcount display to true
{
doDisplayStepCount = true;
doDisplayCountDown = false;
}
}
}
// only display step count if this is the interval to do it
if (doDisplayStepCount)
{
displayStepCount();
}
// only display countdown timer if this is the 10 second interval to do it
if (doDisplayCountDown)
{
displayCountDownTimer();
}
// }
}
}
}
// Send the clear display command (0x76)
// This will clear the display and reset the cursor
void clearDisplay()
{
s7s.write(0x76); // Clear display command
}
// Turn on any, none, or all of the decimals.
// The six lowest bits in the decimals parameter sets a decimal
// (or colon, or apostrophe) on or off. A 1 indicates on, 0 off.
// [MSB] (X)(X)(Apos)(Colon)(Digit 4)(Digit 3)(Digit2)(Digit1)
// (0b010000) - colon only
// (0b111111) - all decimals
void setDecimals(byte decimals)
{
s7s.write(0x77);
s7s.write(decimals);
}
void resetStepCounter()
{
timerbuttonPushCounter = 0;
if (timerStarted)
{
countDownTimer.StopTimer();
timerStarted = false;
}
// clean up
stepsTaken = 0;
clearDisplay();
timerStartValue = 0;
doDisplayHighScore = true;
}
void displayStepCount()
{
setDecimals(0b000000); // Turn off the colon
// Will be used with sprintf to create strings
sprintf(tempString, "%04d", stepsTaken);
Serial.print("Steps taken: " + String(tempString));
clearDisplay();
s7s.print(tempString);
}
void displayCountDownTimer()
{
// example formats
//sprintf(tempString, "%4d", deciSecond); //Convert deciSecond into a string that is right adjusted
//sprintf(tempString, "%d", deciSecond); //Convert deciSecond into a string that is left adjusted
//sprintf(tempString, "%04d", deciSecond); //Convert deciSecond into a string with leading zeros
//sprintf(tempString, "%4d", deciSecond * -1); //Shows a negative sign infront of right adjusted number
unsigned long currentMillis = millis();
int seconds = 0;
int minutes = 0;
// only update the display if 1 second has passed
// if(currentMillis - previousTimerMillis >= countdownInterval)
// CountUpDownTimer TimeHasChanged() returns boolean (in seconds)
if (countDownTimer.TimeHasChanged())
{
// previousTimerMillis = currentMillis;
seconds = countDownTimer.ShowSeconds();
Serial.println("Time left: " + seconds);
minutes = countDownTimer.ShowMinutes();
sprintf(tempString, "%02d%02d", minutes, seconds);
clearDisplay();
setDecimals(0b010000); // Turn on colon
Serial.println(tempString);
s7s.print(tempString);
}
}
void EEPROMWriteInt(const int p_address, const int p_value)
{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);
EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}
//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
int EEPROMReadInt(const int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);
return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}
void WriteHighScore(int address, int value)
{
// int valueRead = EEPROMReadInt(address);
//if (valueRead < value)
// {
EEPROMWriteInt(address, value);
delay(500);
sprintf(tempString, "%04d", value);
Serial.print("high score: " + String(tempString));
clearDisplay();
s7s.print("-HI-");
delay(500);
clearDisplay();
s7s.print("SCOR");
delay(500);
clearDisplay();
s7s.print(tempString);
delay(500);
// }
}
void displayHighScores()
{
int thirtySecondScore = EEPROMReadInt(thirtySecondAddress);
int threeMinuteScore = EEPROMReadInt(threeMinuteAddress);
sprintf(tempString, "%04d", thirtySecondScore);
Serial.println("thirty second score: " + String(tempString));
sprintf(tempString, "%04d", threeMinuteScore);
Serial.println("Three minute score: " + String(tempString));
clearDisplay(); // Clears display, resets cursor
s7s.println("-HI-");
delay(1000);
clearDisplay();
Serial.println("SCOR");
s7s.print("SCOR");
delay(1000);
clearDisplay();
sprintf(tempString, "%04d", thirtySecondScore);
Serial.println(tempString);
s7s.print(tempString);
delay(1000);
clearDisplay();
sprintf(tempString, "%04d", threeMinuteScore);
Serial.println(tempString);
s7s.print(tempString);
delay(1000);
clearDisplay();
}
float calculateForce(int pinvalue)
{
// Use ADC reading to calculate voltage:
float fsrV = pinvalue * VCC / 1023.0; // 1023 is # of units of voltage between 0 and 5V
// Use voltage and static resistor value to
// calculate FSR resistance:
float fsrR = R_DIV * (VCC / fsrV - 1.0);
// Serial.println("Resistance: " + String(fsrR) + " ohms");
// Guesstimate force based on slopes in figure 3 of
// FSR datasheet:
float force;
float fsrG = 1.0 / fsrR; // Calculate conductance
// Break parabolic curve down into two linear slopes:
force = (fsrG - 0.00075) / 0.00000032639;
return force;
}
tempString[5]
. now the 0 is written by sprintf to memory after the array