SystemK v2.00: Interface Improvements #15

Merged
Joe merged 11 commits from interface_improvements into main 2026-03-26 02:08:40 +00:00
Owner

Overview

Several improvements were made to the SystemK Hardware Abstraction Layer (HAL) and
related interfaces. They are ordered from least to most invasive:

# Change Risk
1.1 Add BLE_Init() to the BLE HAL Low
1.2 Add SETTINGS_Load() to the Settings HAL Low
1.3 Document BLE_Quiet / BLE_Unquiet semantics None
2.1 Fix HW_Execute_Console_Command parameter type Low
2.2 Tighten BLE_GetMyAddress pointer parameter Low
2.3 Add max_len parameter to SETTINGS_get_device_name Low
3.1 Remove HW_NeoPixels_Get_My_Color() from the HAL Medium
3.2 Replace HW_NeoPixels_Set_RGB with HW_NeoPixels_Set_Color Medium
4.1 Replace void *Data in AudioAction_T with a typed union High
4.2 Make Prepare_Tag / Send_Tag accept explicit arguments Medium
4.3 Replace generic settings accessors with per-setting typed functions High

Phase 1 — Pure Additions

1.1 BLE_Init() added to BLE/BLE_HW_Interface.h

BLE initialization previously happened implicitly inside each platform
implementation with no explicit contract. Platform porters had to reverse-engineer
where and whether initialization was expected.

// New declaration:
SystemKResult_T BLE_Init(void);

SystemK.c now calls BLE_Init() from Initialize_SystemK(), before the state
machine task is created.


1.2 SETTINGS_Load() added to Settings/Settings_Interface.h

Settings loading also happened implicitly. SETTINGS_Save() existed in the
interface but its counterpart did not.

// New declaration:
SystemKResult_T SETTINGS_Load(void);

SystemK.c now calls SETTINGS_Load() from Initialize_SystemK(), before
BLE_Init() and the state machine task.


1.3 BLE_Quiet / BLE_Unquiet semantics documented

A comment block was added to both declarations in BLE/BLE_HW_Interface.h
describing the full contract: timer behavior, idempotency guarantees, interaction
between timed and indefinite quiet, and the SYSTEMK_RESULT_NOT_READY return
condition. No code changes.


Phase 2 — Mechanical Type Fixes

2.1 HW_Execute_Console_Command parameter type corrected

The command is a text string but was typed as const uint8_t *const. Callers
worked around this with explicit casts.

// Before:
SystemKResult_T HW_Execute_Console_Command(const uint8_t * const command);

// After:
SystemKResult_T HW_Execute_Console_Command(const char * const command);

2.2 BLE_GetMyAddress pointer parameter tightened

The raw uint8_t * parameter gave no indication of the required buffer size.

// Before:
SystemKResult_T BLE_GetMyAddress(uint8_t * BD_ADDR);

// After:
SystemKResult_T BLE_GetMyAddress(uint8_t BD_ADDR[BD_ADDR_SIZE]);

No change is required at call sites; the array type is compatible.


2.3 SETTINGS_get_device_name gains a max_len parameter

Callers had to know the correct buffer size out-of-band.

// Before:
SystemKResult_T SETTINGS_get_device_name(char* name);

// After:
SystemKResult_T SETTINGS_get_device_name(char* name, size_t max_len);

Phase 3 — NeoPixel Refactor

3.1 HW_NeoPixels_Get_My_Color() removed from the HAL

Team color is a game/settings concept, not a hardware concept. The HAL should not
need to know about team assignments.

// Removed:
color_t HW_NeoPixels_Get_My_Color(void);

The seven call sites in NeoPixels/Animations/ now use the GAME_Get_My_Color()
inline helper in Game/Game.h, which reads team, player, and weapon from settings
and derives the color via PROTOCOLS_GetColor().


3.2 HW_NeoPixels_Set_RGB replaced by HW_NeoPixels_Set_Color

The old signature required callers to decompose color_t into separate R, G, B
components and apply gamma correction themselves. Some call sites also swapped
channels to compensate for GRB-order hardware — hardware-specific behavior that
belongs in the HAL.

