-
-
Notifications
You must be signed in to change notification settings - Fork 3
App Rotator
The App Rotator provides automatic failover and multi-application support, allowing your device to seamlessly switch between different operational modes.
The App Rotator (ksAppRotator) acts as a carousel manager for your applications. It attempts to run applications in sequence, automatically switching to the next one if the current application fails or requests termination.
- Automatic Failover β Switch to config app on errors
- Multi-Mode Operation β Main app + config assistant
- Resilience β Device never gets stuck in broken state
- Easy Setup β First boot provisioning flow
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Application Rotator Loop β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β Try Application #1 β
ββββββ¬βββββββββββ¬ββββ
β β
Success β β Failed
β βΌ
β βββββββββββββββ
β β Try App #2 β
β βββββββ¬βββββββ
β β
β Successβ
β β
βΌ βΌ
βββββββββββββββββββββββββββ
β Run App Loop Forever β
β Until app returns falseβ
βββββββββββββββββββββββββββ
The framework provides a convenient macro to implement the rotator:
#include <ksf/ksAppRotator.h>
#include "MyMainApp.h"
#include "MyConfigApp.h"
// In main.cpp
KSF_IMPLEMENT_APP_ROTATOR(
MyMainApp, // First application
MyConfigApp // Fallback application
)- Implements Arduino's
setup()function - Implements Arduino's
loop()function - Creates application instances
- Manages rotation between applications
- Handles cleanup and error recovery
The most common use case:
class MainApp : public ksApplication
{
protected:
bool init() override {
// Check if configured
if (!hasWiFiCredentials()) {
return false; // Go to config app
}
// Normal operation
addComponent<ksWifiConnector>("MyDevice");
addComponent<ksMqttConnector>();
addComponent<DeviceLogic>();
return true;
}
};
class ConfigApp : public ksApplication
{
protected:
bool init() override {
// Setup mode components
addComponent<ksWifiConfigurator>();
addComponent<ksDevicePortal>();
addComponent<ksLed>(LED_BUILTIN);
return true;
}
bool loop() override {
// Auto-restart when configured
if (isWiFiConfigured()) {
return false; // Back to main app
}
return true;
}
};You can have multiple fallback applications:
KSF_IMPLEMENT_APP_ROTATOR(
MainApp, // Try main first
DiagnosticApp, // Falls back to diagnostics
ConfigApp // Last resort: full setup
)Main app can detect errors and trigger config mode:
class MainApp : public ksApplication
{
private:
int connectionFailures = 0;
protected:
bool loop() override {
auto wifi = findComponent<ksWifiConnector>();
if (!wifi->isConnected()) {
connectionFailures++;
// Too many failures, go to config
if (connectionFailures > 10) {
return false; // Trigger rotator
}
} else {
connectionFailures = 0; // Reset on success
}
return true;
}
};Applications are rotated when any of these occur:
bool init() override {
if (missingConfig) {
return false; // Rotate immediately
}
return true;
}bool postInit() override {
auto mqtt = findComponent<ksMqttConnector>();
if (!mqtt) {
return false; // Required component missing
}
return true;
}bool loop() override {
if (fatalError) {
return false; // Stop and rotate
}
return true;
}class MyApp : public ksApplication
{
MyComponent* comp;
MyApp() {
// If this throws, rotator catches it
comp = new MyComponent();
}
};bool init() override {
// Check configuration exists
if (!configExists()) {
return false; // Let config app handle it
}
// Validate required hardware
if (!sensorPresent()) {
return false; // Go to diagnostics
}
// All good, add components
addComponent<ksWifiConnector>();
return true;
}class ConfigApp : public ksApplication
{
protected:
bool init() override {
// WiFi configurator creates AP
addComponent<ksWifiConfigurator>();
// Device portal for web setup
addComponent<ksDevicePortal>();
// LED indicates setup mode
addComponent<ksLed>(LED_BUILTIN);
return true;
}
bool loop() override {
auto led = findComponent<ksLed>();
// Blink fast in config mode
led->pattern(0b10101010);
// Check if setup complete
if (hasWiFiCredentials() && hasMqttSettings()) {
saveConfiguration();
return false; // Exit config mode
}
return true;
}
};class MainApp : public ksApplication
{
protected:
bool postInit() override {
// Get references
auto led = findComponent<ksLed>();
led->pattern(0b11001100); // Slow blink = active
return true;
}
};
class ConfigApp : public ksApplication
{
protected:
bool postInit() override {
auto led = findComponent<ksLed>();
led->pattern(0b10101010); // Fast blink = config
return true;
}
};class MainApp : public ksApplication
{
private:
bool degradedMode = false;
protected:
bool loop() override {
auto wifi = findComponent<ksWifiConnector>();
if (!wifi->isConnected()) {
if (!degradedMode) {
enterDegradedMode();
degradedMode = true;
}
// Check for too long offline
if (offlineTime() > 3600000) { // 1 hour
return false; // Go to config app
}
} else {
degradedMode = false;
}
return true;
}
};// MainApp.h
#pragma once
#include <ksf/ksApplication.h>
#include <ksf/comp/ksWifiConnector.h>
#include <ksf/comp/ksMqttConnector.h>
#include <ksf/comp/ksLed.h>
class MainApp : public ksApplication
{
protected:
bool init() override {
// No configuration? Go to setup
if (!hasWiFiConfig() || !hasMqttConfig()) {
return false;
}
// Add main application components
addComponent<ksWifiConnector>("IoTDevice");
addComponent<ksMqttConnector>();
addComponent<ksLed>(LED_BUILTIN);
return true;
}
bool postInit() override {
auto led = findComponent<ksLed>();
if (led) {
led->pattern(0b11001100); // Running indicator
}
return true;
}
};
// ConfigApp.h
#pragma once
#include <ksf/ksApplication.h>
#include <ksf/comp/ksWifiConfigurator.h>
#include <ksf/comp/ksDevicePortal.h>
#include <ksf/comp/ksLed.h>
class ConfigApp : public ksApplication
{
protected:
bool init() override {
// Setup mode components
addComponent<ksWifiConfigurator>();
addComponent<ksDevicePortal>();
addComponent<ksLed>(LED_BUILTIN);
return true;
}
bool postInit() override {
auto led = findComponent<ksLed>();
if (led) {
led->pattern(0b10101010); // Config mode indicator
}
return true;
}
bool loop() override {
// Auto-exit when configured
if (hasWiFiConfig() && hasMqttConfig()) {
return false; // Go back to main app
}
return true;
}
};
// main.cpp
#include <ksf/ksAppRotator.h>
#include "MainApp.h"
#include "ConfigApp.h"
using namespace ksf;
// Implement application rotation
KSF_IMPLEMENT_APP_ROTATOR(
MainApp, // Main application
ConfigApp // Configuration application
)For complex scenarios, implement custom logic:
class SmartRotator : public ksAppRotator
{
protected:
bool shouldRestartApp() override {
// Custom logic to decide if app should restart
auto lastErrorTime = getLastApplicationErrorTime();
auto timeSinceError = millis() - lastErrorTime;
// Wait 5 minutes before retrying failed app
if (timeSinceError < 300000) {
return false; // Skip to next app
}
return true;
}
};Persist state between applications:
class ConfigApp : public ksApplication
{
protected:
bool loop() override {
if (configurationComplete()) {
// Save completion flag
saveFlag("config_complete", true);
return false;
}
return true;
}
};
class MainApp : public ksApplication
{
protected:
bool init() override {
if (!loadFlag("config_complete")) {
return false; // Go to config
}
// Clear flag for next time
saveFlag("config_complete", false);
addComponent<ksWifiConnector>();
return true;
}
};Boot β App 1 β (fails) β App 2 β (fails) β App 3 β (succeeds) β Run
β
App 3 fails
β
Boot β App 1 β (succeeds) β Run
Each rotation:
- Destroys previous application completely
- Creates new application instance
- Calls init() β postInit() β loop()
- Runs until failure or manual termination
- Old application fully destroyed before new one created
- All components and resources released
- Fresh start on each rotation
bool loop() override {
if (somethingMinor) {
return false; // β Too sensitive, causes loop
}
return true;
}bool init() override {
if (needConfig) {
return false;
}
// β Forgets to check again next time
addComponent<ksWifiConnector>();
return true;
}int globalCounter = 0; // β Survives rotation
class MyApp : public ksApplication {
bool loop() override {
globalCounter++; // Wrong! App may rotate
return true;
}
};- Application β Applications being rotated
- Component β Components within applications
- Getting-Started β First-time setup guide
- Examples β Complete working examples
π€ This wiki is automatically generated and may contain errors.
Please report any issues here:
π https://github.com/cziter15/ksIotFrameworkLib/issues