/* * This program source code file is part of SystemK, a library in the KTag project. * * 🛡️ 🃞 * * Copyright © 2016-2025 Joseph P. Kearney and the KTag developers. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * There should be a copy of the GNU Affero General Public License in the LICENSE * file in the root of this repository. If not, see . */ #include #include "SystemK.h" #define N_PACKET_BUFFERS 10 static BLE_Packet_T Packet_Buffers[N_PACKET_BUFFERS]; static BLE_AdvertisingData_T Advertising_Data; static uint8_t my_BD_ADDR[6]; static bool is_my_BD_ADDR_initialized = false; static const char *KLOG_TAG = "BLE"; void BLE_InitPacketBuffers(void) { for (uint_fast8_t i = 0; i < N_PACKET_BUFFERS; i++) { Packet_Buffers[i].Generic.type = BLE_PACKET_TYPE_BUFFER_FREE; } } //! This function always returns a buffer, but it will clobber data if there are no free buffers. static inline BLE_Packet_T * BLE_GetPacketBuffer(void) { for (uint_fast8_t i = 0; i < N_PACKET_BUFFERS; i++) { if (Packet_Buffers[i].Generic.type == BLE_PACKET_TYPE_BUFFER_FREE) { return &Packet_Buffers[i]; } } KLOG_ERROR(KLOG_TAG, "Overwrote a BLE packet buffer: consider increasing N_PACKET_BUFFERS."); // Just use the last one. return &Packet_Buffers[N_PACKET_BUFFERS - 1]; } 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) { BLE_Packet_T * result = NULL; // All KTag packets are 31 bytes long. if (received_data_length == BLE_KTAG_PACKET_TOTAL_SIZE) { // Is this a KTag packet? if ( (received_data[0] == 0x1E) && (received_data[1] == 0xFF) && (received_data[2] == 0xFF) && (received_data[3] == 0xFF) && (received_data[4] == 'K') && (received_data[5] == 'T') && (received_data[6] == 'a') && (received_data[7] == 'g') ) { result = BLE_GetPacketBuffer(); memcpy(result->Generic.BD_ADDR, peer_BD_ADDR, BD_ADDR_SIZE); result->Generic.RSSI = rssi_in_dBm; uint8_t packet_type = received_data[8]; // Validate the packet type. if ( (packet_type < BLE_FIRST_VALID_PACKET_TYPE) || (packet_type > BLE_LAST_VALID_PACKET_TYPE) ) { packet_type = BLE_PACKET_TYPE_UNKNOWN; } result->Generic.type = packet_type; uint8_t event_number = received_data[9]; result->Generic.event_number = event_number; // Copy the remaining MAX_BLE_PACKET_DATA_SIZE bytes of data over. memcpy(result->Generic.data, &received_data[10], BLE_KTAG_PACKET_DATA_SIZE); } } return result; } void BLE_UpdateInstigationPacket(uint32_t Game_Length_in_ms, uint32_t Time_Remaining_Until_Countdown_in_ms) { 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_INSTIGATE_GAME; Advertising_Data.data[9] = EventNumber++; Advertising_Data.data[10] = (Game_Length_in_ms >> 0) & 0xFF; Advertising_Data.data[11] = (Game_Length_in_ms >> 8) & 0xFF; Advertising_Data.data[12] = (Game_Length_in_ms >> 16) & 0xFF; Advertising_Data.data[13] = (Game_Length_in_ms >> 24) & 0xFF; Advertising_Data.data[14] = (Time_Remaining_Until_Countdown_in_ms >> 0) & 0xFF; Advertising_Data.data[15] = (Time_Remaining_Until_Countdown_in_ms >> 8) & 0xFF; Advertising_Data.data[16] = (Time_Remaining_Until_Countdown_in_ms >> 16) & 0xFF; Advertising_Data.data[17] = (Time_Remaining_Until_Countdown_in_ms >> 24) & 0xFF; Advertising_Data.data[18] = 0x00; Advertising_Data.data[19] = 0xFF; Advertising_Data.data[20] = 0xFF; Advertising_Data.data[21] = 0xFF; Advertising_Data.data[22] = 0xFF; Advertising_Data.data[23] = 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 instigation packet!"); } } void BLE_UpdateStatusPacket(uint8_t current_state) { static uint8_t EventNumber = 0; uint8_t team_ID; uint8_t player_ID; uint8_t weapon_ID; Protocol_T protocol; (void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_TEAMID, &team_ID); (void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_PLAYERID, &player_ID); (void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_WEAPONID, &weapon_ID); protocol = GetWeaponFromID(weapon_ID).Protocol; uint32_t Team_Color = (uint32_t)PROTOCOLS_GetColor(protocol, team_ID, player_ID); 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_STATUS; Advertising_Data.data[9] = EventNumber++; Advertising_Data.data[10] = 4; // Tx Power in dBm Advertising_Data.data[11] = DUBUQUE_PROTOCOL; Advertising_Data.data[12] = team_ID; Advertising_Data.data[13] = player_ID; Advertising_Data.data[14] = Get_Health(); Advertising_Data.data[15] = 0x00; Advertising_Data.data[16] = MAX_HEALTH; Advertising_Data.data[17] = 0x00; Advertising_Data.data[18] = (Team_Color >> 0) & 0xFF; Advertising_Data.data[19] = (Team_Color >> 8) & 0xFF; Advertising_Data.data[20] = (Team_Color >> 16) & 0xFF; Advertising_Data.data[21] = (Team_Color >> 24) & 0xFF; Advertising_Data.data[22] = (Team_Color >> 0) & 0xFF; // Secondary Color Advertising_Data.data[23] = (Team_Color >> 8) & 0xFF; // Secondary Color Advertising_Data.data[24] = (Team_Color >> 16) & 0xFF; // Secondary Color Advertising_Data.data[25] = (Team_Color >> 24) & 0xFF; // Secondary Color Advertising_Data.data[26] = current_state; 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 status packet!"); } } void BLE_UpdateTagPacket(int16_t damage, color_t color, uint8_t target_BD_ADDR[BD_ADDR_SIZE]) { static uint8_t EventNumber = 0; uint8_t team_ID; uint8_t player_ID; (void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_TEAMID, &team_ID); (void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_PLAYERID, &player_ID); 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_TAG; Advertising_Data.data[9] = EventNumber++; Advertising_Data.data[10] = 4; // Tx Power in dBm Advertising_Data.data[11] = DUBUQUE_PROTOCOL; Advertising_Data.data[12] = team_ID; Advertising_Data.data[13] = player_ID; Advertising_Data.data[14] = (uint8_t)((damage >> 0) & 0xFF); Advertising_Data.data[15] = (uint8_t)((damage >> 8) & 0xFF); Advertising_Data.data[16] = (color >> 0) & 0xFF; Advertising_Data.data[17] = (color >> 8) & 0xFF; Advertising_Data.data[18] = (color >> 16) & 0xFF; Advertising_Data.data[19] = (color >> 24) & 0xFF; Advertising_Data.data[20] = target_BD_ADDR[0]; Advertising_Data.data[21] = target_BD_ADDR[1]; Advertising_Data.data[22] = target_BD_ADDR[2]; Advertising_Data.data[23] = target_BD_ADDR[3]; Advertising_Data.data[24] = target_BD_ADDR[4]; Advertising_Data.data[25] = target_BD_ADDR[5]; 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 status packet!"); } } void BLE_RespondToConfigurationPacket(const BLE_ConfigurationPacket_T *const packet, BLE_ConfigurationSubtype_T response) { 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_CONFIGURATION; Advertising_Data.data[9] = EventNumber++; Advertising_Data.data[10] = packet->BD_ADDR[0]; Advertising_Data.data[11] = packet->BD_ADDR[1]; Advertising_Data.data[12] = packet->BD_ADDR[2]; Advertising_Data.data[13] = packet->BD_ADDR[3]; Advertising_Data.data[14] = packet->BD_ADDR[4]; Advertising_Data.data[15] = packet->BD_ADDR[5]; Advertising_Data.data[16] = (uint8_t)response; Advertising_Data.data[17] = (uint8_t)((packet->key_one >> 0) & 0xFF); Advertising_Data.data[18] = (uint8_t)((packet->key_one >> 8) & 0xFF); Advertising_Data.data[19] = (uint8_t)((packet->value_one >> 0) & 0xFF); Advertising_Data.data[20] = (uint8_t)((packet->value_one >> 8) & 0xFF); Advertising_Data.data[21] = (uint8_t)((packet->value_one >> 16) & 0xFF); Advertising_Data.data[22] = (uint8_t)((packet->value_one >> 24) & 0xFF); Advertising_Data.data[23] = (uint8_t)((packet->key_two >> 0) & 0xFF); Advertising_Data.data[24] = (uint8_t)((packet->key_two >> 8) & 0xFF); Advertising_Data.data[25] = (uint8_t)((packet->value_two >> 0) & 0xFF); Advertising_Data.data[26] = (uint8_t)((packet->value_two >> 8) & 0xFF); Advertising_Data.data[27] = (uint8_t)((packet->value_two >> 16) & 0xFF); Advertising_Data.data[28] = (uint8_t)((packet->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!"); } } void BLE_UpdateHelloPacket() { static uint8_t EventNumber = 0; uint32_t device_type32; uint16_t device_type = BLE_DEVICE_TYPE_UNKNOWN; if (SETTINGS_get_uint32_t(SYSTEMK_SETTING_DEVICE_TYPE, &device_type32) == SYSTEMK_RESULT_SUCCESS) { device_type = (uint16_t)device_type32; } uint8_t team_ID; (void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_TEAMID, &team_ID); char device_name[SYSTEMK_MAX_CHARS_IN_DEVICE_NAME + 1]; SystemKResult_T result = SETTINGS_get_device_name(&device_name[0]); if (result != SYSTEMK_RESULT_SUCCESS) { strcpy(device_name, "Anonymous"); } size_t len = strlen(device_name); if (len < sizeof(device_name)) { memset(device_name + len, 0x00, sizeof(device_name) - len); } 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_HELLO; Advertising_Data.data[9] = EventNumber++; Advertising_Data.data[10] = SYSTEMK_MAJOR_VERSION; Advertising_Data.data[11] = SYSTEMK_MINOR_VERSION; Advertising_Data.data[12] = (device_type >> 0) & 0xFF; Advertising_Data.data[13] = (device_type >> 8) & 0xFF; Advertising_Data.data[14] = team_ID; Advertising_Data.data[15] = device_name[0]; Advertising_Data.data[16] = device_name[1]; Advertising_Data.data[17] = device_name[2]; Advertising_Data.data[18] = device_name[3]; Advertising_Data.data[19] = device_name[4]; Advertising_Data.data[20] = device_name[5]; Advertising_Data.data[21] = device_name[6]; Advertising_Data.data[22] = device_name[7]; Advertising_Data.data[23] = device_name[8]; Advertising_Data.data[24] = device_name[9]; Advertising_Data.data[25] = device_name[10]; Advertising_Data.data[26] = device_name[11]; Advertising_Data.data[27] = device_name[12]; Advertising_Data.data[28] = device_name[13]; Advertising_Data.data[29] = device_name[14]; Advertising_Data.data[30] = device_name[15]; result = BLE_SetAdvertisingData(&Advertising_Data); if (result != SYSTEMK_RESULT_SUCCESS) { KLOG_ERROR(KLOG_TAG, "Error updating hello packet!"); } } bool BLE_IsBLEPacketForMe(const uint8_t BD_ADDR[6]) { bool for_me = false; if (is_my_BD_ADDR_initialized == false) { if (BLE_GetMyAddress(my_BD_ADDR) == SYSTEMK_RESULT_SUCCESS) { is_my_BD_ADDR_initialized = true; } else { KLOG_ERROR(KLOG_TAG, "Couldn't get my BD_ADDR!"); } } // Is this my address? if ((is_my_BD_ADDR_initialized == true) && (BD_ADDR[0] == my_BD_ADDR[0]) && (BD_ADDR[1] == my_BD_ADDR[1]) && (BD_ADDR[2] == my_BD_ADDR[2]) && (BD_ADDR[3] == my_BD_ADDR[3]) && (BD_ADDR[4] == my_BD_ADDR[4]) && (BD_ADDR[5] == my_BD_ADDR[5])) { for_me = true; } // Is this the broadcast address? if ((BD_ADDR[0] == 0xFF) && (BD_ADDR[1] == 0xFF) && (BD_ADDR[2] == 0xFF) && (BD_ADDR[3] == 0xFF) && (BD_ADDR[4] == 0xFF) && (BD_ADDR[5] == 0xFF)) { for_me = true; } return for_me; }