Set subscribe handler data as global

Actually char* / char[] (with or without const) are considered "normal" C strings.
String or std::string are non-trivial C++ types (aka class/object types).
String literals are also treated as const char[].

BTW

quite clearly const char* :wink:

Hi ScruffR, Doesnt strcmp(const1, const2) check if const1 == const2? I did this and my led lights up in dark, but stays on in dim and bright when its supposed to turn off!

  • Widget doesnt work though physical btn does. What is inline void instead of void used for? I understand that inline is a hint to the compiler to call it at the specific line.

// Get data from subscribed topic ambience_C8, on / off led according to ambience status
void myAmbienceHandler(const char *event, const char *data){

    if(strcmp(data, "Dark")){
        digitalWrite(D2, HIGH);
        Log.info("Led turns on");
    }
    else if(strcmp(data, "Bright")){
        digitalWrite(D2, LOW);
        Log.info("Led stays off");
    }
    else{
        digitalWrite(D2, LOW);
        Log.info("Led stays off");
    }
}

Heres my full code, I changed most strings to char ( will do that for ledtoggle command) once i start debugging tomorrow.

Device 1 code

#include <blynk.h>

BlynkTimer timer;

SerialLogHandler logHandler(LOG_LEVEL_ERROR, {{"app", LOG_LEVEL_INFO}});


const int led3=D0;       // Blynk Nightlight Control (Slider) + HTML Nightlight Toggle On/Off
int led3_status=-1;      // HTML Nightlight Toggle On/Off

const int ldr=A0;              // Blynk Ambience Brightness 
int ldr_status=0;        // Blynk Ambience Brightness 

WidgetLCD lcd(V1);       // Blynk LCD (Date Day Greeting + Ambience Status)
WidgetLCD lcd2(V3);      // Blynk LCD (Temp + Pressure)
WidgetLCD lcd3(V5);      // Blynk LCD (Door status)

const char auth[] = "";

bool ldrNeedUpdate = false;

void blynkLDRdata(){
    ldrNeedUpdate = true;
}

// cannot be called from a Software Timer!
void mustDo() {
  if (Particle.connected()) {  
    Blynk.run(); 
    timer.run();
    if (ldrNeedUpdate) {
      ldr_status = analogRead(ldr);
      Blynk.virtualWrite(V0, ldr_status);
      ldrNeedUpdate = false;    
    }
  }
  Particle.process();   //Particle.process() is a blocking call, and blocks for a few milliseconds.
}

void softDelay(uint32_t timeout) {
  for (uint32_t ms = millis(); millis() - ms < timeout; mustDo());
}

// Get Temperature data from subscribed topic and print on LCD
void myTempHandler(const char *event, const char *data){
    char msg[128];
    double val = atof(data);
    Log.info("Temperature Handler executed, %f°C", val);
    snprintf(msg, sizeof(msg), "%.2f°C %s", val, (val > 32.50) ? "Warm" : "Cool");
    lcd2.print(0,0, msg);
}
    


void myPressHandler(const char *event, const char *data){
    char msg[128];
    double val = atof(data);
    Log.info("Pressure Handler executed, %.2fPa", val);
    snprintf(msg, sizeof(msg), "%.2fPa %s", val, (val > 900) ? "High" : "Low ");
    lcd2.print(0,1, msg);
}

void myDoorHandler(const char *event, const char *data){
    char msg[128];
    Log.info("Door Handler executed: %s", data);
    if(!strcmp(data, "DOOR UNLOCKED!")) {
        snprintf(msg, sizeof(msg), "Your door was UNLOCKED on %s.\r\n"
                                   "Please ignore if this was you."
                                 , (const char*)Time.format("%a %e %b at %I:%M%p"));
        Log.info(msg);
        Blynk.email("email@.io", "SECURITY ALERT!", msg);
        lcd3.print(0,0, "DOOR UNLOCKED!");
    }
    lcd3.print(0,0, "door locked   ");
}


