General Help: Best Way to Flash an LED, Formatting LCD Output, Fixing Stack Overflow

Hi there!

Apologies for the lengthy post, but I didn’t want the system to think I was spamming based on the number of new posts.

Virtually a complete noob to Particle here, but I have had a Photon running and watching my freezer temperature off and on for a couple of years (and using IFTTT to log to a Google Sheet). Coming home after vacation to find a tripped breaker one time was enough!

I picked up a couple more Photons recently so I can work towards making some “Who is teaching and/or in a meeting?” status boxes for use around the house and at the front door. After large parts of several days searching for help and getting distracted by all of the cool projects people have created, I need some general help all the way around.

To work towards that, I’ve decided to add more features to the FreezerMonitor, one at a time, so I can learn more about everything along the way. I am very much “hunting and pecking” to try to assemble this. I know bits and pieces about various languages that I’ve used over the years, but am a mere beginner in them all.

The first step is upgrading the FreezerMonitor system by adding a second Photon to sit on my desk, flash its D7 in different patterns based on what’s happening in the garage, and display the temp on an LCD screen.

Flashing an LED
What’s the best way to flash an LED? Since I’m getting Stack Overflow resets, I don’t think my way is working very well. :laughing: Is there a way to loop without being in the main void loop?

Am I using the handlers incorrectly? Is it a better idea to have them set a global variable, then test that variable inside the void loop, alternating patterns based on that value?

I wanted to start simply and have the FreezerGood and FreezerBad events be the triggers it would use, but then realized that I could probably do it with testing the value of the temperature event. I haven’t had time to clean up the code.

Formatting LCD Output
I have looked at numerous examples, but I just do not grasp the formatting nor how to concatenate text onto the end.


I would love for that to display 3.65°F or -4.34°F instead of the meaningless digits.

Also, I can’t figure out how to round a double down to two decimal places. I’ve managed to get the variable in my FreezerMonitor to display a whole number, but I’d like to see at least one decimal place. I’ve read the “74.14505494505494” post several times, tried it several times, to no avail. (Granted, that’s not in this code, but once it works here…)

Fixing Stack Overflow
Is my implementation of the handlers and the flashing LED causing the stack overflow issue?

The Serial output was so I could hopefully establish a pattern as to when the device would reset.

/*
 * Project FreezerRemote
 * Description: Remote display for FreezerMonitor
 * Author:
 * Date:
 */
#include "LiquidCrystal_I2C_Spark.h"
LiquidCrystal_I2C *lcd;

int led1 = D7;
int i = 0;
int counter = 0;
char line0[21]; 
char line1[21];

String tempDisplay = "initializing...."; 

// setup() runs once, when the device is first turned on.
void setup(void) {
  // Put initialization like pinMode and begin functions here.
  Particle.subscribe("FreezerGood", Safe, MY_DEVICES);
  Particle.subscribe("FreezerBad", NotSafe, MY_DEVICES);
  Particle.subscribe("temperature", TemperatureHandler, MY_DEVICES);

  pinMode(led1, OUTPUT);
  digitalWrite(led1, LOW);

  Serial.begin(9600);

//initialize the liquid crystal library
//the first parameter is the I2C address
//the second parameter is how many rows are on your screen
//the third parameter is how many columns are on your screen

  lcd = new LiquidCrystal_I2C(0x27, 16, 2);
  lcd->init();
  lcd->backlight();
  lcd->clear();
  lcd->print("Freezer Monitor");

}

void Safe(const char *event, const char *data)
{
  //Particle.publish("MonitorTempReceived", data, PRIVATE);
  String datastr = String(data);
  Particle.variable("MonitorTempReceived", datastr );
    Serial.println();
    Serial.println();
    Serial.print("Safe Handler triggered - Temp = ");
    Serial.println(datastr);
    Serial.println();

    //Print temp to LCD
    lcd->clear();
    lcd->print("Freezer Temp");
    lcd->setCursor(0,1);
    lcd->print(datastr);
    //lcd->print(Time.minute() < 10? ":0": ":");

    //Flash led1
    for (int i=0; i<100000; i++) {
      Serial.print(" Counter = ");
      Serial.println(i);

      digitalWrite(led1, LOW);
      delay(750);
      digitalWrite(led1, HIGH);
      delay(2000);

    }

}

