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;
}
1 Answer 1
This line:
sprintf(tempString, "%04d", thirtySecondTime);
writes 5 characters to tempString, the four digits and a terminating null. You only gave the array room for four.
This:
Serial.println("Timer set to: " + String(tempString));
is a total waste of resources. If you want to talk about memory usage then don't use the String class and definitely don't use it where it simply is not needed.
Write that as:
Serial.print("Timer set to: ");
Serial.println(tempString);
or better yet from a memory standpoint as:
Serial.print(F("Timer set to: "));
Serial.println(tempString);
-
thanks. I was reading about String() memory usage and will change that. Most of the serial output will be removed after I am done with all the debugging. From @Juraj comment I did some research and now understand the termination.crokett– crokett2019年01月27日 20:42:08 +00:00Commented Jan 27, 2019 at 20:42
Explore related questions
See similar questions with these tags.
tempString[5]
. now the 0 is written by sprintf to memory after the array