void setup(){

    // Nightlight Toggle On/Off
    pinMode(led3, OUTPUT); 
    Particle.function("led",ledToggle);

    // HTML Ambience Brightness Gauge
    ldr_status=analogRead(ldr);      // pinMode not needed since analogRead used
    Particle.variable("ldranalogvalue", ldr_status);
    
    Blynk.begin(auth); 

    // bLYNK LCD Time Display (Set Timezone to SG using +8)
    Time.zone(+8);  
    lcd.clear();
    lcd2.clear();
    lcd3.clear();

    // IP ADDRESS
    // iot_C8 MQTT sub to door status, temp and pressure data vals from D8 published temp and pressure readings
    Particle.subscribe("D8_temperature", myTempHandler, MY_DEVICES);
    Particle.subscribe("D8_pressure", myPressHandler, MY_DEVICES);
    Particle.subscribe("D8_door_status", myDoorHandler, MY_DEVICES);

    //Particle.subscribe("D8_", D8_Handlers, MY_DEVICES);

    // Push ldrdata into blynk app every 3 seconds
    timer.setInterval(2000, functions);
    
    Log.info("Serial is Running");
}


void functions(){
    // update ldr_value to gauge
    blynkLDRdata();
    pub_ambience_status();
}

void pub_ambience_status(){
    char msg[10] = "";
    
    if (ldr_status <= 100){  
        strncpy(msg, "Dark", sizeof(msg));  
        Particle.publish("ambience_C8", msg, PRIVATE);
        lcd.print(9,0, "Dark  ");
        Log.info("Ambience Status - %s", msg);
    }
    
    else if (ldr_status >= 300){    
        strncpy(msg, "Bright", sizeof(msg));  
        Particle.publish("ambience_C8", msg, PRIVATE);
        lcd.print(9,0, "Bright");
        Log.info("Ambience Status - %s", msg);
    }
    
    else{
        strncpy(msg, "Dim", sizeof(msg));  
        Particle.publish("ambience_C8", msg, PRIVATE);
        lcd.print(9,0, "Dim   ");
        Log.info("Ambience Status - %s",  msg);
    }
}


void loop(){                     
    mustDo();

    DisplayTime();
    DisplayDate();
    softDelay(2000);
    Greeting();
    softDelay(2000);
}

void DisplayTime(){
    lcd.print(0,0, (const char*)Time.format("%I:%M%p")); 
}

void DisplayDate(){
    lcd.print(0,1, (const char*)Time.format("%a %e %b    "));
}

void Greeting(){

    float hh = (Time.local() % 86400) / 3600.0; // Current decimal hour of the day , currenthr*24hr/1hr
    if (hh < 5.0){
        lcd.print(0, 1, "Midnight      ");
    }
    else if (hh < 12.0){
        lcd.print(0, 1, "Good Morning  ");
    }
    else if (hh < 17.0){
        lcd.print(0, 1, "Good Afternoon");
    }
    else if(hh < 20.0){
        lcd.print(0, 1, "Good Evening  ");
    }
    else if(hh < 24.0){
        lcd.print(0, 1, "Night         ");
    }
}


int ledToggle(String command) {
    if (command=="on") {
        digitalWrite(led3,HIGH);
        Log.info("HTML led3 on");
        return 1;
    }
    else if (command=="off") {
        digitalWrite(led3,LOW);
        Log.info("HTML led3 off");
        return 0;
    }
    else if (command=="read") {
        return digitalRead(led3);
    }
    else {
        return -1;
    }
}

Device 2 code

#include <Adafruit_BMP280.h>
#include <blynk.h>

SerialLogHandler logHandler(LOG_LEVEL_ERROR, {{"app", LOG_LEVEL_INFO}});

Adafruit_BMP280 bmp; // I2C
BlynkTimer timer;

const int led1 = D2;   // Ambience light
const int button = D4; // Fingerprint scanner

const char auth[] = "";  

// HTML Temp + Press Gage Data
double t_temp;
double t_press;

// Update fingerprint status every 5s
Timer timer1(3000, fingerprint_scanner);

bool tempNeedUpdate = false;
bool pressNeedUpdate = false;


void tempdata(){
    tempNeedUpdate = true;
}

void pressdata(){
    pressNeedUpdate = true;
}

// cannot be called from a Software Timer!  
void mustDo() {
  if (Particle.connected()) {
    Blynk.run(); 
    timer.run();
    if (tempNeedUpdate) {
        
        char temp_data[16];
        t_temp = bmp.readTemperature();
        snprintf(temp_data, sizeof(temp_data), "%.2f", t_temp);

        Particle.publish("D8_temperature", temp_data, PRIVATE);
        tempNeedUpdate = false;
    }
    
    if (pressNeedUpdate) {

        char press_data[16];
        t_press = bmp.readPressure();
        snprintf(press_data, sizeof(press_data), "%.2f", t_press);
        
        Particle.publish("D8_pressure", press_data, PRIVATE);
        pressNeedUpdate = false;
        
    }
  }
  Particle.process();
}

