Reworked BLE according to v0.12 of the KTag Beacon Specification (#5)

This change to SystemK implements version 0.12 of the KTag Beacon Specification.

The spec. is here: [KTag Beacon Specification v0.12](https://ktag.clubk.club/Technology/BLE/KTag%20Beacon%20Specification%20v0.12.pdf)
This change also includes the 31AUG2025 changes to the State Machine (now documented [here](https://ktag.clubk.club/Technology/SystemK/SystemKStateMachine.drawio.png)), as well as changes to support automated testing.

**All projects should update to this version.**

Co-authored-by: Joe Kearney <joe@clubk.club>
Reviewed-on: #5
This commit is contained in:
Joe Kearney 2025-09-01 17:44:10 +00:00
parent 7aa827563a
commit 430aec54b8
22 changed files with 368 additions and 47 deletions

View file

@ -26,5 +26,8 @@ SystemKResult_T BLE_GetMyAddress(uint8_t * BD_ADDR);
SystemKResult_T BLE_ScanAndAdvertise(void);
SystemKResult_T BLE_SetAdvertisingData(BLE_AdvertisingData_T * data);
SystemKResult_T BLE_StopAdvertising(void);
SystemKResult_T BLE_StopScanning(void);
SystemKResult_T BLE_Quiet(uint32_t duration_ms);
SystemKResult_T BLE_Unquiet(void);
#endif // BLE_HW_INTERFACE_H

View file

@ -24,7 +24,7 @@
#include "SystemK.h"
#define N_PACKET_BUFFERS 10
#define N_PACKET_BUFFERS 20
static BLE_Packet_T Packet_Buffers[N_PACKET_BUFFERS];
static BLE_AdvertisingData_T Advertising_Data;
static uint8_t my_BD_ADDR[6];
@ -147,6 +147,54 @@ void BLE_UpdateInstigationPacket(uint32_t Game_Length_in_ms, uint32_t Time_Remai
}
}
void BLE_UpdateEventPacket(uint8_t target_BD_ADDR[BD_ADDR_SIZE], BLE_EventID_T event_ID, uint32_t event_data)
{
static uint8_t EventNumber = 0;
Advertising_Data.length = BLE_KTAG_PACKET_TOTAL_SIZE;
// Manufacturer Specific Data
Advertising_Data.data[0] = 0x1E;
Advertising_Data.data[1] = 0xFF;
Advertising_Data.data[2] = 0xFF;
Advertising_Data.data[3] = 0xFF;
Advertising_Data.data[4] = 'K';
Advertising_Data.data[5] = 'T';
Advertising_Data.data[6] = 'a';
Advertising_Data.data[7] = 'g';
Advertising_Data.data[8] = BLE_PACKET_TYPE_EVENT;
Advertising_Data.data[9] = EventNumber++;
Advertising_Data.data[10] = target_BD_ADDR[0];
Advertising_Data.data[11] = target_BD_ADDR[1];
Advertising_Data.data[12] = target_BD_ADDR[2];
Advertising_Data.data[13] = target_BD_ADDR[3];
Advertising_Data.data[14] = target_BD_ADDR[4];
Advertising_Data.data[15] = target_BD_ADDR[5];
Advertising_Data.data[16] = (event_ID >> 0) & 0xFF;
Advertising_Data.data[17] = (event_ID >> 8) & 0xFF;
Advertising_Data.data[18] = (event_ID >> 16) & 0xFF;
Advertising_Data.data[19] = (event_ID >> 24) & 0xFF;
Advertising_Data.data[20] = (event_data >> 0) & 0xFF;
Advertising_Data.data[21] = (event_data >> 8) & 0xFF;
Advertising_Data.data[22] = (event_data >> 16) & 0xFF;
Advertising_Data.data[23] = (event_data >> 24) & 0xFF;
Advertising_Data.data[24] = 0xFF;
Advertising_Data.data[25] = 0xFF;
Advertising_Data.data[26] = 0xFF;
Advertising_Data.data[27] = 0xFF;
Advertising_Data.data[28] = 0xFF;
Advertising_Data.data[29] = 0xFF;
Advertising_Data.data[30] = 0xFF;
SystemKResult_T result = BLE_SetAdvertisingData(&Advertising_Data);
if (result != SYSTEMK_RESULT_SUCCESS)
{
KLOG_ERROR(KLOG_TAG, "Error updating event packet!");
}
}
void BLE_UpdateStatusPacket(uint8_t current_state)
{
static uint8_t EventNumber = 0;
@ -262,7 +310,7 @@ void BLE_UpdateTagPacket(int16_t damage, color_t color, uint8_t target_BD_ADDR[B
//! Event number used for all BLE_PACKET_TYPE_PARAMETERS packets.
static uint8_t ParametersEventNumber = 0;
void BLE_RespondToConfigurationPacket(const BLE_ParametersPacket_T *const packet, BLE_ConfigurationSubtype_T response)
void BLE_RespondToConfigurationPacket(const BLE_ParametersPacket_T *const packet, BLE_ParametersSubtype_T response)
{
Advertising_Data.length = BLE_KTAG_PACKET_TOTAL_SIZE;
@ -312,7 +360,7 @@ void BLE_RespondToConfigurationPacket(const BLE_ParametersPacket_T *const packet
}
}
void BLE_BroadcastCurrentParameterInfoPacket(const BLE_ParameterKey_T key_one, const uint32_t value_one, const BLE_ParameterKey_T key_two, const uint32_t value_two)
void BLE_UpdateParametersPacket(BLE_ParametersSubtype_T subtype, const uint8_t target_BD_ADDR[BD_ADDR_SIZE], const BLE_ParameterKey_T key_one, const uint32_t value_one, const BLE_ParameterKey_T key_two, const uint32_t value_two)
{
Advertising_Data.length = BLE_KTAG_PACKET_TOTAL_SIZE;
@ -327,13 +375,13 @@ void BLE_BroadcastCurrentParameterInfoPacket(const BLE_ParameterKey_T key_one, c
Advertising_Data.data[7] = 'g';
Advertising_Data.data[8] = BLE_PACKET_TYPE_PARAMETERS;
Advertising_Data.data[9] = ParametersEventNumber++;
Advertising_Data.data[10] = BLE_BROADCAST_ADDRESS[0];
Advertising_Data.data[11] = BLE_BROADCAST_ADDRESS[1];
Advertising_Data.data[12] = BLE_BROADCAST_ADDRESS[2];
Advertising_Data.data[13] = BLE_BROADCAST_ADDRESS[3];
Advertising_Data.data[14] = BLE_BROADCAST_ADDRESS[4];
Advertising_Data.data[15] = BLE_BROADCAST_ADDRESS[5];
Advertising_Data.data[16] = BLE_CURRENT_PARAMETER_INFORMATION;
Advertising_Data.data[10] = target_BD_ADDR[0];
Advertising_Data.data[11] = target_BD_ADDR[1];
Advertising_Data.data[12] = target_BD_ADDR[2];
Advertising_Data.data[13] = target_BD_ADDR[3];
Advertising_Data.data[14] = target_BD_ADDR[4];
Advertising_Data.data[15] = target_BD_ADDR[5];
Advertising_Data.data[16] = (uint8_t)subtype;
Advertising_Data.data[17] = (uint8_t)((key_one >> 0) & 0xFF);
Advertising_Data.data[18] = (uint8_t)((key_one >> 8) & 0xFF);
Advertising_Data.data[19] = (uint8_t)((value_one >> 0) & 0xFF);
@ -348,15 +396,16 @@ void BLE_BroadcastCurrentParameterInfoPacket(const BLE_ParameterKey_T key_one, c
Advertising_Data.data[28] = (uint8_t)((value_two >> 24) & 0xFF);
Advertising_Data.data[29] = 0xFF;
Advertising_Data.data[30] = 0xFF;
SystemKResult_T result = BLE_SetAdvertisingData(&Advertising_Data);
if (result != SYSTEMK_RESULT_SUCCESS)
{
KLOG_ERROR(KLOG_TAG, "Error responding to configuration packet!");
}
KLOG_ERROR(KLOG_TAG, "Error updating parameters packet!");
}
}
void BLE_UpdateHelloPacket()
{
static uint8_t EventNumber = 0;

View file

@ -100,6 +100,9 @@ typedef enum
BLE_EVENT_CONFIGURED = 2,
BLE_EVENT_WRAPUP_COMPLETE = 3,
BLE_EVENT_GAME_OVER = 4,
BLE_EVENT_QUIET = 5,
BLE_EVENT_UNQUIET = 6,
BLE_EVENT_FORCE_STATE = 7,
BLE_EVENT_UNUSED = 0xFFFFFFFF
} BLE_EventID_T;
@ -174,7 +177,7 @@ typedef enum
BLE_ACKNOWLEDGE_PARAMETER_CHANGE = 0x03,
BLE_ERROR_CHANGING_PARAMETERS = 0x04,
BLE_ERROR_RESPONDING_TO_REQUEST = 0xFF
} BLE_ConfigurationSubtype_T;
} BLE_ParametersSubtype_T;
typedef enum
{
@ -292,10 +295,11 @@ void BLE_InitPacketBuffers(void);
BLE_Packet_T *BLE_DecodeKTagPacket(const uint8_t *received_data, uint8_t received_data_length, uint8_t peer_BD_ADDR[BD_ADDR_SIZE], int8_t rssi_in_dBm);
void BLE_UpdateInstigationPacket(uint32_t Game_Length_in_ms, uint32_t Time_Remaining_Until_Countdown_in_ms);
void BLE_UpdateEventPacket(uint8_t target_BD_ADDR[BD_ADDR_SIZE], BLE_EventID_T event, uint32_t data);
void BLE_UpdateStatusPacket(uint8_t current_state);
void BLE_UpdateTagPacket(int16_t damage, color_t color, uint8_t target_BD_ADDR[BD_ADDR_SIZE]);
void BLE_RespondToConfigurationPacket(const BLE_ParametersPacket_T *const packet, BLE_ConfigurationSubtype_T response);
void BLE_BroadcastCurrentParameterInfoPacket(const BLE_ParameterKey_T key_one, const uint32_t value_one, const BLE_ParameterKey_T key_two, const uint32_t value_two);
void BLE_RespondToConfigurationPacket(const BLE_ParametersPacket_T *const packet, BLE_ParametersSubtype_T response);
void BLE_UpdateParametersPacket(BLE_ParametersSubtype_T subtype, const uint8_t target_BD_ADDR[BD_ADDR_SIZE], const BLE_ParameterKey_T key_one, const uint32_t value_one, const BLE_ParameterKey_T key_two, const uint32_t value_two);
void BLE_UpdateHelloPacket();
bool BLE_IsBLEPacketForMe(const uint8_t BD_ADDR[BD_ADDR_SIZE]);

View file

@ -22,6 +22,7 @@
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "SystemK.h"
const uint8_t BLE_BROADCAST_ADDRESS[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
@ -51,4 +52,76 @@ const char *BLE_ADDR_To_Str(const uint8_t bd_addr[6])
str_addr[17] = '\0'; // Ensure null termination.
return str_addr;
}
SystemKResult_T BLE_HandleCommonEvents(KEvent_T *event)
{
SystemKResult_T result = SYSTEMK_RESULT_EVENT_NOT_HANDLED;
if (event->ID == KEVENT_BLE_PACKET_RECEIVED)
{
BLE_Packet_T * packet = (BLE_Packet_T *)event->Data;
if (packet->Generic.type == BLE_PACKET_TYPE_EVENT)
{
BLE_EventPacket_T * event_packet = (BLE_EventPacket_T *)packet;
if (BLE_IsBLEPacketForMe(event_packet->target_BD_ADDR))
{
if (BLE_IsPacketNew(event_packet->BD_ADDR, BLE_PACKET_TYPE_EVENT, event_packet->event_number))
{
if (event_packet->event_ID == BLE_EVENT_QUIET)
{
uint32_t duration_ms = event_packet->event_data;
if (BLE_Quiet(duration_ms) == SYSTEMK_RESULT_SUCCESS)
{
if (duration_ms > 0)
{
KLOG_INFO("BLE", "Quiet mode activated for %lu ms.", duration_ms);
}
else
{
KLOG_INFO("BLE", "Quiet mode activated until further notice.");
}
}
else
{
KLOG_ERROR("BLE", "Failed to activate quiet mode.");
}
result = SYSTEMK_RESULT_SUCCESS;
}
else if (event_packet->event_ID == BLE_EVENT_UNQUIET)
{
if (BLE_Unquiet() == SYSTEMK_RESULT_SUCCESS)
{
KLOG_INFO("BLE", "Quiet mode deactivated.");
}
else
{
KLOG_ERROR("BLE", "Failed to deactivate quiet mode.");
}
result = SYSTEMK_RESULT_SUCCESS;
}
else if (event_packet->event_ID == BLE_EVENT_FORCE_STATE)
{
uint32_t requested_state = event_packet->event_data;
if (requested_state < STATE_IS_OUT_OF_RANGE)
{
Transition_For_Event(GetContext(), (StateID_T)requested_state, event);
KLOG_INFO("BLE", "State %lu forced.", requested_state);
result = SYSTEMK_RESULT_SUCCESS;
}
else
{
KLOG_ERROR("BLE", "Attempted to force unknown state %lu.", requested_state);
result = SYSTEMK_RESULT_OVERFLOW;
}
}
}
}
}
}
return result;
}

View file

@ -19,6 +19,7 @@
* file in the root of this repository. If not, see <http://www.gnu.org/licenses/>.
*/
extern const uint8_t BLE_BROADCAST_ADDRESS[6];
extern const uint8_t BLE_BROADCAST_ADDRESS[6];
const char* BLE_ADDR_To_Str(const uint8_t bd_addr[6]);
const char* BLE_ADDR_To_Str(const uint8_t bd_addr[6]);
SystemKResult_T BLE_HandleCommonEvents(KEvent_T *event);