void NotSafe(const char *event, const char *data)
{
    String datastr = String(data);
    digitalWrite(led1, LOW);
    Particle.variable("MonitorTempReceived", datastr );
    Serial.println();
    Serial.println();
    Serial.print("NotSafe Handler triggered - Temp = ");
    Serial.println(datastr);
    Serial.println();
    for (int i=0; i<100000; i++) {
      Serial.print(" Counter = ");
      Serial.println(i);

      digitalWrite(led1, LOW);
      delay(100);
      digitalWrite(led1, HIGH);
      delay(100);
    }

}

void TemperatureHandler(const char *event, const char *data)
{
    String tempDisplay = String(data);
    digitalWrite(led1, LOW);
 

}


// loop() runs over and over again, as quickly as it can execute.
void loop(void) {
  // The core of your code will likely live here.
    lcd->setCursor(0,1);
    lcd->print(tempDisplay);

}

A billion bits of “thank you” in advance to all who care enough to answer!

Welcome to the community!

First of all, you need to fix your use of Particle.variable().
You don’t use that function to actually set the variable but only to register a given global variable with the cloud in order to allow that variable’s value to be requested some time later in time.
So all your scattered Particle.variable() calls need to go and should be replaced with a single call in setup() along side your Particle.subscribe() calls.

Then there are some hints that may be useful to make your code more readable.

You can use Serial.printlnf() to construct complex outputs (using the printf() formatting syntax)
e.g.

  Serial.printlnf("\r\n\r\nNotSafe Handler triggered - Temp = %s", data);

Also you should avoid String and rather stick with C-strings (aka character arrays).
This way you can also use snprintf() to construct a complex string containing multiple values

  float temp = 3.456;
  float hum  = 56.7;
  snprintf(line1, sizeof(line1), "%4.2f °F %3.1f %%", temp, hum);

When you only want to copy a string you can use strcpy() (or better strncpy())
e.g.

  strcpy(line0, data);

I also don’t think you want to blink 100000 times with 2.75 seconds delays :wink:
For blinking an LED without blocking your code flow you can use Software Timers or for the on-board LED you can use the Signaling feature.

In your TemperatureHandler() you create a local variable tempDisplay which will vanish as soon the function ends but that never makes it into your global variable.
You’d need to remove the data type (String) in order to actually access the global variable.

2 Likes

Thank you, ScruffR!

I was going to try to clean up the code before I posted it, but after a long day at work with the breaks being small victories and setbacks with the Photon, I gave up on trying to remember what I was thinking. You might have been able to tell I was working on learning buffers from the char lines at the beginning, but that was a couple of minutes before I hit the “I need help” point.

The Particle.variable and .publish calls were from before the $5 LCD displays arrived when I was just trying to verify that I was actually receiving data into the thing.

Thank you for the pointers on where to head from here!

1 Like

Thanks to your pointers, I have the LED flashing from a Timer and the stack overflows have ceased. All of the LCD data is being sent with “line0” and “line1” character arrays.

I’m getting this error when trying to compile:

src/FreezerRemote.ino: In function 'void Safe(const char*, const char*)':
src/FreezerRemote.ino:89:62: error: expected primary-expression before 'float'
   snprintf(line1, sizeof(line1), "%4.2f °F %3.1f %%", temp, float);
                                                              ^

Here’s that section of the code:

void Safe(const char *event, const char *data)
{
  char line0[17] = "FreezerTemp Good";
  strcpy(line1, data);

  //String tempString;
  //strcpy(tempString, data);

  //temperatureGlobal = tempString.toFloat();
  //float temp = tempString.toFloat();

  /*
  float hum  = 56.7;  
  
  */
  float temp = 3.456;
  snprintf(line1, sizeof(line1), "%4.2f °F %3.1f %%", temp, float);
  

  //snprintf(line1, sizeof(line1), "%4.2f °F %3.1f %%", temp, float);

    Serial.printlnf("\r\n\r\nSafe Handler triggered - Temp = %s°F", data);
    Serial.printlnf(Time.timeStr());

    //Print temp to LCD
    lcd->clear();
    lcd->print(line0);
    lcd->setCursor(0,1);
    lcd->print(line1);


    //Flash led1
    timerFlasher.start();
    timerFlasher.changePeriod(2000);
    offDelay = 250;

    //We're safe, so let's calm down the spinner a bit
    stepDelay = 400;

}

I’ve tried various ways to see if I could get it to work. I’ve looked around quite a bit and even tried https://cpp.sh to see if I could figure it out through other examples, but I’m flummoxed. Any ideas?

Sorry, that was a typo.
Of course instead of float this should have been hum as this is the variable declared just above that line - just like temp one line up from that.
You even commented that line out in your code :see_no_evil:
(corrected above)

That’s the difference between just copy/pasting code and actually understanding what is meant :wink: