Backup work on making the application state a separate class, so I can keep all state knowledge in one place.
This commit is contained in:
parent
f20ff5a4bb
commit
4c5ea8c001
|
@ -10,6 +10,7 @@ namespace Dough
|
|||
|
||||
App::App() : config(),
|
||||
ui(onoffButtonInterruptCallback, setupButtonInterruptCallback),
|
||||
state(&ui),
|
||||
wifi(),
|
||||
mqtt(&wifi, mqttOnConnectCallback, mqttOnMessageCallback),
|
||||
sensorControllerPlugin(&mqtt, &ui),
|
||||
|
@ -34,17 +35,89 @@ namespace Dough
|
|||
distanceSensor.setup();
|
||||
}
|
||||
|
||||
void App::measure()
|
||||
void App::loop()
|
||||
{
|
||||
if (!config.isOk())
|
||||
if (_setupNetworkConnection())
|
||||
{
|
||||
return;
|
||||
ui.processButtonEvents();
|
||||
mqtt.procesIncomingsMessages();
|
||||
|
||||
switch (state.get())
|
||||
{
|
||||
case CONFIGURING:
|
||||
if (config.isOk())
|
||||
{
|
||||
state.startMeasurements();
|
||||
}
|
||||
break;
|
||||
case MEASURING:
|
||||
if (config.isOk())
|
||||
{
|
||||
temperatureSensor.loop();
|
||||
humiditySensor.loop();
|
||||
distanceSensor.loop();
|
||||
}
|
||||
else
|
||||
{
|
||||
state.startConfiguration();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the device is connected to the WiFi network and the MQTT broker.
|
||||
// If not, then try to setup the connection.
|
||||
// Returns true if the connection was established, false otherwise.
|
||||
bool App::_setupNetworkConnection()
|
||||
{
|
||||
static bool connected = false;
|
||||
|
||||
if (!wifi.isConnected())
|
||||
{
|
||||
state.setWiFiConnected(false);
|
||||
if (connected)
|
||||
{
|
||||
_logger.log("s", "ERROR - Connection to WiFi network lost! Reconnecting ...");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.log("s", "Connecting to the WiFi network ...");
|
||||
}
|
||||
connected = false;
|
||||
ui.notifyConnectingToWifi();
|
||||
state.setWiFiConnected(wifi.connect());
|
||||
}
|
||||
if (wifi.isConnected() && !mqtt.isConnected())
|
||||
{
|
||||
state.setWiFiConnected(true);
|
||||
state.setMQTTConnected(false);
|
||||
if (connected)
|
||||
{
|
||||
_logger.log("s", "ERROR - Connection to the MQTT broker lost! Reconnecting ...");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.log("s", "Connecting to the MQTT broker ...");
|
||||
}
|
||||
connected = false;
|
||||
ui.notifyConnectingToMQTT();
|
||||
state.setMQTTConnected(mqtt.connect());
|
||||
}
|
||||
if (wifi.isConnected() && mqtt.isConnected())
|
||||
{
|
||||
state.setWiFiConnected(true);
|
||||
state.setMQTTConnected(true);
|
||||
if (!connected)
|
||||
{
|
||||
_logger.log("s", "Connection to MQTT broker established");
|
||||
ui.notifyConnected();
|
||||
ui.clearButtonEvents();
|
||||
connected = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Get measurements from the sensors.
|
||||
temperatureSensor.loop();
|
||||
humiditySensor.loop();
|
||||
distanceSensor.loop();
|
||||
return connected;
|
||||
}
|
||||
|
||||
void App::clearHistory()
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define DOUGH_APP_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "App/AppStateController.h"
|
||||
#include "UI/UI.h"
|
||||
#include "App/Configuration.h"
|
||||
#include "App/callbacks.h"
|
||||
|
@ -20,6 +21,7 @@ namespace Dough
|
|||
static App *Instance();
|
||||
Configuration config;
|
||||
UI ui;
|
||||
AppStateController state;
|
||||
WiFi wifi;
|
||||
MQTT mqtt;
|
||||
SensorControllerPlugin sensorControllerPlugin;
|
||||
|
@ -28,12 +30,13 @@ namespace Dough
|
|||
SensorController humiditySensor;
|
||||
|
||||
void setup();
|
||||
void measure();
|
||||
void loop();
|
||||
void clearHistory();
|
||||
|
||||
private:
|
||||
App();
|
||||
Logger _logger;
|
||||
bool _setupNetworkConnection();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
#include "App/AppStateController.h"
|
||||
|
||||
namespace Dough
|
||||
{
|
||||
AppStateController::AppStateController(AppStateControllerPluginBase *plugin) : _logger("STATE"),
|
||||
_plugin(plugin) {}
|
||||
|
||||
AppState AppStateController::get()
|
||||
{
|
||||
if (_wifiConnected != CONNECTED)
|
||||
{
|
||||
return _wifiConnected == CONNECTION_LOST ? WIFI_CONNECTION_LOST : CONNECTING_WIFI;
|
||||
}
|
||||
if (_mqttConnected != CONNECTED)
|
||||
{
|
||||
return _mqttConnected == CONNECTION_LOST ? MQTT_CONNECTION_LOST : CONNECTING_MQTT;
|
||||
}
|
||||
if (_operationMode == CONFIGURE)
|
||||
{
|
||||
return CONFIGURING;
|
||||
}
|
||||
if (_operationMode == MEASURE)
|
||||
{
|
||||
return _paused ? PAUSED : MEASURING;
|
||||
;
|
||||
}
|
||||
if (_operationMode == CALIBRATE)
|
||||
{
|
||||
return CALIBRATING;
|
||||
}
|
||||
_logger.log("s", "get(): Unable to determine state!");
|
||||
return INVALID;
|
||||
}
|
||||
|
||||
void AppStateController::setWiFiConnected(bool connected)
|
||||
{
|
||||
if (connected)
|
||||
{
|
||||
_wifiConnected = CONNECTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
_wifiConnected = _wifiConnected == CONNECTED ? CONNECTION_LOST : CONNECTING;
|
||||
setMQTTConnected(false);
|
||||
}
|
||||
_updateState();
|
||||
}
|
||||
|
||||
void AppStateController::setMQTTConnected(bool connected)
|
||||
{
|
||||
if (connected)
|
||||
{
|
||||
_mqttConnected = CONNECTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
_mqttConnected = _mqttConnected == CONNECTED ? CONNECTION_LOST : CONNECTING;
|
||||
}
|
||||
_updateState();
|
||||
}
|
||||
|
||||
void AppStateController::startConfiguration()
|
||||
{
|
||||
_operationMode = CONFIGURE;
|
||||
_updateState();
|
||||
}
|
||||
|
||||
void AppStateController::startMeasurements()
|
||||
{
|
||||
_operationMode = MEASURE;
|
||||
_updateState();
|
||||
}
|
||||
|
||||
void AppStateController::startCalibration()
|
||||
{
|
||||
_operationMode = CALIBRATE;
|
||||
_updateState();
|
||||
}
|
||||
|
||||
void AppStateController::pauseMeasurements()
|
||||
{
|
||||
_paused = true;
|
||||
_updateState();
|
||||
}
|
||||
|
||||
void AppStateController::resumeMeasurements()
|
||||
{
|
||||
_paused = false;
|
||||
_updateState();
|
||||
}
|
||||
|
||||
void AppStateController::_updateState()
|
||||
{
|
||||
auto oldState = _appState;
|
||||
auto newState = get();
|
||||
if (newState != oldState)
|
||||
{
|
||||
_plugin->onStateChange(oldState, newState);
|
||||
_appState = newState;
|
||||
}
|
||||
}
|
||||
} // namespace Dough
|
|
@ -0,0 +1,70 @@
|
|||
#ifndef DOUGH_STATEMACHINE_H
|
||||
#define DOUGH_STATEMACHINE_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "UI/Logger.h"
|
||||
|
||||
namespace Dough
|
||||
{
|
||||
typedef enum
|
||||
{
|
||||
INITIALIZING,
|
||||
WIFI_CONNECTION_LOST,
|
||||
CONNECTING_WIFI,
|
||||
MQTT_CONNECTION_LOST,
|
||||
CONNECTING_MQTT,
|
||||
CONFIGURING,
|
||||
MEASURING,
|
||||
CALIBRATING,
|
||||
PAUSED,
|
||||
INVALID
|
||||
} AppState;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CONNECTING,
|
||||
CONNECTED,
|
||||
CONNECTION_LOST
|
||||
} ConnectionState;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CONFIGURE,
|
||||
MEASURE,
|
||||
CALIBRATE
|
||||
} AppOperationMode;
|
||||
|
||||
// This class defines an interface that can be implemented to create a plugin
|
||||
// for the Dough::AppState class. This plugin can act upon state changes.
|
||||
class AppStateControllerPluginBase
|
||||
{
|
||||
public:
|
||||
virtual void onStateChange(AppState oldState, AppState newState);
|
||||
};
|
||||
|
||||
class AppStateController
|
||||
{
|
||||
public:
|
||||
AppStateController(AppStateControllerPluginBase *plugin);
|
||||
void setWiFiConnected(bool connected);
|
||||
void setMQTTConnected(bool connected);
|
||||
void startConfiguration();
|
||||
void startMeasurements();
|
||||
void startCalibration();
|
||||
void pauseMeasurements();
|
||||
void resumeMeasurements();
|
||||
AppState get();
|
||||
|
||||
private:
|
||||
Logger _logger;
|
||||
AppStateControllerPluginBase *_plugin;
|
||||
AppState _appState = INITIALIZING;
|
||||
ConnectionState _wifiConnected = CONNECTING;
|
||||
ConnectionState _mqttConnected = CONNECTING;
|
||||
AppOperationMode _operationMode = CONFIGURE;
|
||||
bool _paused = false;
|
||||
void _updateState();
|
||||
};
|
||||
} // namespace Dough
|
||||
|
||||
#endif
|
|
@ -11,7 +11,7 @@ namespace Dough
|
|||
// This class is used to store measurements for a sensor and to keep
|
||||
// track of running totals for handling average computations.
|
||||
// It also provides functionality to decide when to read a measurement
|
||||
// from a sensor and when to publish measurements (after significant
|
||||
// from a sensor and when to publish measurements (after significant
|
||||
// changes occur or when the last publish was too long ago).
|
||||
class SensorController;
|
||||
|
||||
|
|
|
@ -112,6 +112,13 @@ namespace Dough
|
|||
_led3.loop();
|
||||
}
|
||||
|
||||
void UI::onStateChange(AppState oldState, AppState newState)
|
||||
{
|
||||
Serial.print(oldState); // TODO
|
||||
Serial.print(" to ");
|
||||
Serial.println(newState); // TODO
|
||||
}
|
||||
|
||||
void UI::notifyConnectingToWifi()
|
||||
{
|
||||
_led1.blink()->slow();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <Arduino.h>
|
||||
#include <stdarg.h>
|
||||
#include "App/AppStateController.h"
|
||||
#include "UI/Button.h"
|
||||
#include "UI/LED.h"
|
||||
#include "config.h"
|
||||
|
@ -11,7 +12,7 @@ namespace Dough
|
|||
{
|
||||
// This class groups all user interface functionality: serial logging,
|
||||
// LEDs and buttons.
|
||||
class UI
|
||||
class UI : public AppStateControllerPluginBase
|
||||
{
|
||||
public:
|
||||
UI(ButtonISR onoffButtonISR, ButtonISR setupButtonISR);
|
||||
|
@ -23,6 +24,7 @@ namespace Dough
|
|||
void updateLEDs();
|
||||
void resume();
|
||||
void suspend();
|
||||
virtual void onStateChange(AppState oldState, AppState newState);
|
||||
void notifyConnectingToWifi();
|
||||
void notifyConnectingToMQTT();
|
||||
void notifyConnected();
|
||||
|
|
115
src/main.cpp
115
src/main.cpp
|
@ -1,9 +1,5 @@
|
|||
#include "main.h"
|
||||
|
||||
// TOOD: implement the calibration logic
|
||||
// TODO: don't make sensors instances anymore, now they are handled by App as the only singleton
|
||||
// TODO: see what more stuff can be moved to the UI code. Maybe state to UI state translation ought to be there as well
|
||||
|
||||
DoughBoyState state = CONFIGURING;
|
||||
Dough::Logger logger("MAIN");
|
||||
|
||||
|
@ -21,93 +17,32 @@ void setup()
|
|||
void loop()
|
||||
{
|
||||
auto app = Dough::App::Instance();
|
||||
|
||||
app->ui.processButtonEvents();
|
||||
|
||||
if (!setupNetworkConnection())
|
||||
{
|
||||
return;
|
||||
}
|
||||
app->loop();
|
||||
|
||||
app->mqtt.procesIncomingsMessages();
|
||||
|
||||
if (state == CONFIGURING)
|
||||
{
|
||||
if (app->config.isOk())
|
||||
{
|
||||
setStateToMeasuring();
|
||||
}
|
||||
}
|
||||
else if (state == MEASURING && !app->config.isOk())
|
||||
{
|
||||
setStateToConfiguring();
|
||||
}
|
||||
else if (state == MEASURING)
|
||||
{
|
||||
app->measure();
|
||||
}
|
||||
else if (state == CALIBRATING)
|
||||
{
|
||||
delay(3000);
|
||||
setStateToPaused();
|
||||
}
|
||||
else if (state == PAUSED)
|
||||
{
|
||||
app->clearHistory();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the device is connected to the WiFi network and the MQTT broker.
|
||||
// If not, then try to setup the connection.
|
||||
// Returns true if the connection was established, false otherwise.
|
||||
bool setupNetworkConnection()
|
||||
{
|
||||
static auto connectionState = CONNECTING_WIFI;
|
||||
|
||||
auto app = Dough::App::Instance();
|
||||
|
||||
if (!app->wifi.isConnected())
|
||||
{
|
||||
if (connectionState == CONNECTED)
|
||||
{
|
||||
logger.log("s", "ERROR - Connection to WiFi network lost! Reconnecting ...");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.log("s", "Connecting to the WiFi network ...");
|
||||
}
|
||||
connectionState = CONNECTING_WIFI;
|
||||
app->ui.notifyConnectingToWifi();
|
||||
app->wifi.connect();
|
||||
}
|
||||
if (app->wifi.isConnected() && !app->mqtt.isConnected())
|
||||
{
|
||||
if (connectionState == CONNECTED)
|
||||
{
|
||||
logger.log("s", "ERROR - Connection to the MQTT broker lost! Reconnecting ...");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.log("s", "Connecting to the MQTT broker ...");
|
||||
}
|
||||
connectionState = CONNECTING_MQTT;
|
||||
app->ui.notifyConnectingToMQTT();
|
||||
app->mqtt.connect();
|
||||
delay(1000); // purely costmetic, to make the faster LED blinking noticable
|
||||
}
|
||||
if (app->wifi.isConnected() && app->mqtt.isConnected())
|
||||
{
|
||||
if (connectionState != CONNECTED)
|
||||
{
|
||||
logger.log("s", "Connection to MQTT broker established");
|
||||
app->ui.notifyConnected();
|
||||
app->ui.clearButtonEvents();
|
||||
connectionState = CONNECTED;
|
||||
setStateToConfiguring();
|
||||
}
|
||||
}
|
||||
|
||||
return connectionState == CONNECTED;
|
||||
// if (state == CONFIGURING)
|
||||
// {
|
||||
// if (app->config.isOk())
|
||||
// {
|
||||
// setStateToMeasuring();
|
||||
// }
|
||||
// }
|
||||
// else if (state == MEASURING && !app->config.isOk())
|
||||
// {
|
||||
// setStateToConfiguring();
|
||||
// }
|
||||
// else if (state == MEASURING)
|
||||
// {
|
||||
// app->measure();
|
||||
// }
|
||||
// else if (state == CALIBRATING)
|
||||
// {
|
||||
// delay(3000);
|
||||
// setStateToPaused();
|
||||
// }
|
||||
// else if (state == PAUSED)
|
||||
// {
|
||||
// app->clearHistory();
|
||||
// }
|
||||
}
|
||||
|
||||
void handleOnoffButtonPress()
|
||||
|
|
10
src/main.h
10
src/main.h
|
@ -12,13 +12,6 @@
|
|||
#include "UI/UI.h"
|
||||
#include "config.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CONNECTING_WIFI,
|
||||
CONNECTING_MQTT,
|
||||
CONNECTED
|
||||
} DoughBoyConnectionState;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CONFIGURING,
|
||||
|
@ -27,10 +20,9 @@ typedef enum
|
|||
CALIBRATING
|
||||
} DoughBoyState;
|
||||
|
||||
bool setupNetworkConnection();
|
||||
void handleMqttMessage(String &topic, String &payload);
|
||||
void handleOnoffButtonPress();
|
||||
void handleSetupButtonPress();
|
||||
|
||||
void setStateToConfiguring();
|
||||
void setStateToMeasuring();
|
||||
void setStateToPaused();
|
||||
|
|
Loading…
Reference in New Issue