// Before:
SystemKResult_T HW_NeoPixels_Set_RGB(NeoPixelsChannel_T channel, uint8_t position,
                                      uint8_t red, uint8_t green, uint8_t blue);

// After:
SystemKResult_T HW_NeoPixels_Set_Color(NeoPixelsChannel_T channel, uint8_t position,
                                        color_t color);

The platform implementation is now responsible for gamma correction and any
color-channel reordering required by its hardware.


Phase 4 — Structural Changes

4.1 AudioAction_T.Data changed from void * to a typed union

void *Data was an untyped payload with no documented contract per action ID.
In practice only two action IDs used non-null data, both passing a pointer to a
uint8_t. This change makes the type explicit and eliminates pointer indirection.

// Before:
typedef struct {
    AudioActionID_T ID;
    bool Play_To_Completion;
    void * Data;
} AudioAction_T;

// After:
typedef union {
    uint8_t number;   // used by AUDIO_PRONOUNCE_NUMBER_0_TO_100 and AUDIO_SET_VOLUME
} AudioActionData_T;

typedef struct {
    AudioActionID_T ID;
    bool Play_To_Completion;
    AudioActionData_T Data;
} AudioAction_T;

Call sites that previously passed (void *)0x00 now omit Data entirely (C99
zero-initializes unspecified members). Call sites that previously passed
(void *)&variable now pass { .number = variable } by value.

The struct shrinks from 12 to 8 bytes. FreeRTOS queues created with
xQueueCreate(n, sizeof(AudioAction_T)) pick up the new size automatically at
runtime.


4.2 Prepare_Tag / Send_Tag accept explicit arguments

The original interface communicated through hidden global state: Prepare_Tag()
encoded a packet from settings into a module-level variable; Send_Tag() consumed
it. The contract was invisible to callers and to platform implementers.

The new interface uses a handle:

// Before:
SystemKResult_T Prepare_Tag(void);
SystemKResult_T Send_Tag(void);

// After:
PreparedTag_T *Prepare_Tag(const TagPacket_T *packet);
SystemKResult_T Send_Tag(PreparedTag_T *tag);

PreparedTag_T is defined in Game/Game.h (SystemK code) and contains a
TimedPulseTrain_T *pulse_train member.

SystemK builds the TagPacket_T from current settings via
GAME_Build_My_Tag_Packet() in Game/Game.h and stores the returned
PreparedTag_T * in GAME_Prepared_Tag (defined in Game/Game.c,
declared extern in Game/Game.h) so it is accessible from both the countdown
state (which calls Prepare_Tag) and the interacting substate (which calls
Send_Tag).

The platform is responsible for encoding the pulse train in Prepare_Tag and
transmitting it in Send_Tag. PROTOCOLS_EncodePacket() is called exactly once,
in Prepare_Tag.


4.3 Generic settings accessors replaced with per-setting typed functions

SETTINGS_get_uint8_t(id, ptr) and SETTINGS_get_uint32_t(id, ptr) baked the
storage type of each setting into every call site via the function name. If a
setting's range ever grows (e.g., Max_Health exceeding 255), all call sites
silently truncate with no compile-time warning.

The SystemKSettingID_T enum is removed from the public interface entirely. In
its place, each setting has one dedicated typed getter and one typed setter:

// Removed from the interface:
SystemKResult_T SETTINGS_get_uint8_t(SystemKSettingID_T id, uint8_t *value);
SystemKResult_T SETTINGS_set_uint8_t(SystemKSettingID_T id, uint8_t value);
SystemKResult_T SETTINGS_get_uint32_t(SystemKSettingID_T id, uint32_t *value);
SystemKResult_T SETTINGS_set_uint32_t(SystemKSettingID_T id, uint32_t value);

