Lifecycle & Main Loop
Driving a kbox-stack instance — the IKnxStack facade methods, Init vs Work vs Process, readiness checks, enabling group-object communication, and the programming button.
The facade
IKnxStack is the entire public surface you drive at runtime. Every method:
| Method | Signature | Purpose |
|---|---|---|
Init | void Init() | One-time boot: read persistent state from flash, load the individual address, init the driver/transceiver. |
Work | void Work() | Call every main-loop iteration: process RX, drive TX, run your logic. |
Process | void Process() | Step the stack's processing without the full Work cycle. |
OnButtonPress | void OnButtonPress() | Notify the stack the programming button was pressed (toggle programming mode). |
SetLogic | void SetLogic(ILogic* logic) | Register the application logic. KnxLogicBase calls this for you. |
IsAllMachineStateLoaded | bool IsAllMachineStateLoaded() | True when every loadable object is in the LOADED state (device is configured). |
Tables | GroupObjectTables& Tables() | Access the group-address ↔ object database. |
SendTelegramGroupValueWrite | void SendTelegramGroupValueWrite(DeviceObject& deviceParameter) | Publish an object's value to its group address(es). |
EnableGroupObjectCommunication | void EnableGroupObjectCommunication() | Allow group objects to send/receive on the bus. |
DisableGroupObjectCommunication | void DisableGroupObjectCommunication() | Suspend group-object communication. |
SaveUserData | bool SaveUserData(const uint8_t* data, uint16_t size) | Persist the application's own arbitrary-size data into the flash layout's user-data region. Returns false if size exceeds UserDataCapacity() or the underlying flash write fails. |
LoadUserData | uint16_t LoadUserData(uint8_t* out, uint16_t maxSize) | Load previously saved application data. Returns the valid payload length (0 = no valid data — never saved, or corrupt/torn write); out must not be used when the return value is 0. |
UserDataCapacity | uint16_t UserDataCapacity() const | The maximum size accepted by SaveUserData. Calls above this limit are rejected (not clamped). |
Init vs Work vs Process
Init()runs once, before the main loop. It loads the persisted machine state from flash (individual address, per-object load states), falls back to defaults on first boot, and initializes the bus driver and transceiver.Work()runs every main-loop iteration. It drains and decodes the RX queue, pushes any pending TX to the wire, calls your logic'sWork(), and handles connection time-outs and the start-up run-state transition.Process()advances the stack's processing step.Work()is the normal driver for a bare-metal super-loop;Process()exists for integrations that want finer control over the processing phase.
int main(void) {
// ... platform + peripheral init ...
logic.Init(); // your application's own init
stack.Init(); // one-time stack boot: REQUIRED before the loop
while (1) {
stack.Work(); // every iteration
}
}Which method runs where:
Init()— once, inmain()before the loop.Work()/Process()— repeatedly, in the main loop.OnButtonPress()— from an interrupt/ISR context (a GPIO EXTI handler), not the main loop.
Readiness: IsAllMachineStateLoaded
A freshly manufactured device is unconfigured until ETS downloads its tables.
IsAllMachineStateLoaded() returns true only when all loadable objects (device object,
address table, association table, group object table, application program) are in the
LOADED state.
Use it as a gate: run application behaviour that depends on group communication only once the device reports loaded.
if (stack.IsAllMachineStateLoaded()) {
// device is configured — safe to publish values, run scheduled logic, etc.
}Enabling and disabling group object communication
EnableGroupObjectCommunication() / DisableGroupObjectCommunication() gate whether
group objects participate on the bus. Disable to quiesce the device (for example during a
maintenance or safe-stop condition) without tearing down the stack, and re-enable to
resume.
Button and programming mode
KNX devices assign their individual (physical) address via programming mode, entered
by a physical button. Wire your button interrupt to OnButtonPress():
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == KNX_BUTTON_Pin) {
stack.OnButtonPress(); // enter/leave programming mode
}
}While programming mode is active the stack responds to ETS individual-address assignment. See the platform peripherals page for the button and LED wiring: STM32 Peripherals.