Doxygen with github
Loading...
Searching...
No Matches
ksIotFrameworkLib

‍Arduino Library for ESP32/ESP8266 - a composition-oriented Internet of Things framework that provides a simple and extendable architecture, handles device setup (WiFi setup, MQTT and application-specific configuration), network connectivity, MQTT telemetry protocol, and more...

Read the documentation License

Codacy Badge Lines of Code Commit activity Powered by platformio Powered by pioarduino

image

IMPORTANT FOR ESP32

This library targets Arduino 3+ on ESP32. Due to PlatformIO limitations, it does not automatically fetch the latest versions. Use the pioarduino fork by Jason2866 in your platformio.ini file:

platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip

IMPORTANT FOR ESP8266

For ESP8266, the latest supported version is based on SDK305. To use it, please add this build flag:

DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305

πŸ“œ Motivation

  • The goal of this project is to create a simple template or starting point for development of applications targeting Espressif microcontrollers.
  • This project aims to streamline the process of copying and modifying source code for different devices.
  • I wanted to apply DRY pattern for my DYI devices, by integrating all common components into a simple library.

πŸ“š Documentation

  • Detailed documentation can be found here.

Examples

πŸ› οΈ Architecture

flowchart TD
subgraph Application_Init["Application_Init"]
B("Mark app state as initialized")
A("Add initial components")
end
subgraph Application_Loop["Application_Loop"]
CCS{"State?"}
Loop{{"For each component"}}
LP1@{ label: "Call component's loop" }
LP2@{ label: "Call component's init" }
LP3@{ label: "Call component's postInit" }
LP4("Remove component")
DF{"Success?"}
SCS2("State -> Initialized")
SCS3("State -> Active")
X0{{"Continue"}}
X1{{"Break"}}
Continue["Continue"]
end
AppState{"AppState"} -- NotInitialized --> Application_Init
AppState -- Initialized --> Application_Loop
A --> B
Loop --> CCS
CCS -- Active --> LP1
CCS -- NotInitialized --> LP2
CCS -- Initialized --> LP3
CCS -- ToRemove --> LP4
LP2 --> SCS2
SCS2 --> DF
LP3 --> SCS3
SCS3 --> DF
LP1 --> DF
DF -- True --> X0
DF -- False --> X1
LP4 --> Continue
LP1@{ shape: rounded}
LP2@{ shape: rounded}
LP3@{ shape: rounded}
  • Only one application runs at a time.
  • Each application manages its own set of components, the framework's core building blocks.
  • Component states are managed within the application's loop function.
  • Components implement init, postInit, and loop methods.
  • Components marked for removal are safely deleted in the next cycle.

πŸ“ Utilities and components structure

πŸ“ ksf
β”œβ”€β”€ πŸ“„ ksAppRotator ─── Application rotator component
β”œβ”€β”€ πŸ“„ ksRtti ─── Implements RTTI (run-time type information) for objects
β”œβ”€β”€ πŸ“„ ksComponent ─── Base component class
β”œβ”€β”€ πŸ“„ ksConstants ─── Basic low-level definitions
β”œβ”€β”€ πŸ“‚ misc
β”‚ β”œβ”€β”€ πŸ“„ ksCertUtils ─── MQTT certificate utilities
β”‚ β”œβ”€β”€ πŸ“„ ksConfig ─── Configuration file handling
β”‚ β”œβ”€β”€ πŸ“„ ksDomainQuery ─── Custom DNS implementation
β”‚ β”œβ”€β”€ πŸ“„ ksSimpleTimer ─── Simple timer functionality
β”‚ └── πŸ“„ ksWSServer ─── Internal WS handling for device portal
└── πŸ“‚ comps
β”œβ”€β”€ πŸ“„ ksConfigProvider ─── Manages configuration parameters and storage
β”œβ”€β”€ πŸ“„ ksDevStatMqttReporter ─── Sends periodic device status updates via MQTT
β”œβ”€β”€ πŸ“„ ksDevicePortal ─── Implements a web-based configuration portal
β”œβ”€β”€ πŸ“„ ksLed ─── Simplifies LED control
β”œβ”€β”€ πŸ“„ ksMqttConfigProvider ─── Manages MQTT-related configuration
β”œβ”€β”€ πŸ“„ ksMqttConnector ─── Handles MQTT connection management
β”œβ”€β”€ πŸ“„ ksResetButton ─── Implements reset button functionality
β”œβ”€β”€ πŸ“„ ksWifiConfigurator ─── Handles WiFi configuration setup
└── πŸ“„ ksWifiConnector ─── Manages WiFi connection

