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(),
|
App::App() : config(),
|
||||||
ui(onoffButtonInterruptCallback, setupButtonInterruptCallback),
|
ui(onoffButtonInterruptCallback, setupButtonInterruptCallback),
|
||||||
|
state(&ui),
|
||||||
wifi(),
|
wifi(),
|
||||||
mqtt(&wifi, mqttOnConnectCallback, mqttOnMessageCallback),
|
mqtt(&wifi, mqttOnConnectCallback, mqttOnMessageCallback),
|
||||||
sensorControllerPlugin(&mqtt, &ui),
|
sensorControllerPlugin(&mqtt, &ui),
|
||||||
|
@ -34,17 +35,89 @@ namespace Dough
|
||||||
distanceSensor.setup();
|
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.
|
return connected;
|
||||||
temperatureSensor.loop();
|
|
||||||
humiditySensor.loop();
|
|
||||||
distanceSensor.loop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::clearHistory()
|
void App::clearHistory()
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define DOUGH_APP_H
|
#define DOUGH_APP_H
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "App/AppStateController.h"
|
||||||
#include "UI/UI.h"
|
#include "UI/UI.h"
|
||||||
#include "App/Configuration.h"
|
#include "App/Configuration.h"
|
||||||
#include "App/callbacks.h"
|
#include "App/callbacks.h"
|
||||||
|
@ -20,6 +21,7 @@ namespace Dough
|
||||||
static App *Instance();
|
static App *Instance();
|
||||||
Configuration config;
|
Configuration config;
|
||||||
UI ui;
|
UI ui;
|
||||||
|
AppStateController state;
|
||||||
WiFi wifi;
|
WiFi wifi;
|
||||||
MQTT mqtt;
|
MQTT mqtt;
|
||||||
SensorControllerPlugin sensorControllerPlugin;
|
SensorControllerPlugin sensorControllerPlugin;
|
||||||
|
@ -28,12 +30,13 @@ namespace Dough
|
||||||
SensorController humiditySensor;
|
SensorController humiditySensor;
|
||||||
|
|
||||||
void setup();
|
void setup();
|
||||||
void measure();
|
void loop();
|
||||||
void clearHistory();
|
void clearHistory();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
App();
|
App();
|
||||||
Logger _logger;
|
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
|
// This class is used to store measurements for a sensor and to keep
|
||||||
// track of running totals for handling average computations.
|
// track of running totals for handling average computations.
|
||||||
// It also provides functionality to decide when to read a measurement
|
// 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).
|
// changes occur or when the last publish was too long ago).
|
||||||
class SensorController;
|
class SensorController;
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,13 @@ namespace Dough
|
||||||
_led3.loop();
|
_led3.loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UI::onStateChange(AppState oldState, AppState newState)
|
||||||
|
{
|
||||||
|
Serial.print(oldState); // TODO
|
||||||
|
Serial.print(" to ");
|
||||||
|
Serial.println(newState); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
void UI::notifyConnectingToWifi()
|
void UI::notifyConnectingToWifi()
|
||||||
{
|
{
|
||||||
_led1.blink()->slow();
|
_led1.blink()->slow();
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include "App/AppStateController.h"
|
||||||
#include "UI/Button.h"
|
#include "UI/Button.h"
|
||||||
#include "UI/LED.h"
|
#include "UI/LED.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
@ -11,7 +12,7 @@ namespace Dough
|
||||||
{
|
{
|
||||||
// This class groups all user interface functionality: serial logging,
|
// This class groups all user interface functionality: serial logging,
|
||||||
// LEDs and buttons.
|
// LEDs and buttons.
|
||||||
class UI
|
class UI : public AppStateControllerPluginBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
UI(ButtonISR onoffButtonISR, ButtonISR setupButtonISR);
|
UI(ButtonISR onoffButtonISR, ButtonISR setupButtonISR);
|
||||||
|
@ -23,6 +24,7 @@ namespace Dough
|
||||||
void updateLEDs();
|
void updateLEDs();
|
||||||
void resume();
|
void resume();
|
||||||
void suspend();
|
void suspend();
|
||||||
|
virtual void onStateChange(AppState oldState, AppState newState);
|
||||||
void notifyConnectingToWifi();
|
void notifyConnectingToWifi();
|
||||||
void notifyConnectingToMQTT();
|
void notifyConnectingToMQTT();
|
||||||
void notifyConnected();
|
void notifyConnected();
|
||||||
|
|
115
src/main.cpp
115
src/main.cpp
|
@ -1,9 +1,5 @@
|
||||||
#include "main.h"
|
#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;
|
DoughBoyState state = CONFIGURING;
|
||||||
Dough::Logger logger("MAIN");
|
Dough::Logger logger("MAIN");
|
||||||
|
|
||||||
|
@ -21,93 +17,32 @@ void setup()
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
auto app = Dough::App::Instance();
|
auto app = Dough::App::Instance();
|
||||||
|
app->loop();
|
||||||
app->ui.processButtonEvents();
|
|
||||||
|
|
||||||
if (!setupNetworkConnection())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
app->mqtt.procesIncomingsMessages();
|
// if (state == CONFIGURING)
|
||||||
|
// {
|
||||||
if (state == CONFIGURING)
|
// if (app->config.isOk())
|
||||||
{
|
// {
|
||||||
if (app->config.isOk())
|
// setStateToMeasuring();
|
||||||
{
|
// }
|
||||||
setStateToMeasuring();
|
// }
|
||||||
}
|
// else if (state == MEASURING && !app->config.isOk())
|
||||||
}
|
// {
|
||||||
else if (state == MEASURING && !app->config.isOk())
|
// setStateToConfiguring();
|
||||||
{
|
// }
|
||||||
setStateToConfiguring();
|
// else if (state == MEASURING)
|
||||||
}
|
// {
|
||||||
else if (state == MEASURING)
|
// app->measure();
|
||||||
{
|
// }
|
||||||
app->measure();
|
// else if (state == CALIBRATING)
|
||||||
}
|
// {
|
||||||
else if (state == CALIBRATING)
|
// delay(3000);
|
||||||
{
|
// setStateToPaused();
|
||||||
delay(3000);
|
// }
|
||||||
setStateToPaused();
|
// else if (state == PAUSED)
|
||||||
}
|
// {
|
||||||
else if (state == PAUSED)
|
// app->clearHistory();
|
||||||
{
|
// }
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleOnoffButtonPress()
|
void handleOnoffButtonPress()
|
||||||
|
|
10
src/main.h
10
src/main.h
|
@ -12,13 +12,6 @@
|
||||||
#include "UI/UI.h"
|
#include "UI/UI.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
CONNECTING_WIFI,
|
|
||||||
CONNECTING_MQTT,
|
|
||||||
CONNECTED
|
|
||||||
} DoughBoyConnectionState;
|
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
CONFIGURING,
|
CONFIGURING,
|
||||||
|
@ -27,10 +20,9 @@ typedef enum
|
||||||
CALIBRATING
|
CALIBRATING
|
||||||
} DoughBoyState;
|
} DoughBoyState;
|
||||||
|
|
||||||
bool setupNetworkConnection();
|
|
||||||
void handleMqttMessage(String &topic, String &payload);
|
|
||||||
void handleOnoffButtonPress();
|
void handleOnoffButtonPress();
|
||||||
void handleSetupButtonPress();
|
void handleSetupButtonPress();
|
||||||
|
|
||||||
void setStateToConfiguring();
|
void setStateToConfiguring();
|
||||||
void setStateToMeasuring();
|
void setStateToMeasuring();
|
||||||
void setStateToPaused();
|
void setStateToPaused();
|
||||||
|
|
Loading…
Reference in New Issue