Using an event handler with a class-based Finite State Machine

I am implementing a class-based FSM on a Boron that wakes up at set intervals to sample sensor input and react accordingly. I am using the example in application note AN010 (AN010 Finite State Machines | Datasheets | Particle)

The FSM works well. My problem arises when I try to move some event handler code from a case-based FSM to my current system.

Specifically, in AN029 (AN029 Wake publish sleep example | Datasheets | Particle) there is a nice example of a firmwareUpdate state, in which the system checks whether new firmware is available; if so, it allows time to install.

The abbreviated code from AN029 is as follows:

unsigned long stateTime;
bool firmwareUpdateInProgress = false;

// forward declaration
void firmwareUpdateHandler(system_event_t event, int param); // forward declaration

void setup() {
 
    System.on(firmware_update, firmwareUpdateHandler);

    // It's only necessary to turn cellular on and connect to the cloud. Stepping up
    // one layer at a time with Cellular.connect() and wait for Cellular.ready() can
    // be done but there's little advantage to doing so.
    Cellular.on();
    Particle.connect();
    stateTime = millis();
}

void loop() {
    switch(state) {
// different state functions here...

case STATE_FIRMWARE_UPDATE:
            if (!firmwareUpdateInProgress) {
                Log.info("firmware update completed");
                state = STATE_SLEEP;
            }
            else
            if (millis() - stateTime >= firmwareUpdateMaxTime.count()) {
                Log.info("firmware update timed out");
                state = STATE_SLEEP;
            }
            break;
    }
}

//...and now the handler
void firmwareUpdateHandler(system_event_t event, int param) {
    switch(param) {
        case firmware_update_begin:
            firmwareUpdateInProgress = true;
            break;

        case firmware_update_complete:
        case firmware_update_failed:
            firmwareUpdateInProgress = false;
            break;
    }
}

Now I try to translate this to a class-based approach:

A. Header file (edited for brevity; full file upon request)

#ifndef __MAINSTATEMACHINE_H
#define __MAINSTATEMACHINE_H

#include "Particle.h"

class MainStateMachine {
public:
    MainStateMachine();
    virtual ~MainStateMachine();

    void setup();
    void loop();

    MainStateMachine &withConnectMaxTime(std::chrono::milliseconds connectMaxTime) { this->connectMaxTime = connectMaxTime; return *this;};
    
    MainStateMachine &withFirmwareUpdateMaxTime(std::chrono::milliseconds firmwareUpdateMaxTime) { this->firmwareUpdateMaxTime = firmwareUpdateMaxTime; return *this;};

protected:

    // Maximum amount of time to wait for a user firmware download in milliseconds
    // before giving up and just going back to sleep
    std::chrono::milliseconds firmwareUpdateMaxTime = 5min;
    
    volatile bool firmwareUpdateInProgress = false;
    
    // forward declarations
    void firmwareUpdateHandler(system_event_t event, int param); 
    void stateSleep();
    
    unsigned long stateTime;
    
	std::function<void(MainStateMachine&)> stateHandler = 0;

};

#endif /* __MAINSTATEMACHINE_H */
  1. The main cpp file
#include "MainStateMachine.h"

static Logger logStatus("app.msm");

MainStateMachine::MainStateMachine() {
}

MainStateMachine::~MainStateMachine() {
}

void MainStateMachine::setup() {
    logStatus.info("MainStateMachine::setup()");

    System.on(firmware_update, MainStateMachine::firmwareUpdateHandler);

    Cellular.on();
    Particle.connect();
    stateTime = millis();
    
    stateHandler = &MainStateMachine::stateWaitConnected;
}

void MainStateMachine::loop() {
    if (stateHandler) {
        stateHandler(*this);
    }
}

void MainStateMachine::stateFirmwareUpdate() {
    if (!firmwareUpdateInProgress) {
        Log.info("firmware update completed");
        stateHandler = &MainStateMachine::stateSleep;
        }
        else
        if (millis() - stateTime >= firmwareUpdateMaxTime.count()) {
            Log.info("firmware update timed out");
            stateHandler = &MainStateMachine::stateSleep;
            }
}

void MainStateMachine::stateSleep() {

    // This is the equivalent to:
    // System.sleep(WKP, RISING, SLEEP_NETWORK_STANDBY);

    SystemSleepConfiguration config;
    // different approaches to sleep, depending on conditions
    switch (conditions) {
        case NIGHTTIME:
            config.mode(SystemSleepMode::ULTRA_LOW_POWER)
            .gpio(photocellPin, RISING)
            .duration(nightSleepTime*1000);
            logStatus.info("going to nighttime sleep for %ld seconds at %d:%d", (long) nightSleepTime, Time.hour(), Time.minute());
            break;
        case DAYTIME:
            config.mode(SystemSleepMode::STOP)
            .gpio(digitalRainPin, FALLING)
            .duration(daySleepTime*1000);
            logStatus.info("going to daytime sleep for %ld seconds at %d:%d", (long) daySleepTime, Time.hour(), Time.minute());
            break;
        case RAINING:
            config.mode(SystemSleepMode::STOP)
            .duration(rainSleepTime*1000);
            logStatus.info("going to raining sleep for %ld seconds at %d:%d", (long) rainSleepTime, Time.hour(), Time.minute());
            break;
        default:
            logStatus.info("Error: failed to set a sleep time");
    }
    

    SystemSleepResult result = System.sleep(config);

    logStatus.info("woke from sleep");

    stateHandler = &MainStateMachine::stateWaitConnected;
    stateTime = millis();
}

void MainStateMachine::firmwareUpdateHandler(system_event_t event, int param) {
    
    switch(param) {
        case firmware_update_begin:
            firmwareUpdateInProgress = true;
            break;

        case firmware_update_complete:
        case firmware_update_failed:
            firmwareUpdateInProgress = false;
            break;
     }
}

When I try to compile, I receive the error message:

MainStateMachine.cpp:29:50: error: invalid use of non-static member function ‘void MainStateMachine::firmwareUpdateHandler(system_event_t, int)’
29 | System.on(firmware_update, MainStateMachine::firmwareUpdateHandler);

Clearly I do not understand how to implement an event handler in C++

I would appreciate any help

David

Solved:

I moved the event handler out of the class, and made it static void.

Then the code compiled properly

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.