void functions(){
    tempdata();
    pressdata();
}

void softDelay(uint32_t timeout) {
  for (uint32_t ms = millis(); millis() - ms < timeout; mustDo());
}

// Get data from subscribed topic ambience_C8, on / off led according to ambience status
void myAmbienceHandler(const char *event, const char *data){

    if(strcmp(data, "Dark")){
        digitalWrite(D2, HIGH);
        Log.info("Led turns on");
    }
    else if(strcmp(data, "Bright")){
        digitalWrite(D2, LOW);
        Log.info("Led stays off");
    }
    
    else /*if(strcmp(data, "Dim"))*/{
        digitalWrite(D2, LOW);
        Log.info("Led stays off");
    }
}

inline void pubDoorState(bool state) {
    int lastState = -1;
    int curState = digitalRead(button);

  if (curState != lastState) {
    Particle.publish("D8_door_status", curState ? "door locked" : "DOOR UNLOCKED!");
    lastState = curState;
  }
}

void fingerprint_scanner() {
    pubDoorState(digitalRead(button));
}

BLYNK_WRITE(V4) {
    pubDoorState(param.asInt());
}

void setup() {
    
    pinMode(led1, OUTPUT);
    pinMode(button, INPUT_PULLUP);
    
    // HTML Temp and pressure gauge reading
    Particle.variable("temp", t_temp);
    Particle.variable("press", t_press);
    
    // iot_D8 Device MQTT subscribe to Ambience_C8
    Particle.subscribe("ambience_C8", myAmbienceHandler, MY_DEVICES); 

    Blynk.begin(auth); 

    timer1.start();
    
    // Update temp and press every 5 seconds
    timer.setInterval(5000, functions);
    
    Log.info("Serial is Running");

    if (!bmp.begin()){
        Log.warn("Can't find the sensor BMP280");
    }
}

void loop(){
    mustDo();
}

Not exactly. As the reference link above clarifies it compares them and returns the "difference" between the two.
Consequently equality would be indicated by 0, hence my code reads something like this

if(!LITERALCMP(data, "DOOR UNLOCKED!"))

notice the ! which means boolean negation (i.e. 0 -> true, non-zero -> false).

inline instructs the compiler to "copy" the respective function instead of using a jump instruction.
For very short functions like this one-liner inlining avoids the extra overhead for the jump and return instructions (including the intrinsic stack push/pop effort).

Hi ScruffR, I realised my temp publish data value is written into char, and published as const char,

    char temp_data[16];
    t_temp = bmp.readTemperature();
    snprintf(temp_data, sizeof(temp_data), "%.2f", t_temp);
  • in my temp subscribe handler, since my “data” is already a const char, why is my output of print (data) empty?
void myTempHandler(const char *event, const char *data){
    Log.trace("%s: <%s>", event, data);
    char msg[128];
    double val = atof(data);
    
    Log.info("Temperature Handler executed, %f°C", val);
    snprintf(msg, sizeof(msg), "%.2f°C %s", val, (val > 32.50) ? "Warm" : "Cool");
    lcd2.print(0,0, msg);
}

I might be able to answer that if you also provide the output of the Log.trace().
When questions like this arise that’s what the logs are for :wink:

Here is the output

[68472] <[14|00|A8|00|07]vw[00]0[00]20
[68540] <[14|00|A9|00|12]vw[00]1[00]p[00]9[00]0[00]Dark  [00]
0000068540 [app] INFO: Ambience Status - Dark
0000068545 [app] INFO: Temperature Handler executed, 31.22°C
[68607] <[14|00|AA|00|19]vw[00]3[00]p[00]0[00]0[00]31.22[C2|B0]C Cool[00]
[68759] <[14|00|AB|00|13]vw[00]1[00]p[00]0[00]0[00]02:19PM[00]
0000068803 [app] INFO: Pressure Handler executed, 100880.00Pa
[68826] <[14|00|AD|00|1C]vw[00]3[00]p[00]0[00]1[00]100880.00Pa High[00]
[68827] <[14|00|AC|00|1A]vw[00]1[00]p[00]0[00]1[00]Mon 25 Jan    [00]

I have no idea why stack overflow still happens, I’ve tried using timers/the mustDo() function yet i cant fix it. I changed from strings to char, even double temp and press to char.

Device 1 Code

#define BLYNK_DEBUG
#define BLYNK_PRINT Serial

#include <blynk.h>

BlynkTimer timer;

SerialLogHandler logHandler(LOG_LEVEL_ERROR, {{"app", LOG_LEVEL_INFO}});


const int led3=D0;       // Blynk Nightlight Control (Slider) + HTML Nightlight Toggle On/Off
int led3_status=-1;      // HTML Nightlight Toggle On/Off

const int ldr=A0;              // Blynk Ambience Brightness 
int ldr_status=0;        // Blynk Ambience Brightness 

WidgetLCD lcd(V1);       // Blynk LCD (Date Day Greeting + Ambience Status)
WidgetLCD lcd2(V3);      // Blynk LCD (Temp + Pressure)
WidgetLCD lcd3(V5);      // Blynk LCD (Door status)

const char auth[] = "";

bool ldrNeedUpdate = false;
void blynkLDRdata(){
    ldrNeedUpdate = true;
}

bool ambienceNeedUpdate = false;
void UpdateAmbience(){
    ambienceNeedUpdate = true;
}

// cannot be called from a Software Timer!
void mustDo() {
  if (Particle.connected()) {  
    Blynk.run(); 
    timer.run();
    if (ldrNeedUpdate) {
      ldr_status = analogRead(ldr);
      Blynk.virtualWrite(V0, ldr_status);
      ldrNeedUpdate = false;    
    }
    
    if(ambienceNeedUpdate){
        pub_ambience_status();
        ambienceNeedUpdate = false;
    }
  }
  Particle.process();   //Particle.process() is a blocking call, and blocks for a few milliseconds.
}

// Get Temperature data from subscribed topic and print on LCD
void myTempHandler(const char *event, const char *data){
    Log.trace("%s: <%s>", event, data);
    char msg[20];
    double val = atof(data);
    Log.info("Temperature Handler executed, %.2f°C", data);
    snprintf(msg, sizeof(msg), "%.2f°C %s", val, (val > 32.50) ? "Warm" : "Cool");
    lcd2.print(0,0, msg);
}
    
void myPressHandler(const char *event, const char *data){
    //Log.trace("%s: <%s>", event, data);
    char msg[20];
    double val = atof(data);
    Log.info("Pressure Handler executed, %.2fPa", val);
    snprintf(msg, sizeof(msg), "%.2fPa %s", val, (val > 900) ? "High" : "Low ");
    lcd2.print(0,1, msg);
}

void myDoorHandler(const char *event, const char *data){
    //Log.trace("%s: <%s>", event, data);
    char msg[60];
    Log.info("Door Handler executed: %s", data);
    if(!strcmp(data, "DOOR UNLOCKED!")) {
        snprintf(msg, sizeof(msg), "Your door was UNLOCKED on %s.\r\n"
                                   "Please ignore if this was you."
                                 , (const char*)Time.format("%a %e %b at %I:%M%p"));
        Log.info(msg);
        Blynk.email("email@gmail.com", "SECURITY ALERT!", msg);
        lcd3.print(0,0, "DOOR UNLOCKED!");
    }
    lcd3.print(0,0, "door locked   ");
}

void softDelay(uint32_t timeout) {
  for (uint32_t ms = millis(); millis() - ms < timeout; mustDo());
}

void setup(){

    // Nightlight Toggle On/Off
    pinMode(led3, OUTPUT); 
    Particle.function("led",ledToggle);

    // HTML Ambience Brightness Gauge
    ldr_status=analogRead(ldr);      // pinMode not needed since analogRead used
    Particle.variable("ldranalogvalue", ldr_status);
    
    Blynk.begin(auth); 

    // bLYNK LCD Time Display (Set Timezone to SG using +8)
    Time.zone(+8);  
    lcd.clear();
    lcd2.clear();
    lcd3.clear();

    // IP ADDRESS
    // iot_C8 MQTT sub to door status, temp and pressure data vals from D8 published temp and pressure readings
    Particle.subscribe("D8_temperature", myTempHandler, MY_DEVICES);
    Particle.subscribe("D8_pressure", myPressHandler, MY_DEVICES);
    Particle.subscribe("D8_door_status", myDoorHandler, MY_DEVICES);

    // Push ldrdata into blynk app every 3 seconds
    //timer.setInterval(3000, blynkLDRdata);
    timer.setInterval(3000, functions);
    
    Log.info("Serial is Running");
}

void functions(){
    blynkLDRdata();
    UpdateAmbience();
}

void pub_ambience_status(){
    char msg[10] = "";
    
    if (ldr_status <= 100){  
        strncpy(msg, "Dark", sizeof(msg));  
        Particle.publish("C8_ambience", msg, PRIVATE);
        lcd.print(9,0, "Dark  ");
        Log.info("Ambience Status - %s", msg);
    }
    
    else if (ldr_status >= 300){    
        strncpy(msg, "Bright", sizeof(msg));  
        Particle.publish("C8_ambience", msg, PRIVATE);
        lcd.print(9,0, "Bright");
        Log.info("Ambience Status - %s", msg);
    }
    
    else{
        strncpy(msg, "Dim", sizeof(msg));  
        Particle.publish("C8_ambience", msg, PRIVATE);
        lcd.print(9,0, "Dim   ");
        Log.info("Ambience Status - %s",  msg);
    }
}

void loop(){                     
    mustDo();
    
    DisplayTimeDate();
    softDelay(2000);
    Greeting();
    softDelay(2000);
}

void DisplayTimeDate(){
    lcd.print(0,0, (const char*)Time.format("%I:%M%p")); 
    lcd.print(0,1, (const char*)Time.format("%a %e %b    "));
}

void Greeting(){

    float hh = (Time.local() % 86400) / 3600.0; // Current decimal hour of the day , currenthr*24hr/1hr
    if (hh < 5.0){
        lcd.print(0, 1, "Midnight      ");
    }
    else if (hh < 12.0){
        lcd.print(0, 1, "Good Morning  ");
    }
    else if (hh < 17.0){
        lcd.print(0, 1, "Good Afternoon");
    }
    else if(hh < 20.0){
        lcd.print(0, 1, "Good Evening  ");
    }
    else if(hh < 24.0){
        lcd.print(0, 1, "Night         ");
    }
}


int ledToggle(const char* command){  // Assign command str as const char
    if(command="on") {
        digitalWrite(led3,HIGH);
        Log.info("HTML led3 on");
        return 1;
    }
    else if(command="off") {
        digitalWrite(led3,LOW);
        Log.info("HTML led3 off");
        return 0;
    }
    else if(command="read") {
        Log.info("HTML led state");
        return digitalRead(led3);
    }
    else {
        return -1;
    }
}

Device 2 Code

#define BLYNK_DEBUG
#define BLYNK_PRINT Serial
#define DEBUG_PRINT(...) { Particle.publish( "DEBUG", String::format(__VA_ARGS__) ); } void setup() { } void loop() { DEBUG_PRINT("HELLO"); delay(1000); }

#include <Adafruit_BMP280.h>
#include <blynk.h>

SerialLogHandler logHandler(LOG_LEVEL_ERROR, {{"app", LOG_LEVEL_INFO}});

Adafruit_BMP280 bmp; // I2C
BlynkTimer timer;

const int led1 = D2;   // Ambience light
const int button = D4; // Fingerprint scanner

const char auth[] = "";  

// HTML Temp + Press Gage Data
double t_temp;
double t_press;

void myAmbienceHandler(const char *event, const char *data);

// Update fingerprint status every 5s
Timer timer1(1000, fingerprint_scanner);
Timer timer2(5000, pub_Temp);
Timer timer3(20000, pub_Press);

bool tempNeedUpdate = false;
bool pressNeedUpdate = false;


void tempdata(){
    tempNeedUpdate = true;
}

void pressdata(){
    pressNeedUpdate = true;
}

void pub_Temp(){
    char temp_data[16];
    t_temp = bmp.readTemperature();
    snprintf(temp_data, sizeof(temp_data), "%.2f", t_temp);
    Particle.publish("D8_temperature", temp_data, PRIVATE);
}

void pub_Press(){
    char press_data[16];
    t_press = bmp.readPressure();
    snprintf(press_data, sizeof(press_data), "%.2f", t_press);
    Particle.publish("D8_pressure", press_data, PRIVATE);
}

// cannot be called from a Software Timer!  
void mustDo() {
  if (Particle.connected()) {
    Blynk.run(); 
    timer.run();
    if (tempNeedUpdate) {
        
        char temp_data[16];
        t_temp = bmp.readTemperature();
        snprintf(temp_data, sizeof(temp_data), "%.2f", t_temp);

        Particle.publish("D8_temperature", temp_data, PRIVATE);
        tempNeedUpdate = false;
    }
    
    if (pressNeedUpdate) {

        char press_data[16];
        t_press = bmp.readPressure();
        snprintf(press_data, sizeof(press_data), "%.2f", t_press);
        
        Particle.publish("D8_pressure", press_data, PRIVATE);
        pressNeedUpdate = false;
    }
  }
  Particle.process();
}

void functions(){
    tempdata();
    pressdata();
}

// Get data from subscribed topic ambience_C8, on / off led according to ambience status
void myAmbienceHandler(const char *event, const char *data){
    //Log.trace("%s: <%s>", event, data);

    if(!strcmp(data, "Dark")){
        digitalWrite(D2, HIGH);
        Log.info("Led turns on");
    }
    else if(!strcmp(data, "Bright") || !strcmp(data, "Dim")){
        digitalWrite(D2, LOW);
        Log.info("Led stays off");
    }
}

void pubDoorState(bool state) { // bool state flag 1/0 button
    Particle.publish("D8_door_status", state ? "door locked" : "DOOR UNLOCKED!");
}

void fingerprint_scanner() {
    pubDoorState(digitalRead(button));
}

BLYNK_WRITE(V4) {
    pubDoorState(param.asInt());
}

void softDelay(uint32_t timeout) {
  for (uint32_t ms = millis(); millis() - ms < timeout; mustDo());
}

void setup() {
    
    pinMode(led1, OUTPUT);
    pinMode(button, INPUT_PULLUP);
    
    // HTML Temp and pressure gauge reading
    Particle.variable("temp", t_temp);
    Particle.variable("press", t_press);
    
    // iot_D8 Device MQTT subscribe to Ambience_C8
    Particle.subscribe("ambience_C8", myAmbienceHandler, MY_DEVICES); 

    Blynk.begin(auth); 

    //timer1.start();
    //timer2.start();
    //timer3.start();
    
    // Update temp and press every 5 seconds
    timer.setInterval(5000, functions);
    
    Log.info("Serial is Running");

    if (!bmp.begin()){
        Log.warn("Can't find the sensor BMP280");
    }
}

void loop(){
    mustDo();
    //Blynk.run();
    
    fingerprint_scanner();
    softDelay(1000);
}

You won't see the Log.trace() output of your subscription handler because you didn't do that

To make it absolutely clear what that meant, you need this

SerialLogHandler logHandler(LOG_LEVEL_ERROR, {{ "app", LOG_LEVEL_ALL }});

I won't address this any further since I already provided some code that - by your own admission - does not suffer from that.
You also haven't pull through with this

This should have helped to locate the code section where the crash comes from. Without that info it would be chasing the wind to guess where it comes from.

Thanks ScruffR! I can see the trace info now!
0000020638 [app] INFO: Door Handler executed: door locked
0000020940 [app] TRACE: D8_temperature: <31.25>

  • It outputs fine since i used .2f , data. However when i replace “val > 32.50” with “data > 32.50”, i get error that says invalid operands of types ‘const char*’ and ‘double’ to binary 'operator>
    I think i will put “32.50” into char threshold[10] or put double val into char threshold[10] …
void myTempHandler(const char *event, const char *data){
    Log.trace("%s: <%s>", event, data);
    char msg[20];
    double val = atof(data);
    Log.info("Temperature Handler executed, %.2f°C", data);
    snprintf(msg, sizeof(msg), "%.2f°C %s", val, (val > 32.50) ? "Warm" : "Cool");
    lcd2.print(0,0, msg);
}
  • I just added crash detection code to the top of setup on line 93, and in pub_ambience_status() where it runs in mustDo()

When i run the code, I this output, does this mean that the line 136 where the if statement lies, is the issue that causes stack overflow? It’s been two weeks straight working on my first photon particle project with very basic c++ that i picked up in class. I will be back in 10hours to try debug again… “sigh”

0000192231 [app] TRACE: D8_door_status: <door locked>
0000192231 [app] INFO: Door Handler executed: door locked
0000193452 [app] TRACE: D8_door_status: <door locked>
0000193452 [app] INFO: Door Handler executed: door locked
0000194245 [app] TRACE: D8_door_status: <door locked>
0000194245 [app] INFO: Door Handler executed: door locked
**0000/194769 [app] INFO: Ambience Status - Dark**
** 136**
**0000195250 [app] TRACE: D8_door_status: <door locked>**
0000195250 [app] INFO: Door Handler executed: door locked
0000196255 [app] TRACE: D8_door_status: <door locked>
0000196255 [app] INFO: Door Handler executed: door locked
0000197263 [app] TRACE: D8_door_status: <door locked>
0000197264 [app] INFO: Door Handler executed: door locked
0000197767 [app] INFO: Ambience Status - Dark
 136
