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:
parent
7aa827563a
commit
430aec54b8
22 changed files with 368 additions and 47 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -32,6 +32,7 @@ idf_component_register(
|
|||
"NeoPixels/Animations/Tag_Received.c"
|
||||
"NeoPixels/Animations/Team_Colors.c"
|
||||
"NeoPixels/Animations/Test_Pattern.c"
|
||||
"NeoPixels/Animations/Wrapping_Up_Animation.c"
|
||||
"Protocols/Dubuque.c"
|
||||
"Protocols/Dynasty.c"
|
||||
"Protocols/Laser_X.c"
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#define ITEM_SIZE sizeof(KEvent_T)
|
||||
static StaticQueue_t StaticQueue;
|
||||
static uint8_t QueueStorageArea[QUEUE_LENGTH * ITEM_SIZE];
|
||||
QueueHandle_t xQueueEvents;
|
||||
static QueueHandle_t xQueueEvents;
|
||||
|
||||
static void Remap_Event(KEvent_T *event)
|
||||
{
|
||||
|
@ -61,7 +61,11 @@ void Init_KEvents(void)
|
|||
void Post_KEvent(KEvent_T *event)
|
||||
{
|
||||
Remap_Event(event);
|
||||
xQueueSend(xQueueEvents, event, 0);
|
||||
if (BLE_HandleCommonEvents(event) == SYSTEMK_RESULT_EVENT_NOT_HANDLED)
|
||||
{
|
||||
// If the event was not handled by the BLE subsystem, post it to the global event queue.
|
||||
xQueueSend(xQueueEvents, event, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Post_KEvent_From_ISR(KEvent_T *event, portBASE_TYPE *xHigherPriorityTaskWoken)
|
||||
|
|
BIN
KIsForQuality.png
Normal file
BIN
KIsForQuality.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 230 KiB |
119
NeoPixels/Animations/Wrapping_Up_Animation.c
Normal file
119
NeoPixels/Animations/Wrapping_Up_Animation.c
Normal file
|
@ -0,0 +1,119 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* Copyright © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint8_t Scene = 0;
|
||||
static uint8_t N_SCENES = 8;
|
||||
static uint16_t Time_in_Current_Scene_in_ms = 0;
|
||||
static color_t My_Color = COLOR_BLACK;
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
Time_in_Current_Scene_in_ms = 0;
|
||||
Scene = N_SCENES - 1;
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
My_Color = HW_NeoPixels_Get_My_Color();
|
||||
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
switch (Scene)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
Set_Barrel_Flash(Color(0x70, 0xFF, 0xFF, 0xFF));
|
||||
Set_Heart(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Square(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Circle(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Arrow(ApplyMask(My_Color, 0x70));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Set_Barrel_Flash(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Heart(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Square(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Circle(ApplyMask(My_Color, 0x70));
|
||||
Set_Arrow(Color(0x00, 0x00, 0x00, 0x00));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Set_Barrel_Flash(Color(0x70, 0xFF, 0xFF, 0xFF));
|
||||
Set_Heart(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Square(ApplyMask(My_Color, 0x70));
|
||||
Set_Circle(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Arrow(Color(0x00, 0x00, 0x00, 0x00));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
Set_Barrel_Flash(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Heart(ApplyMask(My_Color, 0x70));
|
||||
Set_Square(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Circle(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Arrow(Color(0x00, 0x00, 0x00, 0x00));
|
||||
break;
|
||||
}
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, BARREL_FLASH_PIXEL, ApplyMask(COLOR_WHITE, 0x70));
|
||||
for (uint_fast8_t pixel = 0; pixel < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL; pixel++)
|
||||
{
|
||||
if ((pixel % N_SCENES) == Scene)
|
||||
{
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, pixel, ApplyMask(My_Color, 0x70));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, pixel, ApplyMask(My_Color, 0x70));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, pixel, ApplyMask(My_Color, 0x70));
|
||||
}
|
||||
else
|
||||
{
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, pixel, COLOR_BLACK);
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, pixel, COLOR_BLACK);
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, pixel, COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, RECEIVER_INDICATOR_PIXEL, ApplyMask(COLOR_WHITE, 0x70));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
|
||||
Time_in_Current_Scene_in_ms += CONFIG_KTAG_ANIMATION_STEP_TIME_IN_ms;
|
||||
|
||||
if (Time_in_Current_Scene_in_ms > 25)
|
||||
{
|
||||
Time_in_Current_Scene_in_ms = 0;
|
||||
|
||||
if (Scene > 0)
|
||||
{
|
||||
Scene--;
|
||||
}
|
||||
else
|
||||
{
|
||||
Scene = N_SCENES - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T Wrapping_Up_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Wrapping_Up_Animation.h
Normal file
28
NeoPixels/Animations/Wrapping_Up_Animation.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* Copyright © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WRAPPING_UP_ANIMATION_H
|
||||
#define WRAPPING_UP_ANIMATION_H
|
||||
|
||||
extern Animation_T Wrapping_Up_Animation;
|
||||
|
||||
#endif // WRAPPING_UP_ANIMATION_H
|
|
@ -120,6 +120,10 @@ void NeoPixels_Task(void * pvParameters)
|
|||
case NEOPIXELS_BLE_NEARBY:
|
||||
New_Animation = &BLE_Nearby_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_WRAPPING_UP:
|
||||
New_Animation = &Wrapping_Up_Animation;
|
||||
break;
|
||||
}
|
||||
|
||||
if (New_Animation != NULL)
|
||||
|
|
|
@ -85,6 +85,7 @@
|
|||
#include "Animations/Tagged_Out.h"
|
||||
#include "Animations/Team_Colors.h"
|
||||
#include "Animations/Test_Pattern.h"
|
||||
#include "Animations/Wrapping_Up_Animation.h"
|
||||
|
||||
extern QueueHandle_t xQueueNeoPixels;
|
||||
extern TaskHandle_t NeoPixels_Task_Handle;
|
||||
|
@ -138,8 +139,9 @@ typedef enum
|
|||
NEOPIXELS_COUNTDOWN,
|
||||
//! For #NEOPIXELS_TEAM_COLORS, #NeoPixelsAction_T::Data is a #DisplayStyle_T.
|
||||
NEOPIXELS_TEAM_COLORS,
|
||||
//! For #NEOPIXELS_TEAM_COLORS, #NeoPixelsAction_T::Data is a #BLENearby_T.
|
||||
NEOPIXELS_BLE_NEARBY
|
||||
//! For #NEOPIXELS_BLE_NEARBY, #NeoPixelsAction_T::Data is a #BLENearby_T.
|
||||
NEOPIXELS_BLE_NEARBY,
|
||||
NEOPIXELS_WRAPPING_UP
|
||||
} NeoPixelsActionID_T;
|
||||
|
||||
typedef enum
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
# SystemK
|
||||
|
||||
...where the 'K' stands for quality.
|
||||
...where the 'K' stands for Quality.
|
||||
|
||||

|
||||
|
||||
*SystemK* is the name for the common software that is at the core of KTag devices.
|
||||
|
||||
This is that software.
|
|
@ -39,12 +39,15 @@ typedef enum
|
|||
SYSTEMK_RESULT_QUEUE_IS_FULL,
|
||||
SYSTEMK_RESULT_NOT_READY,
|
||||
SYSTEMK_RESULT_FAILED_TO_CREATE_RTOS_TASK,
|
||||
SYSTEMK_RESULT_FAILED_TO_CREATE_RTOS_TIMER,
|
||||
SYSTEMK_RESULT_FAILED_TO_START_RTOS_TIMER,
|
||||
SYSTEMK_RESULT_FILE_NOT_FOUND,
|
||||
SYSTEMK_RESULT_KEY_NOT_FOUND,
|
||||
SYSTEMK_RESULT_READ_FAILED,
|
||||
SYSTEMK_RESULT_WRITE_FAILED,
|
||||
SYSTEMK_RESULT_OVERFLOW,
|
||||
SYSTEMK_RESULT_UNDERFLOW
|
||||
SYSTEMK_RESULT_UNDERFLOW,
|
||||
SYSTEMK_RESULT_EVENT_NOT_HANDLED
|
||||
} SystemKResult_T;
|
||||
|
||||
#endif // RESULTS_H
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
static TimerHandle_t BLEStatusTimer = NULL;
|
||||
static StaticTimer_t xBLEStatusTimerBuffer;
|
||||
static TickType_t xBLEStatusTimerPeriod = 3000 / portTICK_PERIOD_MS;
|
||||
static TickType_t xBLEStatusTimerPeriod = 500 / portTICK_PERIOD_MS;
|
||||
|
||||
static TimerHandle_t GameDurationTimer = NULL;
|
||||
static StaticTimer_t xGameDurationTimerBuffer;
|
||||
|
|
|
@ -406,6 +406,7 @@ static void Playing__Interacting_Do(StateMachineContext_T * context)
|
|||
}
|
||||
break;
|
||||
|
||||
case KEVENT_PLAY_PRESSED_ON_REMOTE:
|
||||
case KEVENT_GAME_OVER:
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_GAME_OVER, .Play_To_Completion = true};
|
||||
|
@ -413,6 +414,21 @@ static void Playing__Interacting_Do(StateMachineContext_T * context)
|
|||
Transition_For_Event(context, STATE_WRAPPING_UP, &Event);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_MENU_UP:
|
||||
{
|
||||
#ifdef LOG_INTERACTING_SUBSTATE
|
||||
KLOG_INFO(KLOG_TAG, "Simulating a tag leading to being tagged out.");
|
||||
#endif // LOG_INTERACTING_SUBSTATE
|
||||
Reduce_Health(Get_Max_Health());
|
||||
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_TAG_RECEIVED, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_TAG_RECEIVED, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&ReceivedTagColor};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// All other events are ignored in this state.
|
||||
|
|
|
@ -185,6 +185,7 @@ static void Playing__Tagged_Out_Do(StateMachineContext_T * context)
|
|||
}
|
||||
break;
|
||||
|
||||
case KEVENT_PLAY_PRESSED_ON_REMOTE:
|
||||
case KEVENT_GAME_OVER:
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_GAME_OVER, .Play_To_Completion = true};
|
||||
|
@ -193,7 +194,7 @@ static void Playing__Tagged_Out_Do(StateMachineContext_T * context)
|
|||
}
|
||||
break;
|
||||
|
||||
case KEVENT_PLAY_PRESSED_ON_REMOTE:
|
||||
case KEVENT_MENU_DOWN:
|
||||
Set_Health(Get_Max_Health());
|
||||
Reset_Bombs();
|
||||
Transition_For_Event(context, STATE_PLAYING__INTERACTING, &Event);
|
||||
|
|
|
@ -50,6 +50,7 @@ static void Starting_Game__Counting_Down_Entry(StateMachineContext_T * context)
|
|||
Reset_Shots_Fired();
|
||||
Reset_Tags_Received();
|
||||
Reset_Times_Tagged_Out();
|
||||
Set_Health(Get_Max_Health());
|
||||
|
||||
uint8_t n_bombs;
|
||||
if (SETTINGS_get_uint8_t(SYSTEMK_SETTING_N_SPECIAL_WEAPONS_ON_REENTRY, &n_bombs) == SYSTEMK_RESULT_SUCCESS)
|
||||
|
|
|
@ -34,8 +34,7 @@ static TickType_t xBLEConfigurationResponseTimerPeriod = 3000 / portTICK_PERIOD_
|
|||
|
||||
static void BLEConfigurationResponseTimerCallback(TimerHandle_t xTimer)
|
||||
{
|
||||
// Don't send HELLO packets once configuration has started.
|
||||
BLE_StopAdvertising();
|
||||
BLE_UpdateHelloPacket();
|
||||
}
|
||||
|
||||
#define MAX_MENU_DEPTH 10
|
||||
|
@ -107,13 +106,6 @@ static void Configuring_Do(StateMachineContext_T *context)
|
|||
{
|
||||
portBASE_TYPE xStatus;
|
||||
static KEvent_T Event;
|
||||
|
||||
// For the first hundred milliseconds, keep updating the Hello packet, since on some platforms (PSoC6), one call is not enough.
|
||||
// TODO: Fix the Hello packet hack on PSoC6.
|
||||
if ((xTaskGetTickCount() - context->Time_At_State_Entry_In_Ticks) < (100 / portTICK_PERIOD_MS))
|
||||
{
|
||||
BLE_UpdateHelloPacket();
|
||||
}
|
||||
|
||||
xStatus = Receive_KEvent(&Event);
|
||||
|
||||
|
@ -238,7 +230,12 @@ static void Configuring_Do(StateMachineContext_T *context)
|
|||
break;
|
||||
|
||||
case KEVENT_BLE_PACKET_RECEIVED:
|
||||
if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_PARAMETERS)
|
||||
if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_INSTIGATE_GAME)
|
||||
{
|
||||
Transition_For_Event(context, STATE_STARTING_GAME__RESPONDING, &Event);
|
||||
// Don't free the packet buffer here; it will be freed in Starting_Game__Responding_Entry().
|
||||
}
|
||||
else if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_PARAMETERS)
|
||||
{
|
||||
HandleBLEConfigurationPacket((BLE_ParametersPacket_T *)Event.Data);
|
||||
}
|
||||
|
@ -408,6 +405,12 @@ void HandleBLEConfigurationPacket(const BLE_ParametersPacket_T *const packet)
|
|||
BLE_RespondToConfigurationPacket(packet, BLE_ERROR_CHANGING_PARAMETERS);
|
||||
}
|
||||
|
||||
// A parameters request addressed to this device or broadcast will end a BLE Quiet.
|
||||
if (BLE_Unquiet() != SYSTEMK_RESULT_SUCCESS)
|
||||
{
|
||||
KLOG_ERROR(KLOG_TAG, "Couldn't Unquiet to respond to a Parameters request!");
|
||||
}
|
||||
|
||||
if (xTimerStart(BLEConfigurationResponseTimer, 0) != pdPASS)
|
||||
{
|
||||
KLOG_ERROR(KLOG_TAG, "Couldn't start the BLEConfigurationResponseTimer!");
|
||||
|
@ -415,11 +418,6 @@ void HandleBLEConfigurationPacket(const BLE_ParametersPacket_T *const packet)
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Once configuration has begun, stop advertising the HELLO packet to free up bandwidth.
|
||||
BLE_StopAdvertising();
|
||||
}
|
||||
|
||||
BLE_FreePacketBuffer((BLE_GenericPacketType_T *)packet);
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ static void Initializing_Do(StateMachineContext_T * context)
|
|||
{
|
||||
switch (Event.ID)
|
||||
{
|
||||
case KEVENT_TRIGGER_SWITCH_PRESSED:
|
||||
case KEVENT_ACCESSORY_SWITCH_PRESSED:
|
||||
Transition_For_Event(context, STATE_REPROGRAMMING, &Event);
|
||||
break;
|
||||
|
||||
|
|
|
@ -54,8 +54,6 @@ static void Ready_Entry(StateMachineContext_T * context)
|
|||
}
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_GAME_ON, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
Set_Health(Get_Max_Health());
|
||||
}
|
||||
|
||||
//! Executes the Ready state.
|
||||
|
@ -91,6 +89,7 @@ static void Ready_Do(StateMachineContext_T * context)
|
|||
if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_INSTIGATE_GAME)
|
||||
{
|
||||
Transition_For_Event(context, STATE_STARTING_GAME__RESPONDING, &Event);
|
||||
// Don't free the packet buffer here; it will be freed in Starting_Game__Responding_Entry().
|
||||
}
|
||||
else if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_EVENT)
|
||||
{
|
||||
|
|
|
@ -54,7 +54,7 @@ const StateActivity_T STATE_WRAPPING_UP_Activities =
|
|||
static void Wrapping_Up_Entry(StateMachineContext_T * context)
|
||||
{
|
||||
LOG("Entering the Wrapping Up state.");
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_IDLE, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_WRAPPING_UP, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)0x00};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
BLE_UpdateStatusPacket(STATE_WRAPPING_UP);
|
||||
if (BLE_ScanAndAdvertise() != SYSTEMK_RESULT_SUCCESS)
|
||||
|
@ -109,7 +109,12 @@ static void Wrapping_Up_Do(StateMachineContext_T * context)
|
|||
break;
|
||||
|
||||
case KEVENT_BLE_PACKET_RECEIVED:
|
||||
if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_EVENT)
|
||||
if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_INSTIGATE_GAME)
|
||||
{
|
||||
Transition_For_Event(context, STATE_STARTING_GAME__RESPONDING, &Event);
|
||||
// Don't free the packet buffer here; it will be freed in Starting_Game__Responding_Entry().
|
||||
}
|
||||
else if (((BLE_Packet_T *)Event.Data)->Generic.type == BLE_PACKET_TYPE_EVENT)
|
||||
{
|
||||
HandleBLEEventPacket((BLE_EventPacket_T *)Event.Data, context);
|
||||
}
|
||||
|
@ -118,6 +123,10 @@ static void Wrapping_Up_Do(StateMachineContext_T * context)
|
|||
BLE_FreePacketBuffer(Event.Data);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEVENT_PLAY_PRESSED_ON_REMOTE:
|
||||
Transition_For_Event(context, STATE_CONFIGURING, &Event);
|
||||
break;
|
||||
|
||||
default:
|
||||
// All other events are ignored in this state.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue