Problem with timer and/or Particle.publish

Hi everyone,

I want to turn on/off the onboard-LED (D7) with a function from outside. If the LED is turned on, a timer is started, which turns off the LED after 10 seconds (timer-function).
If turned on an event is fired with “LED/7 on”.
If turned off an event is fired with “LED/7 off”.

Here is the problem: Turning on works fine with the following event “LED/7 on” and the serial output
LED: on
Event: 1

After the timer is fired, the LED is automatically turned off, but there is no event “LED/7 off” in the stream and the serial output is
LED: off
Event: 0

The line “Event: 0” means, that the event is not successfully fired (to the cloud).
Strange thing: The LED is turned off, the serial output appears, the device (photon) disconnect and reconnects and the event is lost.

Does anyone has an idea how find/fix the error?

Thanks,
Sandy

#define SECURITY_TIMEOUT 10000

int ledState = LOW;
Timer t = Timer(SECURITY_TIMEOUT, &turnOffLED, true);

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

    Particle.function("TurnLED", turnLED);
    Particle.variable("D7", ledState);
}

void loop() {
// nop
}

void changeLEDState(int pin, int state) {
    digitalWrite(pin, state);
    refreshLEDState();
    
    String mode = digitalRead(pin) ? "on" : "off";

    Serial.println("LED: " + mode);
    Serial.println("Event: " + String(Particle.publish("LED/7 ", mode, PUBLIC)));

    if(state) {
        t.start();
    } else {
        t.stop();
    }
}

int turnLED(String dummy) {
    int state = digitalRead(D7);
    changeLEDState(D7, !state);
    return 1;
}

void turnOffLED() {changeLEDState(D7, LOW);}

void refreshLEDState() {
    ledState = digitalRead(D7);
}

That's not the syntax shown in the docs, is it?
https://docs.particle.io/reference/device-os/firmware/photon/#software-timers

And as the docs also suggest, some actions (including Particle.publish()) should not be called from a timer callback but you should treat a timer callback function "like" an ISR.

Hi @ScruffR,

sorry, my fault. I did some experiments with class-methods as timer (not successfull).
Change the code to:

#define SECURITY_TIMEOUT 10000

int ledState = LOW;
Timer t = Timer(SECURITY_TIMEOUT, turnOffLED, true);

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

    Particle.function("TurnLED", turnLED);
    Particle.variable("D7", ledState);
}

void loop() {
// nop
}

void changeLEDState(int pin, int state) {
    digitalWrite(pin, state);
    refreshLEDState();
    
    String mode = digitalRead(pin) ? "on" : "off";

    Serial.println("LED: " + mode);
    Serial.println("Event: " + String(Particle.publish("LED/7 ", mode, PUBLIC)));

    if(state) {
        t.start();
    } else {
        t.stop();
    }
}

int turnLED(String dummy) {
    int state = digitalRead(D7);
    changeLEDState(D7, !state);
    return 1;
}

void turnOffLED() {changeLEDState(D7, LOW);}

void refreshLEDState() {
    ledState = digitalRead(D7);
}

Unfortunately it still does not work. Instead of event “LED/7 off” I get the event “spark/device/diagnostics/update”

That's because this still applies

Also be aware that the timer thread has very limited stack, so multiple nested calls (especially to heavy weight functions like Particle.publish()) may cause a stack overflow with a subsequent SOS panic crash and reset.

Hi @ScruffR,

do you have a “best-practice” alternative for my problem?

You can either off-load the publishes to loop() or you use this library
https://build.particle.io/libs/PublishQueueAsyncRK/0.0.3/tab/PublishQueueAsyncRK.cpp

Thanks for this suggestion…