Kbox-UniStack Docs

Architecture

The layered design of kbox-stack — facade, database, DPT helpers, peripheral interfaces, and security — plus telegram data flow, the heap-free memory model, and where your code plugs in.

Layer overview

kbox-stack is organized as a small set of layers. Only the top and the interface boundary are public; the concrete orchestrator, database internals, and security engine live inside the .a.

        ┌────────────────────────────────────────────────────────────┐
        │  Your application logic  (KnxLogicBase / ILogic)            │
        ├────────────────────────────────────────────────────────────┤
        │  Facade:  IKnxStack   (Init / Work / Process / …)           │
        ├─────────────────────┬──────────────────┬────────────────────┤
        │  GroupObjectTables  │  DPT helpers      │  Security          │
        │     (group addr     │  (Dpt::D9 float,  │  (ISecurity —      │
        │      ↔ object)      │   raw ↔ meaning)  │   optional, secure)│
        ├─────────────────────┴──────────────────┴────────────────────┤
        │  Peripheral interfaces (DI boundary — PORTABLE):            │
        │  IBusDriver ITransmitter IFlash IClock                      │
        │  ILed IButton IWatchDog                                     │
        ├────────────────────────────────────────────────────────────┤
        │  Platform layer (NOT part of the core):                     │
        │  Hal* implementations — STM32 HAL today                     │
        └────────────────────────────────────────────────────────────┘

The dashed rule that matters most is the peripheral interface boundary. Everything above it is the portable core; everything below it is a platform. The core is compiled into the archive with no reference to any concrete Hal* type.

Telegram data flow

A group-write from the bus travels through the stack like this:

The bus driver (IBusDriver) receives raw bytes from the KNX TP1 line and queues them.
IKnxStack::Work() drains the RX queue and decodes each frame into a telegram.
The stack filters echoes/repeats, then routes: broadcast → address programming; destination == our individual address → point-to-point management; group write/read → the database.
A GroupValueWrite is applied to the database, producing a list of changed objects; each change is reported to your logic via the onDifference callback.
Your logic reacts and, to publish a value back, calls SendTelegramGroupValueWrite(deviceObject).
The stack builds the response telegram and queues it; the bus driver encodes it to the wire and transmits it via the transceiver (ITransmitter).

So the round trip is: bus → driver → stack → onDifference → your logic → SendTelegramGroupValueWrite → stack → driver → bus.

Static memory model

No heap. The stack object is placement-new'd into a caller-owned KnxStackStorage — a fixed, statically-sized buffer you declare at file scope. There is no malloc in the construction path. The storage size depends on the configured maximum APDU length (KNX_APDU_MAX): a larger APDU selects a larger buffer.

Because the storage is caller-owned and static, its lifetime is your responsibility. If KnxStackStorage is stack-local, the IKnxStack& returned by the factory dangles the moment its scope ends. See Constructing the Stack and the Integration Contract.

Portable core, pluggable platform

The core (IKnxStack, database, DPT helpers, security) has no dependency on any concrete HAL. It sees only the I<X> peripheral interfaces. To retarget kbox-stack to a different MCU you implement those interfaces for your platform and rebuild the binary — the prebuilt archive that ships today is Cortex-M3 / STM32F103xB. The design is portable; the binary is platform-specific. See Porting.

Where your code plugs in

Your application logic derives from KnxLogicBase (which implements the stack's ILogic interface). Its constructor takes an IKnxStack& and auto-registers itself via SetLogic — so you never forget to wire it up.

#include <Stack/Logic/KnxLogicBase.h>

class MyLogic : public KnxLogicBase {
 public:
  explicit MyLogic(IKnxStack& stack) : KnxLogicBase(stack) { /* ... */ }
  void Work() override { /* product logic — stack.Work() calls this each loop */ }
};

Beyond the Work() hook, your device reacts to bus events through the six callbacks you register in StackParameters:

CallbackFires when
onDifferenceA group write changed one or more objects (the main event)
onKnxTelegramA raw application-level bus telegram arrived
onTelegramAny raw telegram arrived (byte span)
onErrorKnxTelegramA faulty telegram was received
onResetCommandAn A_Restart was processed
onStackErrorThe stack hit an error condition (e.g. a segment-size error)

See Group Objects for the DeviceObject model and Configuration for the full callback and parameter set.