Kbox-UniStack Docs

API Reference

The portable public API of the kbox-stack KNX library — header map, the IKnxStack facade, the MakeStack / MakeSecure factories, models, helpers, serialization, and storage.

The public surface of kbox-stack is portable: every type on this page is MCU-agnostic, and each hardware dependency is a pure I<X> interface. The prebuilt static library ships for Cortex-M3 / STM32F103xB today, but nothing on this page is STM32-specific.

Only the signatures are public. The library is distributed as a precompiled static .a plus 88 public headers under Stack/…; the implementation is not shipped. Include a type with its namespaced path, e.g. #include <Stack/Logic/IKnxStack.h>.

Header map

Every public header lives under Stack/<dir>/. This is what each directory provides.

DirectoryProvides
Builder/KnxTelegramBuilder — fluent construction of a raw KNX telegram / frame. StackFlashLayout.hMakeFlashLayout() and the KnxFlashLayout type. KnxDeviceStorage.h — the KnxDeviceStorage / KnxSecureDeviceStorage<S> consumer storage bundles.
Comparator/Difference — the old/new value pair delivered on a group-object change.
Config/StackConfig.h — compile-time knobs (KNX_APDU_MAX, KNX_FRAME_MAX, KNX_OBJ_VALUE_MAX).
Enums/All wire and stack enums. See Enums Reference.
Helpers/ByteSpan, FixedString, FixedVec, FrameRing, and the Dpt:: codecs.
Logger/BaseLogger — the logging base and LogLevelEnum. See Logging.
Logic/IKnxStack facade, ILogic, KnxLogicBase, and the MakeStack factory.
Models/DeviceObject, KnxData, KnxTelegram, KnxAddress, GroupObjectTables, StackParameters, GroupObjectFlag, interface-object / property models.
Peripheral/The I<X> peripheral interfaces (IBusDriver, IClock, IFlash, ILed, IButton, IWatchDog, ITransmitter) plus Base<X> and STM32 Hal<X> implementations.
Security/ISecurity, IKnxSecure.h (the MakeSecure factory), SecureConfig.h. See KNX Data Secure.
Serialize/Serializable, Serialized, SerializedTypeEnum.
Storage/FlashDataStore — magic + CRC framed blob persistence over an IFlash&.

Stack facade

IKnxStack is the whole runtime surface. You obtain one from MakeStack(...) and drive it from your main loop.

MethodSignaturePurpose
Initvoid Init()One-time init; loads machine state from flash. Call once after construction.
Workvoid Work()Drives the state machine and your logic.Work(). Call every main-loop iteration.
Processvoid Process()Bus-driver ISR hook — call from the RX/TX interrupt (wiring is platform-specific).
OnButtonPressvoid OnButtonPress()Signals the programming-mode button event to the stack.
SetLogicvoid SetLogic(ILogic* logic)Installs the application logic. Deriving from KnxLogicBase auto-registers, so this is usually not called directly.
IsAllMachineStateLoadedbool IsAllMachineStateLoaded()true once every stored table loaded successfully.
TablesGroupObjectTables& Tables()Access to the group-object database (push objects, resolve addresses).
SendTelegramGroupValueWritevoid SendTelegramGroupValueWrite(DeviceObject& deviceParameter)Sends a GroupValueWrite for the given object's current value.
EnableGroupObjectCommunicationvoid EnableGroupObjectCommunication()Enables group-object traffic.
DisableGroupObjectCommunicationvoid DisableGroupObjectCommunication()Disables group-object traffic.
SaveUserDatabool SaveUserData(const uint8_t* data, uint16_t size)Persists your own app data (e.g. an AC-state snapshot) into the layout's UserDataView() region. Returns false if size exceeds UserDataCapacity(), the flash write fails, or the flash layout is invalid.
LoadUserDatauint16_t LoadUserData(uint8_t* out, uint16_t maxSize)Loads previously-saved app data. Returns the valid payload length (>0), or 0 if nothing was saved, the data is corrupt (bad magic/CRC), or the flash layout is invalid — out must not be used when the return value is 0.
UserDataCapacityuint16_t UserDataCapacity() constMaximum size SaveUserData accepts (header excluded). Returns 0 if the flash layout is invalid.

MakeFlashLayout

Before either factory below runs, one base flash region (a single IFlash& you provide) is split into the stack's own named sub-regions by MakeFlashLayout(). It is placement-constructed the same way MakeStack/ MakeSecure are — into a caller-owned KnxFlashLayoutStorage — and returns a KnxFlashLayout& that both other factories consume.

