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 */
- 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