0

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
  • add a position for terminating zero in tempString. tempString[5]. now the 0 is written by sprintf to memory after the array
    – Juraj
    Jan 27, 2019 at 14:38

1 Answer 1

3

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);
1
  • 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. Jan 27, 2019 at 20:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.