// Added (one pair per setting, derived from Settings.csv):
SystemKResult_T SETTINGS_get_Is_Right_Handed(uint8_t *value);
SystemKResult_T SETTINGS_set_Is_Right_Handed(uint8_t value);
SystemKResult_T SETTINGS_get_Audio_Volume(uint8_t *value);
SystemKResult_T SETTINGS_set_Audio_Volume(uint8_t value);
SystemKResult_T SETTINGS_get_Team_ID(uint8_t *value);
SystemKResult_T SETTINGS_set_Team_ID(uint8_t value);
SystemKResult_T SETTINGS_get_Player_ID(uint8_t *value);
SystemKResult_T SETTINGS_set_Player_ID(uint8_t value);
SystemKResult_T SETTINGS_get_Weapon_ID(uint8_t *value);
SystemKResult_T SETTINGS_set_Weapon_ID(uint8_t value);
SystemKResult_T SETTINGS_get_Max_Health(uint8_t *value);
SystemKResult_T SETTINGS_set_Max_Health(uint8_t value);
SystemKResult_T SETTINGS_get_N_Special_Weapons_On_Reentry(uint8_t *value);
SystemKResult_T SETTINGS_set_N_Special_Weapons_On_Reentry(uint8_t value);
SystemKResult_T SETTINGS_get_T_Start_Game_in_ms(uint32_t *value);
SystemKResult_T SETTINGS_set_T_Start_Game_in_ms(uint32_t value);
SystemKResult_T SETTINGS_get_T_Game_Length_in_ms(uint32_t *value);
SystemKResult_T SETTINGS_set_T_Game_Length_in_ms(uint32_t value);
SystemKResult_T SETTINGS_get_Secondary_Color(uint32_t *value);
SystemKResult_T SETTINGS_set_Secondary_Color(uint32_t value);

// Read-only platform constant (not in Settings.csv):
SystemKResult_T SETTINGS_get_Device_Type(uint32_t *value);

The function names are derived from the Key column in Settings/Settings.csv.
Settings/generate_settings_code.py has been updated to generate both the
interface declarations and the per-setting implementations. Adding a new setting
now requires only a new row in the CSV; changing a setting's type requires editing
one CSV cell, regenerating, and fixing the compile errors the compiler identifies.


Platform Porting Guide

This section lists every change a platform implementation must make to compile
against the updated SystemK interface.

Settings (Settings_Interface.h)

  1. Add SETTINGS_Load().
    Rename your existing initialization function (e.g., Initialize_Settings()) to
    SETTINGS_Load(). It must return SystemKResult_T and signal readiness by
    setting the SYS_SETTINGS_READY event group bit before returning.

  2. Replace the four generic accessors with per-setting functions.
    Delete SETTINGS_get_uint8_t, SETTINGS_set_uint8_t, SETTINGS_get_uint32_t,
    and SETTINGS_set_uint32_t. Implement one getter and one setter for each
    setting listed in Settings/Settings.csv, using the naming convention
    SETTINGS_get_<Key> / SETTINGS_set_<Key>. Run
    Settings/generate_settings_code.py to generate a starting implementation.

  3. Add SETTINGS_get_Device_Type(uint32_t *value).
    This is a read-only function that returns your platform's BLE device type
    constant. It is not generated from Settings.csv.

  4. Update SETTINGS_get_device_name.
    Add a size_t max_len parameter and use it to limit the copy into the caller's
    buffer, ensuring null-termination within max_len bytes.

BLE (BLE/BLE_HW_Interface.h)

  1. Add BLE_Init().
    Implement SystemKResult_T BLE_Init(void). Move any BLE host/controller
    initialization that previously happened at startup into this function. Return
    SYSTEMK_RESULT_SUCCESS on success or SYSTEMK_RESULT_UNSPECIFIED_FAILURE if
    the BLE stack fails to initialize.

  2. Update BLE_GetMyAddress.
    Change the parameter from uint8_t *BD_ADDR to uint8_t BD_ADDR[BD_ADDR_SIZE].
    The function body requires no change; only the signature needs updating.

Console (Console_HW_Interface.h)

  1. Update HW_Execute_Console_Command.
    Change the parameter from const uint8_t *const command to
    const char *const command. Remove any (const char *) casts at call sites
    within your platform code.