0000198454 [app] TRACE: D8_door_status: <door locked>
0000198454 [app] INFO: Door Handler executed: door locked

133void pub_ambience_status(){
134   char msg[10] = "";
135   
**136    if (ldr_status <= 100){**  
137        strncpy(msg, "Dark", sizeof(msg));  
138      Particle.publish("C8_ambience", msg, PRIVATE);
139    lcd.print(9,0, "Dark  ");
140   Log.info("Ambience Status - %s", msg);
141 Serial.printlnf("%4d", __LINE__); delay(10);
142
143   }
    
    else if (ldr_status >= 300){    
        strncpy(msg, "Bright", sizeof(msg));  
        Particle.publish("C8_ambience", msg, PRIVATE);
        lcd.print(9,0, "Bright");
        Log.info("Ambience Status - %s", msg);
        Serial.printlnf("%4d", __LINE__); delay(10);

    }
    
    else{
        strncpy(msg, "Dim", sizeof(msg));  
        Particle.publish("C8_ambience", msg, PRIVATE);
        lcd.print(9,0, "Dim   ");
        Log.info("Ambience Status - %s",  msg);
        Serial.printlnf("%4d", __LINE__); delay(10);

    }
}

Not a surprise there since you cannot compare string data to a number literal in a meaningful way like that.

Why on earth would you do that? Numeric values are best compared as numbers not as text.

No.
As I said earlier

Adding only one of these "beacon" lines will not help much. You need to add multiple such lines wherever you imagine the crash may happen and then wait for the crash to hit. The last line printed before the crash was obviously still executed but the next was not reached so the problem must lay between.
The narrower the gap between two of these lines the more precise your error detection will be.

BTW, I also told you about the need for a reference line due to the offset between the logged line number and the line number in your editor.

Hi ScruffR! Here is my output, I’ve added crash detection to every function

example

void function(){
    Serial.printlnf("%4d func start", __LINE__); delay(10);
    Serial.printlnf("%4d func end", __LINE__); delay(10);
}

  • Given that the crash happened at the output , the last line number printed was 83. while setup line was 106. The calculated difference = 23 lines, which the code lies there?
  106 reference <-- LINE, setup start
...
...
...
  32 Must Do func start
  51 Must Do func end
  32 Must Do func start
  51 Must Do func end
  32 Must Do func start
  83 Door Handler func start
0000054253 [app] TRACE: D8_door_status: <door locked>
0000054253 [app] INFO: Door Handler executed: door locked
  83 Door Handler func start
0000054264 [app] TRACE: D8_door_status: <DOOR UNLOCKED!>
0000054264 [app] INFO: Door Handler executed: DOOR UNLOCKED!
0000054265 [app] INFO: Your door was UNLOCKED on Tue 26 Jan at 10:05AM.
Please ig
  83 Door Handler func start

  • this is my analysis with auto crash detection on start and end of function
    can i say that the issue with code is along my 3 handlers because they start but do not close? I’m kind of lost, I think it would be easier for me to understand if there was an example!

  • Update (Further testing):
    pub ambience + press,temp,door
    test1 - 4min runtime, stack overflow
    test2 - ??
    test3 - ??

pub ambience + sub press, temp = ??runtime, no overflow
pub ambience + sub press, door = ??runtime, no overflow

pub ambience + sub press = ??runtime, no overflow
pub ambience + sub temp = ??runtime, no overflow
pub ambience + sub door = ??runtime, no overflow

sub press, temp(5mins, status=online, no overflow)

sub press, door

  • test1 (10mins runtime, status=offline, no overflow)

sub press,temp,door (test1,2,3 lasted 2mins, status=offline)

  • test1 (auto disconnect, flash red 3x, cloud)
  • test2 (auto disconnect, cloud)
  • test3 (auto disconnect, flash red 3x, cloud)
  51 Must Do func end
  32 Must Do func start
 150 function start
 153 function end
 158 Pub Status func start
0000276027 [app] INFO: Ambience Status - Dark
 182 Pub Status func end
  51 Must Do func end
 195 Void loop end
 186 Void loop start
  32 Must Do func start
  51 Must Do func end

...
...
...
 153 function end
 158 Pub Status func start
0000279020 [app] INFO: Ambience Status - Dark
 182 Pub Status func end
  51 Must Do func end

...
...
...
  51 Must Do func end
 209 Greeting func start
 226 Greeting func end
  32 Must Do func start
  51 Must Do func end

...
...
...

  51 Must Do func end
  32 Must Do func start
  83 Door Handler func start