// KBOX_FLASH_LAYOUT_STORAGE_SIZE == 256u (host upper bound).
struct KnxFlashLayoutStorage {
  alignas(alignof(std::max_align_t)) unsigned char data[KBOX_FLASH_LAYOUT_STORAGE_SIZE];
};

class KnxFlashLayout {
 public:
  IFlash& StackStateView();  // stack machine-state region
  IFlash& UserDataView();    // your own app-data region (SaveUserData/LoadUserData)
  IFlash& CounterView();     // Secure only — Classic returns a valid but fail-closed view
  IFlash& SioKeyView();      // Secure only — Classic returns a valid but fail-closed view
  bool    IsValid() const;   // checked by Stack::Init() — see Integration Contract
};

// Classic — regions: {stack-state, user-data}.
KnxFlashLayout& MakeFlashLayout(KnxFlashLayoutStorage& storage, IFlash& baseFlash,
                                const StackParameters& sp, uint16_t userDataPageCount);

// Secure — regions: {counter, sio-key, stack-state, user-data}.
KnxFlashLayout& MakeFlashLayout(KnxFlashLayoutStorage& storage, IFlash& baseFlash,
                                const StackParameters& sp, const SioSizing& sioSizing,
                                uint16_t userDataPageCount);

layout.IsValid() == false (misaligned base tail, or a region window that overflows the 64KB tail-address space) is not checked by this factory itself — it is propagated into MakeStack()'s Stack::Init(), which reports StackErrorTypeEnum_FLASH_LAYOUT_INVALID and stops before any flash-dependent step runs. See Integration Contract.

MakeStack / MakeSecure

The stack is placement-constructed into a caller-owned, opaquely-sized storage buffer. There is no heap allocation — you provide the memory and every dependency by reference, and all of them must outlive the returned handle.

// Storage size is chosen at compile time from KNX_APDU_MAX.
// KBOX_STACK_STORAGE_SIZE == 1344u  when KNX_APDU_MAX >= 254
//                        == 952u   otherwise
struct KnxStackStorage {
  alignas(alignof(std::max_align_t)) unsigned char data[KBOX_STACK_STORAGE_SIZE];
};

// Classic (non-secure) stack.
IKnxStack& MakeStack(KnxStackStorage& storage, StackParameters& stackParameters,
                     IBusDriver& busDriver, IWatchDog& watchDog, ILed& knxLed,
                     IClock& clock, ITransmitter& transmitter, IButton& button,
                     KnxFlashLayout& layout);

// Secure stack — same, plus an ISecurity& from MakeSecure(...).
IKnxStack& MakeStack(KnxStackStorage& storage, StackParameters& stackParameters,
                     IBusDriver& busDriver, IWatchDog& watchDog, ILed& knxLed,
                     IClock& clock, ITransmitter& transmitter, IButton& button,
                     KnxFlashLayout& layout, ISecurity& security);

The security object is built by its own factory. It needs the same KnxFlashLayout& (it reads its CounterView()/SioKeyView()), a sizing/buffers pair describing the key tables, the device individual address, and a clock.

// KBOX_SECURE_STORAGE_SIZE == 1560u when KNX_APDU_MAX >= 254, else 1304u.
struct SecureStackStorage {
  alignas(alignof(std::max_align_t)) unsigned char data[KBOX_SECURE_STORAGE_SIZE];
};

ISecurity& MakeSecure(SecureStackStorage& storage, KnxFlashLayout& layout,
                      const SioSizing& sizing, const SioBuffers& buffers,
                      KnxAddress deviceIA, IClock& clock);

Give KnxStackStorage, SecureStackStorage, and KnxFlashLayoutStorage static / file-scope lifetime. The returned reference points into that buffer; a stack-local buffer dangles the moment its function returns. layout itself must be constructed before both MakeSecure() and MakeStack(). See the Integration Contract.

Consumer storage bundles

Builder/KnxDeviceStorage.h collects the storage variables above (Classic: 2; Secure: 4, plus the MakeBuffers()/sioBuffers threading) into one aggregate per device kind, and adds header-only overloads of MakeFlashLayout/ MakeStack/MakeSecure that take the bundle instead of the individual storages. The underlying factories (shown above) are unchanged — these are one-line forwarders. This is the recommended way to wire a new device.

// ─── Classic ────────────────────────────────────────────────────────────
struct KnxDeviceStorage {
  KnxFlashLayoutStorage layout;
  KnxStackStorage       stack;
};

KnxFlashLayout& MakeFlashLayout(KnxDeviceStorage& device, IFlash& baseFlash,
                                const StackParameters& sp, uint16_t userDataPageCount);