NeoPixels (NeoPixels/NeoPixel_HW_Interface.h)

  1. Delete HW_NeoPixels_Get_My_Color().
    Remove the implementation entirely. Team color is now resolved in SystemK.

  2. Replace HW_NeoPixels_Set_RGB with HW_NeoPixels_Set_Color.

    // Before:
    SystemKResult_T HW_NeoPixels_Set_RGB(NeoPixelsChannel_T channel,
                                          uint8_t position,
                                          uint8_t red, uint8_t green, uint8_t blue);
    
    // After:
    SystemKResult_T HW_NeoPixels_Set_Color(NeoPixelsChannel_T channel,
                                            uint8_t position,
                                            color_t color);
    

    Your implementation must now decompose color using Red(), Green(),
    Blue() and apply gamma correction. If your hardware uses GRB channel ordering,
    swap the red and green components here. Example for RGB-order hardware with
    gamma correction:

    SystemKResult_T HW_NeoPixels_Set_Color(NeoPixelsChannel_T channel,
                                            uint8_t position,
                                            color_t color)
    {
        // Apply gamma correction and write to hardware:
        set_pixel(channel, position,
                  Gamma8[Red(color)],
                  Gamma8[Green(color)],
                  Gamma8[Blue(color)]);
        return SYSTEMK_RESULT_SUCCESS;
    }
    

Audio (Audio/Audio_HW_Interface.h)

  1. Update AudioAction_T data access.
    action.Data is no longer a void *. Where your implementation previously
    dereferenced the pointer — e.g., *((uint8_t *)action.Data) — use the union
    member directly:

    // Before:
    uint8_t number = *((uint8_t *)action.Data);
    
    // After:
    uint8_t number = action.Data.number;
    

IR (IR/IR_HW_Interface.h)

  1. Implement the new Prepare_Tag / Send_Tag signatures.

    PreparedTag_T *Prepare_Tag(const TagPacket_T *packet);
    SystemKResult_T Send_Tag(PreparedTag_T *tag);
    

    PreparedTag_T is defined in Game/Game.h and contains a single member,
    TimedPulseTrain_T *pulse_train. You do not need to define the struct yourself.

    Prepare_Tag receives a fully populated TagPacket_T from SystemK. It
    must encode the packet via PROTOCOLS_EncodePacket(), store the result in a
    static PreparedTag_T, prepare the hardware for transmission, and return a
    pointer to that static. Do not call PROTOCOLS_EncodePacket() in Send_Tag
    — encoding must happen in Prepare_Tag to minimize transmit latency.

    Send_Tag receives the pointer returned by Prepare_Tag and transmits the
    pre-encoded pulse train via tag->pulse_train. It must not re-encode anything.

    Your platform no longer needs to read settings (team ID, player ID, weapon ID)
    in either function; SystemK provides a fully populated TagPacket_T.

    Example:

    static PreparedTag_T Prepared_Shot;
    
    PreparedTag_T *Prepare_Tag(const TagPacket_T *packet)
    {
        Prepared_Shot.pulse_train = PROTOCOLS_EncodePacket((TagPacket_T *)packet);
        // ... log, enable transmitter hardware ...
        return &Prepared_Shot;
    }
    
    SystemKResult_T Send_Tag(PreparedTag_T *tag)
    {
        // ... transmit tag->pulse_train ...
        return SYSTEMK_RESULT_SUCCESS;
    }
    