0000283475 [app] TRACE: D8_door_status: <door locked>
0000283475 [app] INFO: Door Handler executed: door locked
  97 Door Handler func end
  51 Must Do func end
  32 Must Do func start
  51 Must Do func end

...
...
...
  32 Must Do func start
  51 Must Do func end
  32 Must Do func start
  51 Must Do func end
  32 Must Do func start
  57 Temp Handler func start
0000284811 [app] TRACE: D8_temperature: <30.36>
0000284811 [app] INFO: Temperature Handler executed, 30.36°C
  70 Press Handler func start
0000284822 [app] TRACE: D8_pressure: <101118.45>
0000284822 [app] INFO: Pressure Handler executed, 101118.45Pa
  83 Door Handler func start

I said

That means you take 106 which the reference line prints and the look in your editor at which line number you find that reference line and then calculate the difference between these two numbers (printed_line_number minus editor_line_number = offset, should be positive).
Once you have the offset you subtract that from your printe_crash_line_number (e.g. 83) to know at which line number you need to look in your editor.

To make things a bit neater it would be advisable to have setup() and loop() be the first functions and all your own functions follow these. That way you'd also be sure, that the offset will be calculated correctly for the entire project.
Particularly mashing setup() and loop() between your own functions and then also have other functions between setup() and loop() is not only poor but bad coding style.

BTW, you can make things easier for you.
Instead of writhing a special text for each function like

void someFn() {
  Serial.println("%4d someFn start", __LINE__);
  ...
  Serial.println("%4d someFn end", __LINE__);
}

just write

void someFn() {
  Serial.println(">(%4d) %s", __LINE__, __FUNCTION__);
  ...
  Serial.println("<(%4d) %s", __LINE__, __FUNCTION__);
}

If you have multiple return statements, you need a "<" line infront of all of them.

Hi ScruffR! I think I understand now! So my code editor line 26 is where i put the Serial.print(), while the reference line number printed was 26. So the difference between this offset is.. 0?

  • So now my offset is 0, I subtract this 0 from crash line number 144, thus 144 - 0 = 144! Now I look at line 144 in my editor!
Code editor

25  void setup(){
26      Serial.printlnf("%4d <-- reference line", __LINE__);
27      pinMode(led3, OUTPUT); 
 26 <-- reference line
  47 setup end
  52 void loop start
 178 display start
 181 display end
...
...
 201 greet end
 124 temp hand start
 134 press hand start
 144 door hand start
  • So with reference to this, Since line 144 (handler start) was the last line printed before crash was executed, the next was not reached (handler end). This means I have to add more crash lines between line144 (handler start) and handler end to find the error precisely?

This assertion seems reasonable.
If you want to drill down further you can add some more print lines between all the code lines in that region.

Working on it right now!

I forgot to include an “else” after the “if” statement! I just tested it and my code works!! its been running for 10minutes and it has just crashed again! Now the crash goes here, reading code again to check for errors right now…

 162 door hand else start
 124 temp hand start
 134 press hand start

BEFORE

        lcd3.print(0,0, "DOOR UNLOCKED!");
    }
    lcd3.print(0,0, "door locked   ");
}

AFTER 
    }
    else{
        Serial.printlnf("%4d door hand else start", __LINE__); delay(10);
        lcd3.print(0,0, "door locked   ");
        Serial.printlnf("%4d door hand else end LOCEKD", __LINE__); delay(10);
    }
    Serial.printlnf("%4d door hand after if else{} <here>", __LINE__); delay(10);
    Serial.printlnf("%4d door hand end", __LINE__); delay(10);
}

 134 press hand start
 144 door hand start
 159 door hand after if{} <here>

When you have the line numbers you don’t need to modify the text of the message for each line. That makes debugging unnecessarily cumbersome.

Hmm… seems like I should have used float val = atof(data) instead of double val = atof(data). It takes around 10minutes for stack overflow to occur!

Hi ScruffR! Okay thanks I’ll keep that in mind! Quick question, if i having float val = atof(data) = 32.00000
and comparing like this (val > 32.50) gives error, Do i use (val > 32.5 instead)

  • regarding the softDelay function, the doloop() func is executed first, followed by the softdelay. So if I remove teh doloop() inside the softdelay, does it act like a normal delay() or still act as a softdelay()?
void softDelay(uint32_t timeout) {
  for (uint32_t ms = millis(); millis() - ms < timeout; DoLoop());
}

I’ll be back tomorrow to debug again!