πŸ”… Rules

  • Components should be added in the app's init method, so they will be available for postInit methods. (you can anytime later, from the loop but please treat it like exceptional case)
  • The init method is the best place to add dependent components, setup initial pin values etc.
  • The postInit method is the best place to obtain a weak pointer to another component by calling findComponent. This will handle cases when other components were added via init method.

🌱 Building the Application

To create an application, define a new class that inherits from ksApplication and add initial components in the init method. Refer to projects like emon_fw for a practical example.

πŸ”Ž How It Works

  • The application is instantiated, and its init method is called. If init returns false, the loop method is skipped, and the App Rotator proceeds to instantiate and run the next application in its sequence.
  • If init returns true, the loop method executes, initializing all components.
  • In the next iteration, each component’s postInit method is invoked.
  • Once initialized, the application enters a continuous loop, calling each component’s loop method.
  • If any component’s loop method returns false, the application terminates, and the App Rotator proceeds to the next application.
bool PelletInfo::init()
{
// Add WiFi and MQTT debug components
addComponent<ksf::comps::ksWifiConnector>(PelletInfoConfig::pelletInfoDeviceName);
addComponent<ksf::comps::ksMqttDebugResponder>();
addComponent<ksf::comps::ksDevStatMqttReporter>();
// Add OTA updater component
addComponent<ksf::comps::ksDevicePortal>();
// Add state display and receiver components
addComponent<comps::StateDisplay>();
addComponent<comps::StateReceiver>();
// Add reset button component
addComponent<ksf::comps::ksResetButton>(CFG_PUSH_PIN, LOW);
// Add MQTT connector component
addComponent<ksf::comps::ksMqttConnector>();
// Initialization completed; return true to indicate success
return true;
}

πŸ” Application rotator

The library implements one very useful utility named ksAppRotator. This object can wrap application instantiation logic into something like carousel or rotator.

Typically the device hosts two applications. First application is running core device logic while the second one is dedicated to help the user with the device configuration.

Each application has it's own loop method taking care of all underlying logic. In case of fail (which can happen anytime, even when creating the application object), the rotator will spawn next application and start processing it's logic until fail or break.

This is very flexible, because you can even raise fail (return false) from application's init method and then go directly into the configuration mode (for example there's no WiFi credentials provided by the user).

πŸ”£ Compiler flags

  • Bare Arduino projects need to have gnu++2a enabled via compiler.cpp.extra_flags= option in the board.txt file.

#️⃣ Custom RTTI

  • Use the KSF_RTTI_DECLARATIONS macro to provide proper runtime type information generation for proper casting of components.
  • See ksConfigProvider.h for an example. Your application components should use this macro, otherwise the component finding mechanism won't work.

πŸ”₯ Saving power

  • Modem sleep is enabled by default and can be controlled as an optional parameter in the ksWifiConnector constructor.
  • Automatic modem sleep requires the DTIM (Delivery Traffic Indication Message) to be correctly set on the access point.
  • The best value for me was 3. It allows the ESP32 to go down from around 100mA to 20mA.

πŸ“‘ Dependencies

  • It is highly recommended to use PlatformIO as it will automatically download dependencies!

πŸ”‘ Frameworks

πŸ”‘ Libraries