## Overview Several improvements were made to the SystemK Hardware Abstraction Layer (HAL) and related interfaces. They are ordered from least to most invasive: | # | Change | Risk | |---|--------|------| | 1.1 | Add `BLE_Init()` to the BLE HAL | Low | | 1.2 | Add `SETTINGS_Load()` to the Settings HAL | Low | | 1.3 | Document `BLE_Quiet` / `BLE_Unquiet` semantics | None | | 2.1 | Fix `HW_Execute_Console_Command` parameter type | Low | | 2.2 | Tighten `BLE_GetMyAddress` pointer parameter | Low | | 2.3 | Add `max_len` parameter to `SETTINGS_get_device_name` | Low | | 3.1 | Remove `HW_NeoPixels_Get_My_Color()` from the HAL | Medium | | 3.2 | Replace `HW_NeoPixels_Set_RGB` with `HW_NeoPixels_Set_Color` | Medium | | 4.1 | Replace `void *Data` in `AudioAction_T` with a typed union | High | | 4.2 | Make `Prepare_Tag` / `Send_Tag` accept explicit arguments | Medium | | 4.3 | Replace generic settings accessors with per-setting typed functions | High | --- ## Phase 1 — Pure Additions ### 1.1 `BLE_Init()` added to `BLE/BLE_HW_Interface.h` BLE initialization previously happened implicitly inside each platform implementation with no explicit contract. Platform porters had to reverse-engineer where and whether initialization was expected. ```c // New declaration: SystemKResult_T BLE_Init(void); ``` `SystemK.c` now calls `BLE_Init()` from `Initialize_SystemK()`, before the state machine task is created. --- ### 1.2 `SETTINGS_Load()` added to `Settings/Settings_Interface.h` Settings loading also happened implicitly. `SETTINGS_Save()` existed in the interface but its counterpart did not. ```c // New declaration: SystemKResult_T SETTINGS_Load(void); ``` `SystemK.c` now calls `SETTINGS_Load()` from `Initialize_SystemK()`, before `BLE_Init()` and the state machine task. --- ### 1.3 `BLE_Quiet` / `BLE_Unquiet` semantics documented A comment block was added to both declarations in `BLE/BLE_HW_Interface.h` describing the full contract: timer behavior, idempotency guarantees, interaction between timed and indefinite quiet, and the `SYSTEMK_RESULT_NOT_READY` return condition. No code changes. --- ## Phase 2 — Mechanical Type Fixes ### 2.1 `HW_Execute_Console_Command` parameter type corrected The command is a text string but was typed as `const uint8_t *const`. Callers worked around this with explicit casts. ```c // Before: SystemKResult_T HW_Execute_Console_Command(const uint8_t * const command); // After: SystemKResult_T HW_Execute_Console_Command(const char * const command); ``` --- ### 2.2 `BLE_GetMyAddress` pointer parameter tightened The raw `uint8_t *` parameter gave no indication of the required buffer size. ```c // Before: SystemKResult_T BLE_GetMyAddress(uint8_t * BD_ADDR); // After: SystemKResult_T BLE_GetMyAddress(uint8_t BD_ADDR[BD_ADDR_SIZE]); ``` No change is required at call sites; the array type is compatible. --- ### 2.3 `SETTINGS_get_device_name` gains a `max_len` parameter Callers had to know the correct buffer size out-of-band. ```c // Before: SystemKResult_T SETTINGS_get_device_name(char* name); // After: SystemKResult_T SETTINGS_get_device_name(char* name, size_t max_len); ``` --- ## Phase 3 — NeoPixel Refactor ### 3.1 `HW_NeoPixels_Get_My_Color()` removed from the HAL Team color is a game/settings concept, not a hardware concept. The HAL should not need to know about team assignments. ```c // Removed: color_t HW_NeoPixels_Get_My_Color(void); ``` The seven call sites in `NeoPixels/Animations/` now use the `GAME_Get_My_Color()` inline helper in `Game/Game.h`, which reads team, player, and weapon from settings and derives the color via `PROTOCOLS_GetColor()`. --- ### 3.2 `HW_NeoPixels_Set_RGB` replaced by `HW_NeoPixels_Set_Color` The old signature required callers to decompose `color_t` into separate R, G, B components and apply gamma correction themselves. Some call sites also swapped channels to compensate for GRB-order hardware — hardware-specific behavior that belongs in the HAL. ```c // Before: SystemKResult_T HW_NeoPixels_Set_RGB(NeoPixelsChannel_T channel, uint8_t position, uint8_t red, uint8_t green, uint8_t blue); // After: SystemKResult_T HW_NeoPixels_Set_Color(NeoPixelsChannel_T channel, uint8_t position, color_t color); ``` The platform implementation is now responsible for gamma correction and any color-channel reordering required by its hardware. --- ## Phase 4 — Structural Changes ### 4.1 `AudioAction_T.Data` changed from `void *` to a typed union `void *Data` was an untyped payload with no documented contract per action ID. In practice only two action IDs used non-null data, both passing a pointer to a `uint8_t`. This change makes the type explicit and eliminates pointer indirection. ```c // Before: typedef struct { AudioActionID_T ID; bool Play_To_Completion; void * Data; } AudioAction_T; // After: typedef union { uint8_t number; // used by AUDIO_PRONOUNCE_NUMBER_0_TO_100 and AUDIO_SET_VOLUME } AudioActionData_T; typedef struct { AudioActionID_T ID; bool Play_To_Completion; AudioActionData_T Data; } AudioAction_T; ``` Call sites that previously passed `(void *)0x00` now omit `Data` entirely (C99 zero-initializes unspecified members). Call sites that previously passed `(void *)&variable` now pass `{ .number = variable }` by value. The struct shrinks from 12 to 8 bytes. FreeRTOS queues created with `xQueueCreate(n, sizeof(AudioAction_T))` pick up the new size automatically at runtime. --- ### 4.2 `Prepare_Tag` / `Send_Tag` accept explicit arguments The original interface communicated through hidden global state: `Prepare_Tag()` encoded a packet from settings into a module-level variable; `Send_Tag()` consumed it. The contract was invisible to callers and to platform implementers. The new interface uses a handle: ```c // Before: SystemKResult_T Prepare_Tag(void); SystemKResult_T Send_Tag(void); // After: PreparedTag_T *Prepare_Tag(const TagPacket_T *packet); SystemKResult_T Send_Tag(PreparedTag_T *tag); ``` `PreparedTag_T` is defined in `Game/Game.h` (SystemK code) and contains a `TimedPulseTrain_T *pulse_train` member. SystemK builds the `TagPacket_T` from current settings via `GAME_Build_My_Tag_Packet()` in `Game/Game.h` and stores the returned `PreparedTag_T *` in `GAME_Prepared_Tag` (defined in `Game/Game.c`, declared `extern` in `Game/Game.h`) so it is accessible from both the countdown state (which calls `Prepare_Tag`) and the interacting substate (which calls `Send_Tag`). The platform is responsible for encoding the pulse train in `Prepare_Tag` and transmitting it in `Send_Tag`. `PROTOCOLS_EncodePacket()` is called exactly once, in `Prepare_Tag`. --- ### 4.3 Generic settings accessors replaced with per-setting typed functions `SETTINGS_get_uint8_t(id, ptr)` and `SETTINGS_get_uint32_t(id, ptr)` baked the storage type of each setting into every call site via the function name. If a setting's range ever grows (e.g., `Max_Health` exceeding 255), all call sites silently truncate with no compile-time warning. The `SystemKSettingID_T` enum is removed from the public interface entirely. In its place, each setting has one dedicated typed getter and one typed setter: ```c // Removed from the interface: SystemKResult_T SETTINGS_get_uint8_t(SystemKSettingID_T id, uint8_t *value); SystemKResult_T SETTINGS_set_uint8_t(SystemKSettingID_T id, uint8_t value); SystemKResult_T SETTINGS_get_uint32_t(SystemKSettingID_T id, uint32_t *value); SystemKResult_T SETTINGS_set_uint32_t(SystemKSettingID_T id, uint32_t value); // Added (one pair per setting, derived from Settings.csv): SystemKResult_T SETTINGS_get_Is_Right_Handed(uint8_t *value); SystemKResult_T SETTINGS_set_Is_Right_Handed(uint8_t value); SystemKResult_T SETTINGS_get_Audio_Volume(uint8_t *value); SystemKResult_T SETTINGS_set_Audio_Volume(uint8_t value); SystemKResult_T SETTINGS_get_Team_ID(uint8_t *value); SystemKResult_T SETTINGS_set_Team_ID(uint8_t value); SystemKResult_T SETTINGS_get_Player_ID(uint8_t *value); SystemKResult_T SETTINGS_set_Player_ID(uint8_t value); SystemKResult_T SETTINGS_get_Weapon_ID(uint8_t *value); SystemKResult_T SETTINGS_set_Weapon_ID(uint8_t value); SystemKResult_T SETTINGS_get_Max_Health(uint8_t *value); SystemKResult_T SETTINGS_set_Max_Health(uint8_t value); SystemKResult_T SETTINGS_get_N_Special_Weapons_On_Reentry(uint8_t *value); SystemKResult_T SETTINGS_set_N_Special_Weapons_On_Reentry(uint8_t value); SystemKResult_T SETTINGS_get_T_Start_Game_in_ms(uint32_t *value); SystemKResult_T SETTINGS_set_T_Start_Game_in_ms(uint32_t value); SystemKResult_T SETTINGS_get_T_Game_Length_in_ms(uint32_t *value); SystemKResult_T SETTINGS_set_T_Game_Length_in_ms(uint32_t value); SystemKResult_T SETTINGS_get_Secondary_Color(uint32_t *value); SystemKResult_T SETTINGS_set_Secondary_Color(uint32_t value); // Read-only platform constant (not in Settings.csv): SystemKResult_T SETTINGS_get_Device_Type(uint32_t *value); ``` The function names are derived from the `Key` column in `Settings/Settings.csv`. `Settings/generate_settings_code.py` has been updated to generate both the interface declarations and the per-setting implementations. Adding a new setting now requires only a new row in the CSV; changing a setting's type requires editing one CSV cell, regenerating, and fixing the compile errors the compiler identifies. --- ## Platform Porting Guide This section lists every change a platform implementation must make to compile against the updated SystemK interface. ### Settings (`Settings_Interface.h`) 1. **Add `SETTINGS_Load()`.** Rename your existing initialization function (e.g., `Initialize_Settings()`) to `SETTINGS_Load()`. It must return `SystemKResult_T` and signal readiness by setting the `SYS_SETTINGS_READY` event group bit before returning. 2. **Replace the four generic accessors with per-setting functions.** Delete `SETTINGS_get_uint8_t`, `SETTINGS_set_uint8_t`, `SETTINGS_get_uint32_t`, and `SETTINGS_set_uint32_t`. Implement one getter and one setter for each setting listed in `Settings/Settings.csv`, using the naming convention `SETTINGS_get_<Key>` / `SETTINGS_set_<Key>`. Run `Settings/generate_settings_code.py` to generate a starting implementation. 3. **Add `SETTINGS_get_Device_Type(uint32_t *value)`.** This is a read-only function that returns your platform's BLE device type constant. It is not generated from `Settings.csv`. 4. **Update `SETTINGS_get_device_name`.** Add a `size_t max_len` parameter and use it to limit the copy into the caller's buffer, ensuring null-termination within `max_len` bytes. ### BLE (`BLE/BLE_HW_Interface.h`) 5. **Add `BLE_Init()`.** Implement `SystemKResult_T BLE_Init(void)`. Move any BLE host/controller initialization that previously happened at startup into this function. Return `SYSTEMK_RESULT_SUCCESS` on success or `SYSTEMK_RESULT_UNSPECIFIED_FAILURE` if the BLE stack fails to initialize. 6. **Update `BLE_GetMyAddress`.** Change the parameter from `uint8_t *BD_ADDR` to `uint8_t BD_ADDR[BD_ADDR_SIZE]`. The function body requires no change; only the signature needs updating. ### Console (`Console_HW_Interface.h`) 7. **Update `HW_Execute_Console_Command`.** Change the parameter from `const uint8_t *const command` to `const char *const command`. Remove any `(const char *)` casts at call sites within your platform code. ### NeoPixels (`NeoPixels/NeoPixel_HW_Interface.h`) 8. **Delete `HW_NeoPixels_Get_My_Color()`.** Remove the implementation entirely. Team color is now resolved in SystemK. 9. **Replace `HW_NeoPixels_Set_RGB` with `HW_NeoPixels_Set_Color`.** ```c // Before: SystemKResult_T HW_NeoPixels_Set_RGB(NeoPixelsChannel_T channel, uint8_t position, uint8_t red, uint8_t green, uint8_t blue); // After: SystemKResult_T HW_NeoPixels_Set_Color(NeoPixelsChannel_T channel, uint8_t position, color_t color); ``` Your implementation must now decompose `color` using `Red()`, `Green()`, `Blue()` and apply gamma correction. If your hardware uses GRB channel ordering, swap the red and green components here. Example for RGB-order hardware with gamma correction: ```c SystemKResult_T HW_NeoPixels_Set_Color(NeoPixelsChannel_T channel, uint8_t position, color_t color) { // Apply gamma correction and write to hardware: set_pixel(channel, position, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]); return SYSTEMK_RESULT_SUCCESS; } ``` ### Audio (`Audio/Audio_HW_Interface.h`) 10. **Update `AudioAction_T` data access.** `action.Data` is no longer a `void *`. Where your implementation previously dereferenced the pointer — e.g., `*((uint8_t *)action.Data)` — use the union member directly: ```c // Before: uint8_t number = *((uint8_t *)action.Data); // After: uint8_t number = action.Data.number; ``` ### IR (`IR/IR_HW_Interface.h`) 11. **Implement the new `Prepare_Tag` / `Send_Tag` signatures.** ```c PreparedTag_T *Prepare_Tag(const TagPacket_T *packet); SystemKResult_T Send_Tag(PreparedTag_T *tag); ``` `PreparedTag_T` is defined in `Game/Game.h` and contains a single member, `TimedPulseTrain_T *pulse_train`. You do not need to define the struct yourself. `Prepare_Tag` receives a fully populated `TagPacket_T` from SystemK. It must encode the packet via `PROTOCOLS_EncodePacket()`, store the result in a static `PreparedTag_T`, prepare the hardware for transmission, and return a pointer to that static. **Do not call `PROTOCOLS_EncodePacket()` in `Send_Tag`** — encoding must happen in `Prepare_Tag` to minimize transmit latency. `Send_Tag` receives the pointer returned by `Prepare_Tag` and transmits the pre-encoded pulse train via `tag->pulse_train`. It must not re-encode anything. Your platform no longer needs to read settings (team ID, player ID, weapon ID) in either function; SystemK provides a fully populated `TagPacket_T`. Example: ```c static PreparedTag_T Prepared_Shot; PreparedTag_T *Prepare_Tag(const TagPacket_T *packet) { Prepared_Shot.pulse_train = PROTOCOLS_EncodePacket((TagPacket_T *)packet); // ... log, enable transmitter hardware ... return &Prepared_Shot; } SystemKResult_T Send_Tag(PreparedTag_T *tag) { // ... transmit tag->pulse_train ... return SYSTEMK_RESULT_SUCCESS; } ```
Joe changed title from SystemK Interface Improvements to SystemK v2.00: Interface Improvements 2026-03-26 01:47:26 +00:00
@ -59,3 +59,3 @@
#define NEC_COMMAND_ROKU_02_OK 3576350698
#define NEC_COMMAND_ROKU_02_COUNTERCLOCKWISE 2272839658
#define NEC_COMMAND_ROKU_02_ASTERIX 2272839658
#define NEC_COMMAND_ROKU_02_ASTERIX 2657208298
Author
Owner

I don't know how this was wrong, but it was.

I don't know how this was wrong, but it was.
@ -75,1 +73,3 @@
#define SYSTEMK_VERSION_STRING "01.01"
#define SYSTEMK_MAJOR_VERSION 2
#define SYSTEMK_MINOR_VERSION 0
#define SYSTEMK_VERSION_STRING "02.00"
Author
Owner

We changed the interface, so we're changing the major version.

We changed the interface, so we're changing the major version.
Joe merged commit baded1dc61 into main 2026-03-26 02:08:40 +00:00
Joe deleted branch interface_improvements 2026-03-26 02:08:40 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Software/SystemK!15
No description provided.