-
-
Notifications
You must be signed in to change notification settings - Fork 3
Component
A Component is the fundamental building block in ksIotFrameworkLib, encapsulating specific functionality in a reusable, composable unit.
Components are modular, self-contained units that provide specific functionality like WiFi connectivity, MQTT communication, LED control, or any custom behavior. They are managed by the Application and follow a well-defined lifecycle.
- Modular — Each component has a single, focused responsibility
- Reusable — Can be used across multiple applications
- Composable — Multiple components work together seamlessly
- Self-contained — Manages its own state and resources
Every component must:
- Inherit from
ksComponent - Use the RTTI macro
- Override lifecycle methods
#pragma once
#include <ksf/ksComponent.h>
class MyComponent : public ksf::ksComponent
{
// Required: RTTI macro
KSF_RTTI_DECLARATIONS(MyComponent, ksComponent)
protected:
bool init() override {
// One-time initialization
return true; // false = component fails
}
bool postInit() override {
// Setup dependencies, find other components
return true;
}
bool loop() override {
// Called repeatedly
return true; // false = component fails
}
public:
~MyComponent() override {
// Cleanup
}
};┌─────────────────────────────────────────────────────────┐
│ CREATED │
│ Component instantiated via addComponent<T>() │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ NOT_INITIALIZED │
│ init() is called │
│ ✓ Setup hardware │
│ ✓ Initialize state │
└─────────────────────┬───────────────────────────────────┘
│ return true
▼
┌─────────────────────────────────────────────────────────┐
│ INITIALIZED │
│ Component ready, other components can find it │
│ postInit() is called │
│ ✓ Find dependent components │
│ ✓ Setup interactions │
└─────────────────────┬───────────────────────────────────┘
│ return true
▼
┌─────────────────────────────────────────────────────────┐
│ ACTIVE │
│ loop() called repeatedly │
│ ✓ Perform ongoing operations │
│ ✓ Process events │
└─────────────────────┬───────────────────────────────────┘
│
│ return false or remove()
▼
┌─────────────────────────────────────────────────────────┐
│ TO_REMOVE │
│ Marked for removal │
│ ✓ Cleanup in progress │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ DELETED │
│ Memory freed, resources released │
└─────────────────────────────────────────────────────────┘
Called once when component is created. Use for:
- Hardware initialization (GPIO, peripherals)
- Creating objects and resources
- Initial configuration
- Validation of prerequisites
Return Value:
-
true— Success, proceed to INITIALIZED state -
false— Failure, application will fail
Example:
bool init() override {
// Initialize serial
Serial.begin(115200);
// Setup pin
pinMode(buttonPin, INPUT_PULLUP);
// Create timer
lastCheck = millis();
return true;
}Called after ALL components are initialized. Use for:
- Finding references to other components
- Setting up component interactions
- Registering event callbacks
- Any operation requiring other components to exist
Return Value:
-
true— Success, proceed to ACTIVE state -
false— Failure, application will fail
Example:
bool postInit() override {
// Find MQTT component (must exist by now)
mqtt = findComponent<ksMqttConnector>();
if (!mqtt) {
return false; // Dependency not found
}
// Subscribe to topics
mqtt->subscribe("device/control");
// Find LED for feedback
led = findComponent<ksLed>();
return true;
}Called repeatedly by the framework. Use for:
- Polling sensors
- Processing data
- Implementing component logic
- Checking time-based conditions
Return Value:
-
true— Continue running -
false— Component failed, triggers app rotation
Important: Must return quickly, no blocking calls!
Example:
bool loop() override {
// Check timer (non-blocking)
if (millis() - lastCheck > interval) {
// Read sensor
float value = readSensor();
// Send via MQTT
if (mqtt && mqtt->isConnected()) {
mqtt->publish("sensor", String(value));
}
lastCheck = millis();
}
return true;
}Get reference to other component in postInit():
class MyComponent : public ksComponent
{
private:
std::shared_ptr<ksMqttConnector> mqtt;
std::shared_ptr<ksLed> led;
protected:
bool postInit() override {
mqtt = findComponent<ksMqttConnector>();
led = findComponent<ksLed>();
if (!mqtt || !led) {
return false;
}
// Now use them
mqtt->onMessage([this, led](auto topic, auto payload) {
led->toggle();
});
return true;
}
};Use events for loose coupling:
// Publisher
bool loop() override {
if (eventDetected()) {
publishEvent("sensor_triggered", data);
}
return true;
}
// Subscriber
bool postInit() override {
subscribe("sensor_triggered", [](auto data) {
// Handle event
activateActuator();
});
return true;
}Store data in application:
// Save state
getApplication()->setData("sensorValue", value);
// Retrieve state
auto value = getApplication()->getData("sensorValue");The framework uses a custom RTTI (Run-Time Type Information) system to enable:
- Type-safe component discovery
- Safe component casting
- Dynamic type checking
Every component MUST use this macro at the top of the class:
class MyComponent : public ksComponent
{
KSF_RTTI_DECLARATIONS(MyComponent, ksComponent)
// ...
};// Safe cast with check
auto comp = findComponent<ksComponent>();
if (comp && comp->isA<MyComponent>()) {
auto myComp = comp->asA<MyComponent>();
// Use myComp
}// ✅ Good - Single responsibility
class TemperatureSensor : public ksComponent {
bool loop() override {
float temp = readTemp();
publish("temp", temp);
return true;
}
};
// ❌ Bad - Multiple responsibilities
class KitchenSink : public ksComponent {
bool loop() override {
readTemp();
connectWiFi();
updateDisplay();
sendEmail();
return true;
}
};bool loop() override {
// ✅ Good - Non-blocking
if (millis() - lastTime > interval) {
doWork();
lastTime = millis();
}
return true;
}
// ❌ Bad - Blocking delay
bool loop() override {
doWork();
delay(1000); // Don't use delay()!
return true;
}~MyComponent() override {
// Clean up
if (sensorActive) {
sensor.end();
}
if (buffer) {
free(buffer);
}
}bool postInit() override {
mqtt = findComponent<ksMqttConnector>();
// Validate required dependency
if (!mqtt) {
return false; // Can't work without it
}
// Optional dependency
led = findComponent<ksLed>();
// Can work with or without LED
return true;
}class SensorComponent : public ksComponent
{
private:
float lastValue;
unsigned long lastRead;
int errorCount;
protected:
bool loop() override {
// Access member state
if (errorCount > maxErrors) {
return false;
}
return true;
}
};class TempSensor : public ksComponent
{
KSF_RTTI_DECLARATIONS(TempSensor, ksComponent)
private:
std::shared_ptr<ksMqttConnector> mqtt;
unsigned long lastRead = 0;
const unsigned long interval = 5000;
protected:
bool init() override {
lastRead = millis();
return true;
}
bool postInit() override {
mqtt = findComponent<ksMqttConnector>();
return true; // MQTT optional
}
bool loop() override {
if (millis() - lastRead >= interval) {
float temp = readTemperature();
if (mqtt && mqtt->isConnected()) {
mqtt->publish("sensor/temperature", String(temp));
}
lastRead = millis();
}
return true;
}
float readTemperature() {
// Read actual sensor here
return 22.5;
}
};class RelayController : public ksComponent
{
KSF_RTTI_DECLARATIONS(RelayController, ksComponent)
private:
int relayPin;
protected:
bool init() override {
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, LOW);
return true;
}
bool postInit() override {
auto mqtt = findComponent<ksMqttConnector>();
if (mqtt) {
mqtt->onMessage([this](auto topic, auto payload) {
if (strcmp(topic, "relay/set") == 0) {
if (payload == "ON") {
digitalWrite(relayPin, HIGH);
} else if (payload == "OFF") {
digitalWrite(relayPin, LOW);
}
}
});
}
return true;
}
public:
RelayController(int pin) : relayPin(pin) {}
};class PumpController : public ksComponent
{
KSF_RTTI_DECLARATIONS(PumpController, ksComponent)
private:
enum State { IDLE, PRIMING, RUNNING, SHUTDOWN };
State state = IDLE;
std::shared_ptr<ksMqttConnector> mqtt;
unsigned long stateTime = 0;
protected:
bool postInit() override {
mqtt = findComponent<ksMqttConnector>();
return true;
}
bool loop() override {
switch (state) {
case IDLE:
if (shouldStart()) {
state = PRIMING;
stateTime = millis();
mqtt->publish("pump/state", "PRIMING");
}
break;
case PRIMING:
if (millis() - stateTime > 5000) {
state = RUNNING;
mqtt->publish("pump/state", "RUNNING");
}
break;
case RUNNING:
if (shouldStop()) {
state = SHUTDOWN;
mqtt->publish("pump/state", "SHUTDOWN");
}
break;
case SHUTDOWN:
if (millis() - stateTime > 3000) {
state = IDLE;
mqtt->publish("pump/state", "IDLE");
}
break;
}
return true;
}
};bool loop() override {
doSomething();
delay(1000); // ❌ Blocks entire framework
return true;
}bool init() override {
while (!wifiConnected()) { // ❌ Never blocks
delay(100);
}
return true;
}class MyComponent : public ksComponent
{
// ❌ Missing: KSF_RTTI_DECLARATIONS(MyComponent, ksComponent)
bool init() override { return true; }
};private:
ksMqttConnector* mqtt; // ❌ Unsafe
bool postInit() override {
mqtt = findComponent<ksMqttConnector>().get(); // Don't
return true;
}- Application — Components managed by applications
- Components-Reference — Built-in component docs
- Architecture — Component communication patterns
- Advanced-Topics — Events, custom RTTI details
🤖 This wiki is automatically generated and may contain errors.
Please report any issues here:
👉 https://github.com/cziter15/ksIotFrameworkLib/issues