Set subscribe handler data as global

Ahh i see! So far no crash with a single subscribe used. Currently im trying to implement MQTT pub/sub to replace particle pub/sub but im facing issues connecting my localhost.
It doesnt connect to 127.0.0.1;1883 !

How do I connect to localhost?

127.0.0.1 would refer to the Photon itself :wink:

Your local MQTT broker has probably a 192.168.x.y or 10.0.0.x address which you should either get from the device running the broker or your router.

Hi ScruffR, thanks! I managed to get MQTT to work using wifi hotspot. I also tidied up mode of my code using timers. I forgot to mention that I’m also sending data from particle.variable(ldr/temp/pressure) to HTML file.


After tidying up my code, the red SOS still occurs every 10seconds after code successfully flashes.

Update: I commented away timer4.start() yet the red SOS continues, I suspect the timer time length is causing this …

Q1. Should I run the auto crash detection and debug to fix the SOS?

Q2. Should I sync the timer time length on Device1 Timer3&4 same as Device2 Timer1&2? I’m not sure if I’m using timer correctly in both device codes.

Q3. I put my particle sub/pub handlers into timers hoping it would fix the red SOS since i expected timers to work in a sequential manner.
Example:
Timer timer1(1000L, pub_ambience_status);
Timer timer2(5000L, blynkLDRdata);
Timer timer3(5000L, subTemp);
Timer timer4(1000L, subPress);

void setup(){
timer1.start();
timer2.start();
timer3.start();
timer4.start();
}

timer1 (1s) → timer2 (5s) → timer3 (5s) → timer4 (1s) and repeat again

(timer1)       (timer2)       (timer3)        (timer4)

0-------------1---------------6---------------11------------12

(timer1)       (timer2)       (timer3)        (timer4)

12-----------13--------------18-------------23------------24

<Device 1 Code>

#include <DailyTimerSpark.h>
#include <blynk.h>

char auth[] = "HIDE";

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

int ldr=A0;              // Blynk Ambience Brightness 
int ldr_status=0;        // Blynk Ambience Brightness 
int ldranalogvalue=0;    // Blynk Ambience Brightness Gauge

WidgetLCD lcd(V1);       // Blynk LCD (Date Day Greeting)
WidgetLCD lcd2(V3);

// Check ambience status every 30s
Timer timer1(1000L, pub_ambience_status);

// Blynk LDR Gauge
Timer timer2(5000L, blynkLDRdata);

// iot_C8 MQTT sub to temp and pressure data vals from D8 published temp and pressure readings
Timer timer3(5000L, subTemp);
Timer timer4(1000L, subPress);


void pub_ambience_status(){
       ldr_status = analogRead(ldr);
       Serial.println("Ambience Change");
       Serial.printlnf("LDR Status = %d", ldr_status);

    if (ldr_status <= 100){    // Modify this in class
        Particle.publish("ambience_C8", "Dark", PRIVATE);
        Serial.println("Ambience Status= Dark");
        Serial.println("Ambience Dark Request triggered");
    }
    
    else if (ldr_status >= 300){
        Particle.publish("ambience_C8", "Bright", PRIVATE);
        Serial.println("Ambience Status= Bright");
        Serial.println("Ambience Bright Request triggered");
    }
    
    else{
        Particle.publish("ambience_C8", "Dim", PUBLIC);
        Serial.println("Ambience Status= Dim");
        Serial.println("Ambience Dim Request triggered");
    }
}

void blynkLDRdata(){
   ldr_status = analogRead(ldr);
   Blynk.virtualWrite(V0, ldr_status);
}

void subTemp(){
   Particle.subscribe("temperature_D8", myTempHandler, MY_DEVICES);
}

// Get Temperature data from subscribed topic and print on LCD
void myTempHandler(const char *event, const char *data){
   double temp_val = atof(data);
   Serial.printlnf("Temperature Handler executed, %f°C", temp_val);

   char msg[32];
   snprintf(msg, sizeof(msg), "%.2f°C %s", temp_val, (temp_val > 32.50) ? "Warm" : "Cool");
   lcd2.print(0, 0, msg);
}

void subPress(){
   Particle.subscribe("pressure_D8", myPressHandler, MY_DEVICES);
}

void myPressHandler(const char *event, const char *data){
   double press_val = atof(data);
   Serial.printlnf("Pressure Handler executed, %fPa", press_val);

   char msg[32];
   snprintf(msg, sizeof(msg), "%dPa %s", press_val, (press_val > 900) ? "High" : "Low");
   lcd2.print(0, 0, msg);
}

void setup(){
   
   // bLYNK LCD Time Display (Set Timezone to SG using +8)
   Time.zone(+8);  
   
   // Nightlight Toggle On/Off
   pinMode(led3, OUTPUT); 
   Particle.function("led",ledToggle);

   // HTML Ambience Brightness Gauge
   ldr_status=analogRead(ldr);
   Particle.variable("ldranalogvalue", ldr_status);
   
   Serial.begin(9600);
   while (!Serial);
   Serial.println("Serial is Running");
   
   Blynk.begin(auth); 
   
   timer1.start();
   timer2.start();
   timer3.start();
   timer4.start();
}


void loop(){                     
   
   Blynk.run();

   // Blynk LCD Display (Date + Greeting)
   DisplayTime();
   MorningDisplay();
   AfternoonDisplay();

}


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

   else {
       return -1;
   }
}


void DisplayTime(){

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

void MorningDisplay(){
   DailyTimer MorningTimer(6, 0, 11, 59, EVERY_DAY);  // Fixed start time and end time
   if(MorningTimer.startTrigger()){
       lcd.print(0,1, "Good Morning!");
       delay(2000);
       
       MorningMsg();
       delay(1000);
   }
}

void AfternoonDisplay(){
   DailyTimer AfternoonTimer(12, 0, 17, 59, EVERY_DAY);  // Fixed start time and end time
   if(AfternoonTimer.startTrigger()){
       lcd.print(0,1, "Good Afternoon! ");
       delay(1000);
       
       AfternoonMsg();
       delay(1000);
   }
}


void MorningMsg(){
   String text = "Time for Breakfast";
   int space=(strlen(text)-15);                 // Set no. of space for text to move
   for(int start=0; start<space; start++){      // Increment loop
       
       lcd.print(0,1, text.substring(start));   // substring func remove char of string via loop and prints continuously
       delay(1000);
   }
}

void AfternoonMsg(){
   String text = "Lunch Break     ";            // Spaces to avoid other text from overlapping
   lcd.print(0,1, text);   
   delay(1000);
}

<Device 2 Code>

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

Adafruit_BMP280 bmp; 

char auth[] = "HIDE";  

double env_temp;
double env_Pressure;

// HTML Temp + Press Gage Data
double temp_reading;
double press_reading;

// Blynk Slider + Button
int led1 = D2;

Timer timer1(5000L, pubTemp);
Timer timer2(10000L, pubPress);

// iot_D8 Device MQTT subscribe to Ambience_C8
Timer timer3(10000L, subAmbience);

void pubTemp(){
   double t_temp = bmp.readTemperature();
   Serial.println("Temperature Request triggered");
   Particle.publish("temperature_D8", String(t_temp), PRIVATE);
}

void pubPress(){
   double t_Pressure = bmp.readPressure();
   Serial.println("Pressure Request triggered");
   Particle.publish("pressure_D8", String(t_Pressure), PRIVATE);
}

void subAmbience(){
   Particle.subscribe("ambience_C8", myAmbienceHandler, MY_DEVICES); 
}

// Get data from subscribed topic ambience_C8, on / off led according to ambience status
void myAmbienceHandler(const char *event, const char *data){
   String ambience_status = String(data);
   Serial.println("Ambience Handler executed");
   Serial.println(ambience_status);

   if(ambience_status == "Dark"){
       digitalWrite(D2, HIGH);
       Serial.println("Led turns on");
   }
   else if(ambience_status == "Bright"){
       digitalWrite(D2, LOW);
       Serial.println("Led stays off");
   }
   else if(ambience_status == "Dim"){
       digitalWrite(D2, LOW);
       Serial.println("Led stays off");
   }
}



void setup() {
   
   pinMode(led1, OUTPUT);

   // HTML Temp and pressure gauge reading
   Particle.variable("temp", temp_reading);    
   Particle.variable("press", press_reading);    
   
   Blynk.begin(auth); 

   Serial.begin(9600);
   while (!Serial);
   Serial.println("Serial is Running");

   if (!bmp.begin()){
       Serial.println("Can't find the sensor BMP280");
   }
   
   timer1.start();
   timer2.start();
   timer3.start();
}

void loop() {
   Blynk.run();

   temp_reading = bmp.readTemperature();
   press_reading = bmp.readPressure();
}

I doubt that. It’s usually not helpful to jump to conclusions before having evidence for it.
So yes to Q1.
Q2 is a non-issue IMO although having multiple timers with the same cadence would rather suggest to do all the stuff in the same timer (if it has to be via timer at all is open for discussion).
For Q3, no. your 1sec timers will fire 5 times in 5 seconds while your 5sec timers will fire only once. And in the long run you should not rely on the order of execution at all.

When using timers you need to be aware that the thread running these timers only has a very limited stack space and hence any heavy library calls (i.e. Blynk) may run the risk of overflowing the stack.

Typically when you get an SOS crash you also get a number of slow blinks after the first set of SOS blinks. The number of these blinks tells you more about the type of crash.
If this is indeed a stack issue you’d see SOS+13.
For that it’s always good to also provide that information as early as possible in a discussion like this.

https://docs.particle.io/tutorials/device-os/led/photon/#red-flash-sos

While Software Timers are a great tool for some things, they are not suitable for everything and in your case I don’t see good reason for them.

I’d guess your initial SOS crash was of a different kind. That also shows that it is not advisable to considerably change code you still haven’t fully debugged.
Adding new features (in this case Software Timers) may well introduce new problems of its own and makes debugging much harder (is it still the old problem or a new one or a combination of the two).

BTW, why on earth have you got subTemp(), subPress() and subAmbience timers that keep subscribing to the same events over and over every few seconds?

About this

Since programming is a matter where preciseness is key I also want to be precise when describing things verbally :wink:
Particle.variable()s are requested not sent.

Hey ScruffR! My initial crash is indeed different from the current one is due to stack overflow.

Q1. Is panic error and stack_overflow error the same?

Q2. In device1 code, im using software timer2 to write data to blynk gauge, however it doesnt seem to work with blynk and is causing the red SOS.

With new code here, I’ve bottled down the two issues of red SOS.

Device 1 updated code has the following:
Timer 1 - publishes ambience_c8, no subscribes are inside.
Timer 2 - publishes ldrdata to blynk (This causes red SOS) [I couldnt get blynktimer timer; to work thus i used software timer], remove timer 2 and device works fine
Prints displaytime and message function to blynk widget lcd(v1) via lcd.print.

Device 2 updated code has the following:
Timer 1 - publishes temp
Timer 2 - publishes pressure
Subscribes to temp, pressure and ambience_c8 (This causes panic, hard_fault, not sure what this is though still flash red)
Prints temp and pressure to blynk widget lcd2(V3) via lcd2.print

I understand that IoT integration with blynk and particle requires the void loop to be as clean as possible containing only
blynk.run() and timer.run().

Q3. In device1 code, I know the displaytime() with delay(1000) and displaymorningmsg() in void loop causes the blynk app connection instability, however I have no idea how to change the code. If i put these two functions in void setup, then only displaytime() lcd.print(0,1) will display.

Q4. Now I know that int (32bit int) vs long int (64bit int), I will remove the L from all my timers.

<Device 1 code>

#include <DailyTimerSpark.h>
#include <blynk.h>

char auth[] = "HIDDEN";
    
int led3=D0;             // Blynk Nightlight Control + HTML Nightlight Toggle On/Off
int led3_status=-1;      // HTML Nightlight Toggle On/Off

int ldr=A0;              // Blynk Ambience Brightness 
int ldr_status=0;        // Blynk Ambience Brightness 
int ldranalogvalue=0;    // Blynk Ambience Brightness Gauge

WidgetLCD lcd(V1);       // Blynk LCD (Date Day Greeting)
WidgetLCD lcd2(V3);

// Check ambience status every 1min
Timer timer1(60000, pub_ambience_status);

// Blynk LDR Gauge
Timer timer2(1000, blynkLDRdata);

void pub_ambience_status(){
    if(analogRead(ldr) >= (ldr_status+30) || analogRead(ldr) <= (ldr_status-30)) //50
    {
        ldr_status = analogRead(ldr);
        Serial.println("Ambience Change");
        Serial.printlnf("LDR Status = %d", ldr_status);

        if (ldr_status <= 100){    // Modify this in class
         Particle.publish("ambience_C8", "Dark", PRIVATE);
         Serial.println("Ambience Status= Dark");
         Serial.println("Ambience Dark Request triggered");
        }
     
         else if (ldr_status >= 300){
         Particle.publish("ambience_C8", "Bright", PRIVATE);
         Serial.println("Ambience Status= Bright");
         Serial.println("Ambience Bright Request triggered");
        }
     
        else{
         Particle.publish("ambience_C8", "Dim", PUBLIC);
         Serial.println("Ambience Status= Dim");
         Serial.println("Ambience Dim Request triggered");
        }
    }
}

void blynkLDRdata(){
    ldr_status = analogRead(ldr);
    Blynk.virtualWrite(V0, ldr_status);
}

// Get Temperature data from subscribed topic and print on LCD
void myTempHandler(const char *event, const char *data){
    double temp_val = atof(data);
    Serial.printlnf("Temperature Handler executed, %f°C", temp_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2f°C %s", temp_val, (temp_val > 32.50) ? "Warm" : "Cool");
    lcd2.print(0, 0, msg);
}

void myPressHandler(const char *event, const char *data){
    double press_val = atof(data);
    Serial.printlnf("Pressure Handler executed, %fPa", press_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%dPa %s", press_val, (press_val > 900) ? "High" : "Low");
    lcd2.print(0, 1, msg);
}


void setup(){
    DisplayTime();
    MorningDisplay();
    
    // 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);  

    // IP ADDRESS
    // iot_C8 MQTT sub to temp and pressure data vals from D8 published temp and pressure readings
    //Particle.subscribe("temperature_D8", myTempHandler, MY_DEVICES);
    //Particle.subscribe("pressure_D8", myPressHandler, MY_DEVICES);
    
    Serial.begin(9600);
    while (!Serial);
    Serial.println("Serial is Running");
    Serial.println("Blynk is running. Ensure wifi connected on device and app are same");
    
    timer1.start();
    //timer2.start();
}


void loop(){                     
    
    // Run Blynk App
    Blynk.run();
    
    // Blynk LDR Gauge
    //Blynk.virtualWrite(V0, ldr_status);
    
    // Blynk LCD Display (Date + Greeting)
    //DisplayTime();
    //MorningDisplay();
}



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

    else {
        return -1;
    }
}


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


void MorningDisplay(){
    DailyTimer MorningTimer(6, 0, 11, 59, EVERY_DAY);  // Fixed start time and end time
    if(MorningTimer.startTrigger()){
        lcd.print(0,1, "Good Morning!");
    }
}

<Device 2 code>

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


Adafruit_BMP280 bmp; // I2C

WidgetLCD lcd2(V3);


char auth[] = "HIDDEN";  

double env_temp;
double env_Pressure;

// HTML Temp + Press Gage Data
double temp_reading;
double press_reading;

// Blynk Slider + Button
int led1 = D2;

Timer timer1(5000, pubTemp);
Timer timer2(20000, pubPress);

void myTempHandler(const char *event, const char *data){
    double temp_val = atof(data);
    Serial.printlnf("Temperature Handler executed, %f°C", temp_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2f°C %s", temp_val, (temp_val > 32.50) ? "Warm" : "Cool");
    lcd2.print(0, 0, msg);
}

void myPressHandler(const char *event, const char *data){
    double press_val = atof(data);
    Serial.printlnf("Pressure Handler executed, %.2fPa", press_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2fPa %s", press_val, (press_val > 900) ? "High" : "Low");
    lcd2.print(0, 1, msg);
}

void pubTemp(){
    double t_temp = bmp.readTemperature();
    Serial.println("Temperature Request triggered");
    Particle.publish("temperature_D8", String(t_temp), PRIVATE);
}

void pubPress(){
    double t_Pressure = bmp.readPressure();
    Serial.println("Pressure Request triggered");
    Particle.publish("pressure_D8", String(t_Pressure), PRIVATE);
}

// Get data from subscribed topic ambience_C8, on / off led according to ambience status
void myAmbienceHandler(const char *event, const char *data){
    String ambience_status = String(data);
    Serial.println("Ambience Handler executed");
    Serial.println(ambience_status);

    if(ambience_status == "Dark"){
        digitalWrite(D2, HIGH);
        Serial.println("Led turns on");
    }
    else if(ambience_status == "Bright"){
        digitalWrite(D2, LOW);
        Serial.println("Led stays off");
    }
    else if(ambience_status == "Dim"){
        digitalWrite(D2, LOW);
        Serial.println("Led stays off");
    }
}


void setup() {
    
    pinMode(led1, OUTPUT);

    // HTML Temp and pressure gauge reading
    Particle.variable("temp", temp_reading);    
    Particle.variable("press", press_reading);    
    
    
    // iot_D8 Device MQTT subscribe to Ambience_C8
    Particle.subscribe("ambience_C8", myAmbienceHandler, MY_DEVICES); 
    
    Particle.subscribe("temperature_D8", myTempHandler, MY_DEVICES);
    Particle.subscribe("pressure_D8", myPressHandler, MY_DEVICES);
    

    Blynk.begin(auth); 

    Serial.begin(9600);
    while (!Serial);
    Serial.println("Serial is Running");
    Serial.println("Blynk is running. Ensure wifi connected on device and app are same");
 
    if (!bmp.begin()){
        Serial.println("Can't find the sensor BMP280");
    }
    
    timer1.start();
    timer2.start();
}

void loop() {
    Blynk.run();

    temp_reading = bmp.readTemperature();
    press_reading = bmp.readPressure();
}

Update 2:

Now I’ve made some changes to both device codes. I’m only getting red SOS on device 1 (13 blinks ) when I use timer2.start().

Not sure why the hard_fault error earlier is gone but i guess thats a good sign!

Device 1 code:

  • Timer1- publish amibnece_c8 every 1min (updates ldr val to HTML every 1min)
  • timer 2 - publish ldrdata to blynk (causes red sos) [ I just need to fix this but blynktimer timer; doesnt work :frowning: )
  • subscribes to press and temp in void setup,
  • removed displaytimer and msg() in void loop, placed inside setup instead. prints only %a, %e, %b on lcd(0,1), not sure why lcd(0,0) not printing %I:%M%p.
  • prints temp and pressure display on lcd2

Device 2 code

  • timer 1 - publishes temp every 5s
  • timer 2 - publishes pressure every 20s
  • subscribes to ambience_c8 only (NO panic, hard_fault, works fine!)

<Device 1 code>

#include <DailyTimerSpark.h>
#include <blynk.h>

char auth[] = "HIDDEN";
    
int led3=D0;             // Blynk Nightlight Control + HTML Nightlight Toggle On/Off
int led3_status=-1;      // HTML Nightlight Toggle On/Off

int ldr=A0;              // Blynk Ambience Brightness 
int ldr_status=0;        // Blynk Ambience Brightness 
int ldranalogvalue=0;    // Blynk Ambience Brightness Gauge

WidgetLCD lcd(V1);       // Blynk LCD (Date Day Greeting)
WidgetLCD lcd2(V3);

// Check ambience status every 1min
Timer timer1(60000, pub_ambience_status);

// Blynk LDR Gauge
Timer timer2(60000, blynkLDRdata);

void pub_ambience_status(){
    if(analogRead(ldr) >= (ldr_status+30) || analogRead(ldr) <= (ldr_status-30)) //50
    {
        ldr_status = analogRead(ldr);
        Serial.println("Ambience Change");
        Serial.printlnf("LDR Status = %d", ldr_status);

        if (ldr_status <= 100){    // Modify this in class
         Particle.publish("ambience_C8", "Dark", PRIVATE);
         Serial.println("Ambience Status= Dark");
         Serial.println("Ambience Dark Request triggered");
        }
     
         else if (ldr_status >= 300){
         Particle.publish("ambience_C8", "Bright", PRIVATE);
         Serial.println("Ambience Status= Bright");
         Serial.println("Ambience Bright Request triggered");
        }
     
        else{
         Particle.publish("ambience_C8", "Dim", PUBLIC);
         Serial.println("Ambience Status= Dim");
         Serial.println("Ambience Dim Request triggered");
        }
    }
}

void blynkLDRdata(){
    ldr_status = analogRead(ldr);
    Blynk.virtualWrite(V0, ldr_status);
}

// Get Temperature data from subscribed topic and print on LCD
void myTempHandler(const char *event, const char *data){
    double temp_val = atof(data);
    Serial.printlnf("Temperature Handler executed, %f°C", temp_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2f°C %s", temp_val, (temp_val > 32.50) ? "Warm" : "Cool");
    lcd2.print(0, 0, msg);
}

void myPressHandler(const char *event, const char *data){
    double press_val = atof(data);
    Serial.printlnf("Pressure Handler executed, %.2fPa", press_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2fPa %s", press_val, (press_val > 900) ? "High" : "Low");
    lcd2.print(0, 1, msg);
}


void setup(){
    DisplayTime();
    MorningDisplay();
    
    // 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);  

    // IP ADDRESS
    // iot_C8 MQTT sub to temp and pressure data vals from D8 published temp and pressure readings
    Particle.subscribe("temperature_D8", myTempHandler, MY_DEVICES);
    Particle.subscribe("pressure_D8", myPressHandler, MY_DEVICES);
    
    Serial.begin(9600);
    while (!Serial);
    Serial.println("Serial is Running");
    Serial.println("Blynk is running. Ensure wifi connected on device and app are same");
    
    timer1.start();
    //timer2.start();
}


void loop(){                     
    
    // Run Blynk App
    Blynk.run();
    
    // Blynk LDR Gauge
    //Blynk.virtualWrite(V0, ldr_status);
    
    // Blynk LCD Display (Date + Greeting)
    //DisplayTime();
    //MorningDisplay();
}



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

    else {
        return -1;
    }
}


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


void MorningDisplay(){
    DailyTimer MorningTimer(6, 0, 11, 59, EVERY_DAY);  // Fixed start time and end time
    if(MorningTimer.startTrigger()){
        lcd.print(0,1, "Good Morning!");
    }
}

<Device 2 code >

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


Adafruit_BMP280 bmp; // I2C

WidgetLCD lcd2(V3);


char auth[] = "HIDDEN";  

double env_temp;
double env_Pressure;

// HTML Temp + Press Gage Data
double temp_reading;
double press_reading;

// Blynk Slider + Button
int led1 = D2;

Timer timer1(5000, pubTemp);
Timer timer2(20000, pubPress);

void pubTemp(){
    double t_temp = bmp.readTemperature();
    Serial.println("Temperature Request triggered");
    Particle.publish("temperature_D8", String(t_temp), PRIVATE);
}

void pubPress(){
    double t_Pressure = bmp.readPressure();
    Serial.println("Pressure Request triggered");
    Particle.publish("pressure_D8", String(t_Pressure), PRIVATE);
}

// Get Temperature data from subscribed topic and print on LCD
void myTempHandler(const char *event, const char *data){
    double temp_val = atof(data);
    Serial.printlnf("Temperature Handler executed, %f°C", temp_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2f°C %s", temp_val, (temp_val > 32.50) ? "Warm" : "Cool");
    lcd2.print(0, 0, msg);
}

void myPressHandler(const char *event, const char *data){
    double press_val = atof(data);
    Serial.printlnf("Pressure Handler executed, %.2fPa", press_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2fPa %s", press_val, (press_val > 900) ? "High" : "Low");
    lcd2.print(0, 1, msg);
}

// Get data from subscribed topic ambience_C8, on / off led according to ambience status
void myAmbienceHandler(const char *event, const char *data){
    String ambience_status = String(data);
    Serial.println("Ambience Handler executed");
    Serial.println(ambience_status);

    if(ambience_status == "Dark"){
        digitalWrite(D2, HIGH);
        Serial.println("Led turns on");
    }
    else if(ambience_status == "Bright"){
        digitalWrite(D2, LOW);
        Serial.println("Led stays off");
    }
    else if(ambience_status == "Dim"){
        digitalWrite(D2, LOW);
        Serial.println("Led stays off");
    }
}


void setup() {
    
    pinMode(led1, OUTPUT);

    // HTML Temp and pressure gauge reading
    Particle.variable("temp", temp_reading);    
    Particle.variable("press", press_reading);    
    
    
    // iot_D8 Device MQTT subscribe to Ambience_C8
    Particle.subscribe("ambience_C8", myAmbienceHandler, MY_DEVICES); 
    
    //Particle.subscribe("temperature_D8", myTempHandler, MY_DEVICES);
    //Particle.subscribe("pressure_D8", myPressHandler, MY_DEVICES);
    

    Blynk.begin(auth); 

    Serial.begin(9600);
    while (!Serial);
    Serial.println("Serial is Running");
    Serial.println("Blynk is running. Ensure wifi connected on device and app are same");
 
    if (!bmp.begin()){
        Serial.println("Can't find the sensor BMP280");
    }
    
    timer1.start();
    timer2.start();
}

void loop() {
    Blynk.run();

    temp_reading = bmp.readTemperature();
    press_reading = bmp.readPressure();
}

“Panic crash” is the umbrella term for all SOS+x errors and “stack overflow” (SOS+13) is one of them.

While this isn’t really a question, that’s what I was referring to with this

Hmm, how come? I provided you with a link that would have clarified that and also listed some common causes :wink:

This is a very simplistic view. You can still keep loop() clean and tidy even with doing other stuff therein. You just need to avoid anything that may bog down the code flow for extended periods (e.g. delay(), tight loops, …).

One way to do that is by conciously using timers (i.e. one-shot timers) or a dedicated “soft delay” function that keeps your essential tasks running while waiting.

e.g.

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

// cannot be called from a Software Timer!
void mustDo() {
  if (Particle.connected()) {
    Blynk.run(); // not Blynk.loop();
    timer.run(); // not Blynk.timer();
    if (ldrNeedUpdate) {
      ldr_status = analogRead(ldr);
      Blynk.virtualWrite(V0, ldr_status);
      ldrNeedUpdate = false;
    }
  }
  Particle.process();
}

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

void loop() {
  mustDo();
}

Whenever you need a delay all the essential stuff will still be executed while waiting.
Alternatively I’d even consider using an FSM for the display which would be called in the mustDo() function and can be controlled from your other code - including timers.

I’d also not read the BMP sensor on each iteration of loop(), particularly when only using the readings every few seconds.

BTW, Blynk timers are nothing else than “obscured” a millis() timers.

Hi ScruffR! I noticed that the red SOS would appear when my device1 timer1 (pub_ambience) duration is set to 3 seconds. However at 30seconds or 60seconds, the red SOS does not appear.

Q5. I was wondering if using switch-case would work with timers than compared to if-elseif-else statement.

I tried your example code but Blynk.loop() and Blynk.timer() gives blynkparticle does not contain member loop/timer! I commented them out and pasted the code, the ldr value wasnt updated :frowning: Also, how do I set the timeout or softdelay duration?

Despite that, I was able to use BlynkTimer timer; to update ldr value in blynk app and also displaytime.

BlynkTimer timer;

void setup(){    
timer.setInterval(3000, ldrData);
}

void ldrData(){
    ldr_status=analogRead(ldr);
    Blynk.virtualWrite(V0, ldr_status);
    
    DisplayTime();
}

void loop(){
timer.run();
}

Q6. What can I use to replace the delay function which will not interrupt blynk.run in void loop since I want to display the day + date for two seconds, followed by a greeting message (e.g good morning) for two seconds and repeat this cycle.

Meanwhile, im doing some reading up on one shot timers and soft delays here…

Update 2:

For device 1, Instead of using software timer to publish this value, I decided to use the BlynkTimer timer; and place my if-elseif-else statement + publish ambience value. However, the Red SOS occurs after 3minutes of running the code and connects back to cloud.

Q7. How should I optimize this?

Q8. I plan put temp and pressure publish and reading in BlynkTimer timer; as well, do you recommend this?

Device 1 Code

#include <DailyTimerSpark.h>
#include <blynk.h>

BlynkTimer timer;

char auth[] = "HIDDEN";
    
int led3=D0;             // Blynk Nightlight Control + HTML Nightlight Toggle On/Off
int led3_status=-1;      // HTML Nightlight Toggle On/Off

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

WidgetLCD lcd(V1);       // Blynk LCD (Date Day Greeting)
WidgetLCD lcd2(V3);      // Blynk LCD (Temp + Pressure)

// Check ambience status every 30 seconds
/*Timer timer1(30000, pub_ambience_status);

void pub_ambience_status(){

        ldr_status = analogRead(ldr);
        Serial.println("Ambience Change");
        Serial.printlnf("LDR Status = %d", ldr_status);

        if (ldr_status <= 100){    // Modify this in class
         Particle.publish("ambience_C8", "Dark", PRIVATE);
         Serial.println("Ambience Status= Dark");
         Serial.println("Ambience Dark Request triggered");
        }
     
         else if (ldr_status >= 300){
         Particle.publish("ambience_C8", "Bright", PRIVATE);
         Serial.println("Ambience Status= Bright");
         Serial.println("Ambience Bright Request triggered");
        }
     
        else{
         Particle.publish("ambience_C8", "Dim", PUBLIC);
         Serial.println("Ambience Status= Dim");
         Serial.println("Ambience Dim Request triggered");
        }
}*/


// Get Temperature data from subscribed topic and print on LCD
void myTempHandler(const char *event, const char *data){
    double temp_val = atof(data);
    Serial.printlnf("Temperature Handler executed, %f°C", temp_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2f°C %s", temp_val, (temp_val > 32.50) ? "Warm" : "Cool");
    lcd2.print(0, 0, msg);
}

void myPressHandler(const char *event, const char *data){
    double press_val = atof(data);
    Serial.printlnf("Pressure Handler executed, %.2fPa", press_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2fPa %s", press_val, (press_val > 900) ? "High" : "Low");
    lcd2.print(0, 1, msg);
}


void setup(){
    //MorningDisplay();
    
    // 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();

    // IP ADDRESS
    // iot_C8 MQTT sub to temp and pressure data vals from D8 published temp and pressure readings
    Particle.subscribe("temperature_D8", myTempHandler, MY_DEVICES);
    Particle.subscribe("pressure_D8", myPressHandler, MY_DEVICES);
    
    Serial.begin(9600);
    while (!Serial);
    Serial.println("Serial is Running");
    Serial.println("Blynk is running. Ensure wifi connected on device and app are same");
    
    //timer1.start();
    
    // Push ldrdata into blynk app every 3 seconds (Prevents floodings)
    timer.setInterval(3000, ambience_status);
}


void ambience_status(){
    // update ldr_value to gauge
    ldr_status=analogRead(ldr);
    Blynk.virtualWrite(V0, ldr_status);
    
    // Publish ambience_status
    if (ldr_status <= 100){  
        lcd.print(9,0, "Dark  ");
        Particle.publish("ambience_C8", "Dark", PRIVATE);
        Serial.println("Ambience Status= Dark");
        Serial.println("Ambience Dark Request triggered");
    }
    
    else if (ldr_status >= 300){    
        lcd.print(9,0, "Bright");
        Particle.publish("ambience_C8", "Bright", PRIVATE);
        Serial.println("Ambience Status= Bright");
        Serial.println("Ambience Bright Request triggered");
    }
    
    else{
        lcd.print(9,0, "Dim   ");
        Particle.publish("ambience_C8", "Dim", PRIVATE);
        Serial.println("Ambience Status= Dim");
        Serial.println("Ambience Dim Request triggered");
    }


    DisplayTime();
    DisplayDate();
}

void loop(){                     
    Blynk.run();
    timer.run();
}

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 MorningDisplay(){
    DailyTimer MorningTimer(6, 0, 11, 59, EVERY_DAY); 
    if(MorningTimer.startTrigger()){
        lcd.print(0,1, "Good Morning!");
    }
}


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

    else {
        return -1;
    }
}

Device 2 Code

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

Adafruit_BMP280 bmp; // I2C

WidgetLCD lcd2(V3);


char auth[] = "HIDDEN";  

double env_temp;
double env_Pressure;

// HTML Temp + Press Gage Data
double temp_reading;
double press_reading;

// Blynk Slider + Button
int led1 = D2;

// Publish temp val every 5s
Timer timer1(5000, pubTemp);
// Publish press val every 20s
Timer timer2(20000, pubPress);

void pubTemp(){
    double t_temp = bmp.readTemperature();
    Serial.println("Temperature Request triggered");
    Particle.publish("temperature_D8", String(t_temp), PRIVATE);
}

void pubPress(){
    double t_Pressure = bmp.readPressure();
    Serial.println("Pressure Request triggered");
    Particle.publish("pressure_D8", String(t_Pressure), PRIVATE);
}

// Get Temperature data from subscribed topic and print on LCD
void myTempHandler(const char *event, const char *data){
    double temp_val = atof(data);
    Serial.printlnf("Temperature Handler executed, %f°C", temp_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2f°C %s", temp_val, (temp_val > 32.50) ? "Warm" : "Cool");
    lcd2.print(0, 0, msg);
}

void myPressHandler(const char *event, const char *data){
    double press_val = atof(data);
    Serial.printlnf("Pressure Handler executed, %.2fPa", press_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2fPa %s", press_val, (press_val > 900) ? "High" : "Low");
    lcd2.print(0, 1, msg);
}

// Get data from subscribed topic ambience_C8, on / off led according to ambience status
void myAmbienceHandler(const char *event, const char *data){
    String ambience_status = String(data);
    Serial.println("Ambience Handler executed");
    Serial.println(ambience_status);

    if(ambience_status == "Dark"){
        digitalWrite(D2, HIGH);
        Serial.println("Led turns on");
    }
    else if(ambience_status == "Bright"){
        digitalWrite(D2, LOW);
        Serial.println("Led stays off");
    }
    else if(ambience_status == "Dim"){
        digitalWrite(D2, LOW);
        Serial.println("Led stays off");
    }
}


void setup() {
    
    pinMode(led1, OUTPUT);

    // HTML Temp and pressure gauge reading
    Particle.variable("temp", temp_reading);    
    Particle.variable("press", press_reading);    
    
    
    // iot_D8 Device MQTT subscribe to Ambience_C8
    Particle.subscribe("ambience_C8", myAmbienceHandler, MY_DEVICES); 

    Blynk.begin(auth); 

    Serial.begin(9600);
    while (!Serial);
    Serial.println("Serial is Running");
    Serial.println("Blynk is running. Ensure wifi connected on device and app are same");
 
    if (!bmp.begin()){
        Serial.println("Can't find the sensor BMP280");
    }
    
    timer1.start();
    timer2.start();
}

void loop() {
    Blynk.run();

    temp_reading = bmp.readTemperature();
    press_reading = bmp.readPressure();
}

switch-case is a common C concept and works irrespective of location. The only thing to consider that it can only work on ordinal values (not strings nor floating point values).

My bad, it should obviously be Blynk.run() instead of Blynk.loop() (corrected above) and Blynk.timer() should rather be timer.run() :blush:

Just as you would with delay() pass the desired time as parameter softDelay(1000).

That is what softDelay() would do. In both cases - when running loop() or stuck inside softDelay() the important stuff will be attended to via the mustDo() calls.

Hi ScruffR! Thanks for your advice! I put my particle.publish inside the Mustdo() function using a couple of if-statements to replace use of software timer and also iterate through bmp.reading in mustDo function as well!

Q1. I have a physical button and widget button. I am able to code the physical button to publish data when pressed. However, I cant seem to sync widget button state to physical button.
I want both widget button and physical button to publish data when pressed. How can I achieve this?
I tried this method to read state of button V4:

BLYNK_READ(V4){
    if(BLYNK_READ(V4)==HIGH){
         Particle.publish("door_status_D8", "door locked");
    }
    else{
        Particle.publish("door_status_D8", "DOOR UNLOCKED");
    }
}

Q2. Now that most of my code is inside the mustDo() function and use of softdelays in void loop, the red SOS is gone! but… im getting red SOS when Im trying to write code for blynk.email widget to send an email when particle.subscribe door handler receives “door unlocked” but I cant get the email to send through :frowning: Am I using blynk.email widget correctly?

void myDoorHandler(const char *event, const char *data){
    String door_status = String(data);
    Serial.println("Door Handler executed");
    Serial.println(door_status);

    if(door_status == "DOOR UNLOCKED"){
        String msg = "Your door was UNLOCKED on "+Time.format("%a %e %b")+" at "+Time.format("%I:%M%p.")+"Please ignore this email if this was you.";
        lcd3.print(0, 0, "Door UNLOCKED!");
        Serial.println(msg);
        Blynk.email("SECURITY ALERT!", msg);

Device 1 Code

#include <DailyTimerSpark.h>
#include <blynk.h>

BlynkTimer timer;

char auth[] = "HIDDEN";
    
int led3=D0;             // Blynk Nightlight Control + HTML Nightlight Toggle On/Off
int led3_status=-1;      // HTML Nightlight Toggle On/Off

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)

bool ldrNeedUpdate = false;

void blynkLDRdata(){
  ldrNeedUpdate = true;
}

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

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){
    double temp_val = atof(data);
    Serial.printlnf("Temperature Handler executed, %f°C", temp_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2f°C %s", temp_val, (temp_val > 32.50) ? "Warm" : "Cool");
    lcd2.print(0, 0, msg);
}

void myPressHandler(const char *event, const char *data){
    double press_val = atof(data);
    Serial.printlnf("Pressure Handler executed, %.2fPa", press_val);

    char msg[32];
    snprintf(msg, sizeof(msg), "%.2fPa %s", press_val, (press_val > 900) ? "High" : "Low");
    lcd2.print(0, 1, msg);
}

void myDoorHandler(const char *event, const char *data){
    String door_status = String(data);
    Serial.println("Door Handler executed");
    Serial.println(door_status);

    if(door_status == "DOOR UNLOCKED"){
        String msg = "Your door was UNLOCKED on "+Time.format("%a %e %b")+" at "+Time.format("%I:%M%p.")+"Please ignore this email if this was you.";
        lcd3.print(0, 0, "Door UNLOCKED!");
        Serial.println(msg);
        Blynk.email("SECURITY ALERT!", msg);

    }
    else{
        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 temp and pressure data vals from D8 published temp and pressure readings
    Particle.subscribe("temperature_D8", myTempHandler, MY_DEVICES);
    Particle.subscribe("pressure_D8", myPressHandler, MY_DEVICES);
    Particle.subscribe("door_status_D8", myDoorHandler, MY_DEVICES);
    
    Serial.begin(9600);
    while (!Serial);
    Serial.println("Serial is Running");
    Serial.println("Blynk is running. Ensure wifi connected on device and app are same");
    
    // Push ldrdata into blynk app every 3 seconds (Prevents floodings)
    timer.setInterval(2000, functions);
}


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

void pub_ambience_status(){
    if (ldr_status <= 100){  
        lcd.print(9,0, "Dark  ");
        Particle.publish("ambience_C8", "Dark", PRIVATE);
        Serial.println("Ambience Status= Dark");
        Serial.println("Ambience Dark Request triggered");
    }
    
    else if (ldr_status >= 300){    
        lcd.print(9,0, "Bright");
        Particle.publish("ambience_C8", "Bright", PRIVATE);
        Serial.println("Ambience Status= Bright");
        Serial.println("Ambience Bright Request triggered");
    }
    
    else{
        lcd.print(9,0, "Dim   ");
        Particle.publish("ambience_C8", "Dim", PRIVATE);
        Serial.println("Ambience Status= Dim");
        Serial.println("Ambience Dim Request triggered");
    }
}


void loop(){                     
    mustDo();

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

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(){
    DailyTimer MorningTimer(5, 0, 11, 59, EVERY_DAY); 
    DailyTimer AfternoonTimer(12, 0, 16, 59, EVERY_DAY); 
    DailyTimer EveningTimer(17, 0, 19, 59, EVERY_DAY); 
    DailyTimer NightTimer(20, 0, 23, 59, EVERY_DAY); 

    if(MorningTimer.startTrigger()){
        lcd.print(0,1, "Good Morning  ");
    }
    
    else if(AfternoonTimer.startTrigger()){
        lcd.print(0,1, "Good Afternoon");
    }
    
    else if(EveningTimer.startTrigger()){
        lcd.print(0,1, "Good Evening  ");
    }
    else if(NightTimer.startTrigger()){
        lcd.print(0,1, "Good Night    ");
    }
    else{
        lcd.print(0,1, "Midnight      ");
    }
}


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

    else {
        return -1;
    }
}

Device 2 Code

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

Adafruit_BMP280 bmp; // I2C
BlynkTimer timer;

int button=D4; // Fingerprint scanner

char auth[] = "HIDDEN";  

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

// Blynk Slider + Button
int led1 = D2;

Timer timer1(5000, 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) {
        t_temp = bmp.readTemperature();
        Particle.publish("temperature_D8", String(t_temp), PRIVATE);
        Serial.println("Temperature Request triggered");
        tempNeedUpdate = false;
    }
    
    if (pressNeedUpdate) {
        t_press = bmp.readPressure();
        Particle.publish("pressure_D8", String(t_press), PRIVATE);
        Serial.println("Pressure Request triggered");
        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){
    String ambience_status = String(data);
    Serial.println("Ambience Handler executed");
    Serial.println(ambience_status);

    if(ambience_status == "Dark"){
        digitalWrite(D2, HIGH);
        Serial.println("Led turns on");
    }
    else if(ambience_status == "Bright"){
        digitalWrite(D2, LOW);
        Serial.println("Led stays off");
    }
    else if(ambience_status == "Dim"){
        digitalWrite(D2, LOW);
        Serial.println("Led stays off");
    }
}


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); 

    Serial.begin(9600);
    while (!Serial);
    Serial.println("Serial is Running");
    Serial.println("Blynk is running. Ensure wifi connected on device and app are same");
 
    if (!bmp.begin()){
        Serial.println("Can't find the sensor BMP280");
    }
    

    timer1.start();
    timer.setInterval(5000, functions);
}

void loop(){
    mustDo();


}


void fingerprint_scanner(){
    if (digitalRead(button) == LOW){
        Particle.publish("door_status_D8", "DOOR UNLOCKED");
    }
    else{
        Particle.publish("door_status_D8", "door locked");
    }
}

You omitted the info about which kind of SOS it was again!
And you also use String again despite being warned to avoid it wherever possible.

If I’m not mistaken the first parameter of Blynk.email() is supposed to be the email address, isn’t it? But you are not passing a valid email address there.


Not sure what the current state of your project is, but here I have tried to do the same thing entirely without timers and delays. Also unifying functions that mainly do the same thing into only one.
https://go.particle.io/shared_apps/600c4afae6f0b000092f775c

The code for both device types is all there in that one file. You only need to set the target type in the first line

// either
#define DEVICE_TYPE 1
// or
#define DEVICE_TYPE 2

Hi ScruffR! Thank you very much for your writing the code. I’ve tested it and I have no stack overflow, i will try to implement parts of it (unifying function and publish) into my code

  • Why do you use const int instead of int to declare pins?
  • I see that you used log.info/log.warn instead of serial.print! I’ll implement that to my code so it looks neater :slight_smile:
  • I’ve also removed dailysparktimer and used your float hh timer instead.
  • I noticed that you used wrote char msg[16] = “”, strncpy(msg, “Dark”, sizeof(msg)); and Particle.publish(“C8_ambience”, msg);
    How is this different from Particle.publish(“C8_ambience”, “Dark”, PRIVATE);? Is the one you wrote, is a char and the one with “Dark” a string that gets converted to char (Since ambience_handler (const char *event, const char *data) )?
  • Whats the difference between having a function prototype and function declaration? Does having a function prototype help to improve code speed?

I just realised i used strings in my ambience handler!! I noticed in your code you wrote the below.

void c8_Handler(const char *event, const char *data){
  Log.trace("%s: <%s>", event, data);
  if(!LITERALCMP(data, "Dark")) {

I;m trying to understand const and strings. If I replace if(ambience_status==“Dark”) with if(data==“Dark”), my code still verified, however whatever is inside the if statement does not execute. I tried if(data = “Dark”) and whatever inside the if statement works. Why is this so?

I was wondering if writing serialprint&publish once based on LDR state every 5 seconds would allow photon to be more efficient;

I’m about halfway done with my project, though I’m still facing stack overflow issue with my own code.

I managed to get the fingerprint scanner widget button on blynk working with this:

void fingerprint_scanner(){
    if (digitalRead(button) == LOW){
        Particle.publish("door_status_D8", "DOOR UNLOCKED");
    }
    else{ // HIGH
        Particle.publish("door_status_D8", "door locked");
    }
}

BLYNK_WRITE(V4){       // widget btn state opposite of physical btn state  
    if(param.asInt()){ // HIGH , param.asInt() gets value from pin V4
        Particle.publish("door_status_D8", "door locked");
    }
    else{              // LOW
        Particle.publish("door_status_D8", "DOOR UNLOCKED");
    }
}

const “variables” will only reside in flash and use no RAM. Non-const initialised variables occupy space in both flash and RAM. Since these values will never change while the program is running there is no use in wasting RAM.

Log.xxx() is not only neater but also allows you to disable the logging entirely (and not even be compiled into the code) by simply changing the LOG_LEVEL

A single Particle.publish() instruction makes it easier to (potentially) change the event name and using a common variable to store the data makes reusing that variable for different function calls easier (e.g. logging and publishing and displaying the same text).
Also Particle has removed the possibility to publish PUBLIC events and hence the PRIVATE scope has become redundant.
I also moved the C8_/D8_ qualifyier to the front in order to have one subscribe filter and thus handler catch multiple events of the same type.

Function prototypes are actually a C/C++ requirement but Particle (adopting the Wiring/Arduino ease-of-use paradigm) has opted to have a pre-processor adding prototypes when the user happens to have forgotten to do so.
However, in some circumstances that pre-processor fails to do that perfectly and hence it’s best practice to add them manually anyway (in my test at least one function was not prototyped by the pre-processor, so I added prototypes for all functions - if you remove the prototypes you may see the difference).

String is a non-trivial datatype and hence has a somewhat heavier impact on code size and may also cause heap fragmentation due to its dynamic memory needs but offers some layers of comfort using it. However, I usually opt for safer and smaller code even when it means needing to know more about how programming works :wink:

C-strings (i.e. char* and char[]) don’t offer the comfort of having an operator==() overload for equality checks hence you need to use strcmp() and relatives instead.
In addition to that I defined a macro (LITERALCMP) which neatly packages strncmp() in a way so that I don’t have to write the same literal twice :sunglasses: (programmers are lazy too :see_no_evil:).
The == check didn’t fail to compile because it’s a perfectly allowed instruction but it doesn’t do what you may think it does. It only compares the addresses of the two strings but they will (almost) never be the same even when the string contents are.

This isn’t best style tho’
Both functions do mostly the same thing, so that part should go in a dedicated function which can be called from these two.

inline void pubDoorState(bool state) {
  Particle.publish("D8_door_status", state ? "door locked" : "DOOR UNLOCKED!");
}

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

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

(I also changed the event name for above reason to unify the subscription handler(s) and also added the exclamation mark to the unlocked-message as my subscribe handler looks for it)

BTW, even with the two separate implementations it’s poor style to have the same logic flipped between the two. In one you first check for LOW/FALSE/0 and else and in the second you do the opposite HIGH/TRUE/!0 and else. Even when writing code only for yourself try to write it in a way that anybody (including yourself in the far future) will be easily able to comprehend what’s going on and functions that do the same should also look the same (which would then make it easy to see that they actually should be the same :wink: ).

1 Like

Hi ScruffR! Thanks for the detailed explaination!! I never knew about all these !! Interesting and informative!
Update 2 :A lright, I’ll put a function into physical and widget button function. Currently checking my code now since D8_handlers arent working

  • Does the same goes for static int (flash only, no ram) and int (flash and ram)?
  • My log.info doesnt output in console. Why is this so? I’m trying to debug my D8_Handler function which consists of all the 3 handlers (temp, press and door)

I’ve re-written my code here

Device 1 Code

#include <blynk.h>

BlynkTimer timer;

SerialLogHandler logHandler(LOG_LEVEL_ERROR, {{"iot_C8", 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[] = "HIDDEN";

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() is a blocking call, and blocks for a few milliseconds.
    }
  }
  Particle.process();
}

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

void D8_Handlers(const char *event, const char *data){
    char msg[128];
    int line = 0;
    double val = atof(data);

    if(event = "D8_temperature"){
        Log.info("Temperature Handler executed, %f°C", val);
        snprintf(msg, sizeof(msg), "%.2f°C %s", val, (val > 32.50) ? "Warm" : "Cool");
        line = 0;
    }
    
    else if(event = "D8_pressure"){
        Log.info("Pressure Handler executed, %.2fPa", val);
        snprintf(msg, sizeof(msg), "%.2fPa %s", val, (val > 900) ? "High" : "Low");
        line = 1;
    }
    
    else if(event = "D8_door_status"){
        Log.info("Door Handler executed: %s", data);
        if(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("hidden@gmail.com", "SECURITY ALERT!", msg);
        }
        lcd3.print(0, 0, data);
    }
    
    else{
        Log.warn("Unknown event <%s>", event);
        return;
    }
    lcd.print(0, line, msg);
}


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_door_status", myDoorHandler, MY_DEVICES);
    //Particle.subscribe("D8_temperature", myTempHandler, MY_DEVICES);
    //Particle.subscribe("D8_pressure", myPressHandler, MY_DEVICES);

    Particle.subscribe("D8_Handlers", 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(){
    if (ldr_status <= 100){  
        lcd.print(9,0, "Dark  ");
        Particle.publish("ambience_C8", "Dark", PRIVATE);
        Log.info("Ambience Status= Dark");
    }
    
    else if (ldr_status >= 300){    
        lcd.print(9,0, "Bright");
        Particle.publish("ambience_C8", "Bright", PRIVATE);
        Log.info("Ambience Status= Bright");
    }
    
    else{
        lcd.print(9,0, "Dim   ");
        Particle.publish("ambience_C8", "Dim", PRIVATE);
        Log.info("Ambience Status= Dim");
    }
}


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, {{"iot_D8", LOG_LEVEL_INFO}});

Adafruit_BMP280 bmp; // I2C
BlynkTimer timer;

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

const char auth[] = "HIDDEN";  

// 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), "%.1f", 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(data = "Dark"){
        digitalWrite(D2, HIGH);
        Log.info("Led turns on");
    }
    else if((data = "Bright") || (data = "Dim")){
        digitalWrite(D2, LOW);
        Log.info("Led stays off");
    }
    else{
        // nothing
    }
}


void fingerprint_scanner(){
    if (digitalRead(button) == LOW){
        Particle.publish("D8_door_status", "DOOR UNLOCKED");
    }
    else{ // HIGH
        Particle.publish("D8_door_status", "door locked");
    }
}

BLYNK_WRITE(V4){       // widget btn state opposite of physical btn state  
    if(param.asInt()){ // HIGH , param.asInt() gets value from pin V4
        Particle.publish("D8_door_status", "door locked");
    }
    else{              // LOW
        Particle.publish("D8_door_status", "DOOR UNLOCKED");
    }
}


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();
}

static has different meanings between C and C++.
static variables inside functions are still RAM based but will retain their value even after the function has finished.
static class variables are shared between all instances of said class but are also RAM based.

Because you changed the scope for the log handler from app to iot_C8.
But Log.info() logs to the app scope. If you want it to log it the iot_C8 scope you’d need to instantiate a Logger myLog("iot_C8") and use that instead.

There are mulitple issues.

  1. You are using the assignment operator = instead of the equality check ==
  2. even with == as I said earlier const char* cannot be checked for equality that way
  3. in a really early post of mine in this thread I suggested, when debugging always add an unconditional Log.trace("%s: <%s>", event, data); at the very top of your handler to see, whether the handler fires at all and what data it receives (will require extenging the scope to LOG_LEVEL_ALL)

But foremost this

This will only trigger for events starting with D8_Handlers but the only common part of your events is D8_. That’s why my code looks like this

  Particle.subscribe("D8_", d8_Handler);

Hi ScruffR! Oh thanks!! I checked out the strcmp() you hyperlinked. Now I understand that it was a const char string instead of normal string!!