IKnxStack& MakeStack(KnxDeviceStorage& device, StackParameters& stackParameters,
                     IBusDriver& busDriver, IWatchDog& watchDog, ILed& knxLed,
                     IClock& clock, ITransmitter& transmitter, IButton& button,
                     KnxFlashLayout& layout);

// Usage:
static KnxDeviceStorage device;
KnxFlashLayout& layout = MakeFlashLayout(device, baseFlash, stackParameters, /*userDataPageCount=*/1u);
IKnxStack& stack = MakeStack(device, stackParameters, busDriver, watchDog, knxLed,
                             clock, transmitter, button, layout);

// ─── Secure — S is a `static constexpr SioSizing` bound as a template ref ──
template <const SioSizing& S>
struct KnxSecureDeviceStorage {
  KnxFlashLayoutStorage layout;
  SecureStackStorage    secure;
  KnxStackStorage       stack;
  SioStorage<S>         sio;
};

template <const SioSizing& S>
KnxFlashLayout& MakeFlashLayout(KnxSecureDeviceStorage<S>& device, IFlash& baseFlash,
                                const StackParameters& sp, uint16_t userDataPageCount);

// deviceIA defaults to KnxAddress(0xFFFFu) — the S-Mode "unprogrammed" address;
// pass your own only if the device ships pre-addressed.
template <const SioSizing& S>
ISecurity& MakeSecure(KnxSecureDeviceStorage<S>& device, KnxFlashLayout& layout,
                      IClock& clock, KnxAddress deviceIA = KnxAddress(0xFFFFu));

template <const SioSizing& S>
IKnxStack& MakeStack(KnxSecureDeviceStorage<S>& device, StackParameters& stackParameters,
                     IBusDriver& busDriver, IWatchDog& watchDog, ILed& knxLed,
                     IClock& clock, ITransmitter& transmitter, IButton& button,
                     KnxFlashLayout& layout, ISecurity& security);

// Usage:
static constexpr SioSizing kSioSizing = {5u, 0u, 16u, 8u, 0u};
static KnxSecureDeviceStorage<kSioSizing> device;
KnxFlashLayout& layout = MakeFlashLayout(device, baseFlash, stackParameters, /*userDataPageCount=*/1u);
ISecurity& secure = MakeSecure(device, layout, clock);  // deviceIA default 0xFFFF
IKnxStack& stack = MakeStack(device, stackParameters, busDriver, watchDog, knxLed,
                             clock, transmitter, button, layout, secure);

Models

Helpers

Fixed-capacity, heap-free building blocks. All sizes are compile-time.

  • ByteSpan — a non-owning {const uint8_t*, size_t} view. Implicitly constructs from FixedVec<uint8_t,N> and std::vector<uint8_t>. Read-only.
  • FixedString<N> — a bounded string builder (append, appendDec, appendHexByte, appendHexSpaced, …) that flags overflowed() instead of overrunning. Aliases: LogString (192) and ErrString (16).
  • FixedVec<T, N> — a fixed-capacity std::vector-like container. It is fail-closed: overflowing push_back/insert drops the element and sets overflowed() rather than reallocating or writing out of bounds. Aliases include FrameBytes, ApduBytes, SmallApdu (16), MedApdu (72).
  • DPT codecs — the Dpt:: value encoders/decoders live under Helpers/Dpt/. See Value Encoding (DPT).

Serialize / Storage

Serialize/ is a compact tag-length-value scheme for packing typed records into a byte blob; Storage/FlashDataStore persists an arbitrary blob to an IFlash& behind a magic + CRC header.

// Serialize — a TLV record you Push into and iterate with Next().
class Serializable {
 public:
  virtual Serialized Serialize() = 0;
};

class Serialized {
 public:
  Serialized();
  Serialized(uint8_t* data, uint8_t size);
  void       Push(uint8_t* data, SerializedTypeEnum type, uint8_t size);
  void       Push(Serialized serializedData);
  bool       Valid();
  Serialized Next();
  uint8_t    GetType();
  uint8_t*   GetData();
  uint8_t*   GetPayload();
  uint8_t    GetDataSize();
  uint8_t    GetPayloadSize();
};

// Storage — framed blob persistence over an injected IFlash.
class FlashDataStore {
 public:
  explicit FlashDataStore(IFlash& flash);
  bool     Save(const uint8_t* data, uint16_t size);
  uint16_t Load(uint8_t* out, uint16_t maxSize);
  uint16_t Capacity() const;
};