Initial public release of SystemK.
This commit is contained in:
parent
387f57cdda
commit
6f51f5b006
129 changed files with 11654 additions and 2 deletions
68
Audio/Audio_HW_Interface.h
Normal file
68
Audio/Audio_HW_Interface.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_HW_INTERFACE_H
|
||||
#define AUDIO_HW_INTERFACE_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_SET_VOLUME,
|
||||
AUDIO_SILENCE,
|
||||
AUDIO_PLAY_STARTUP_SOUND,
|
||||
AUDIO_PLAY_SHOT_FIRED,
|
||||
AUDIO_PLAY_TAG_RECEIVED,
|
||||
AUDIO_PLAY_TAGGED_OUT,
|
||||
AUDIO_PLAY_MISFIRE,
|
||||
AUDIO_PRONOUNCE_NUMBER_0_TO_100,
|
||||
AUDIO_PLAY_MENU_PROMPT,
|
||||
AUDIO_PLAY_SELECTION_INDICATOR,
|
||||
AUDIO_PLAY_HEALTH_REMAINING,
|
||||
AUDIO_PLAY_ELECTRONIC_DANCE_MUSIC,
|
||||
AUDIO_PLAY_GENERIC_ERROR,
|
||||
AUDIO_PLAY_VOLUME_PROMPT,
|
||||
AUDIO_PLAY_RIGHT_HANDED,
|
||||
AUDIO_PLAY_LEFT_HANDED,
|
||||
AUDIO_PLAY_GAME_ON,
|
||||
AUDIO_PLAY_HARDWARE_SETTINGS_PROMPT,
|
||||
AUDIO_PLAY_GAME_SETTINGS_PROMPT,
|
||||
AUDIO_PLAY_BONK,
|
||||
AUDIO_PLAY_NEAR_MISS,
|
||||
AUDIO_PLAY_PLAYER_ID_PROMPT,
|
||||
AUDIO_PLAY_TEAM_ID_PROMPT,
|
||||
AUDIO_PLAY_FRIENDLY_FIRE,
|
||||
AUDIO_PLAY_STARTING_THEME,
|
||||
AUDIO_PLAY_BOOP,
|
||||
AUDIO_PLAY_BEEP,
|
||||
AUDIO_PLAY_REPROGRAMMING,
|
||||
AUDIO_PLAY_BOMB,
|
||||
AUDIO_PLAY_GAME_OVER
|
||||
} AudioActionID_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
AudioActionID_T ID;
|
||||
bool Play_To_Completion;
|
||||
void * Data;
|
||||
} AudioAction_T;
|
||||
|
||||
SystemKResult_T Perform_Audio_Action(AudioAction_T * action);
|
||||
|
||||
#endif // AUDIO_HW_INTERFACE_H
|
29
BLE/BLE_HW_Interface.h
Normal file
29
BLE/BLE_HW_Interface.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLE_HW_INTERFACE_H
|
||||
#define BLE_HW_INTERFACE_H
|
||||
|
||||
SystemKResult_T BLE_GetMyAddress(uint8_t * BD_ADDR);
|
||||
SystemKResult_T BLE_ScanAndAdvertise(void);
|
||||
SystemKResult_T BLE_SetAdvertisingData(BLE_AdvertisingData_T * data);
|
||||
|
||||
#endif // BLE_HW_INTERFACE_H
|
98
BLE/BLE_Packet_Tracker.c
Normal file
98
BLE/BLE_Packet_Tracker.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "SystemK.h"
|
||||
#include "BLE_Packets.h"
|
||||
#include "BLE_Packet_Tracker.h"
|
||||
|
||||
#define MAX_REMEMBERED_PACKETS 10
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t sender_BD_ADDR[BD_ADDR_SIZE];
|
||||
BLE_PacketType_T packet_type;
|
||||
uint8_t event_number;
|
||||
} TrackedPacket_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
TrackedPacket_T packets[MAX_REMEMBERED_PACKETS];
|
||||
// Number of unique packets we've seen so far. This value saturates at MAX_REMEMBERED_PACKETS.
|
||||
uint8_t count;
|
||||
// Index of the oldest packet.
|
||||
uint8_t head;
|
||||
} PacketTracker_T;
|
||||
|
||||
static PacketTracker_T Tracker =
|
||||
{
|
||||
.count = 0,
|
||||
.head = 0,
|
||||
};
|
||||
|
||||
// A packet is _new_ if the combination of BLE adress, type, and event number have not been seen recently.
|
||||
bool BLE_IsPacketNew(const uint8_t *sender_BD_ADDR,
|
||||
BLE_PacketType_T packet_type,
|
||||
uint8_t event_number)
|
||||
{
|
||||
// Check if the packet already exists in the tracker.
|
||||
for (int i = 0; i < Tracker.count; i++)
|
||||
{
|
||||
int index = (Tracker.head + i) % MAX_REMEMBERED_PACKETS;
|
||||
if ((memcmp(Tracker.packets[index].sender_BD_ADDR, sender_BD_ADDR, BD_ADDR_SIZE) == 0) &&
|
||||
(Tracker.packets[index].packet_type == packet_type) &&
|
||||
(Tracker.packets[index].event_number == event_number))
|
||||
{
|
||||
// We've already seen this packet.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The packet is new--find the location to store it.
|
||||
int new_index;
|
||||
if (Tracker.count < MAX_REMEMBERED_PACKETS)
|
||||
{
|
||||
// There's space, so just add the new packet at the end.
|
||||
new_index = (Tracker.head + Tracker.count) % MAX_REMEMBERED_PACKETS;
|
||||
Tracker.count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No space, so overwrite the oldest packet.
|
||||
new_index = Tracker.head;
|
||||
Tracker.head = (Tracker.head + 1) % MAX_REMEMBERED_PACKETS;
|
||||
}
|
||||
|
||||
// Add the new packet to the tracker.
|
||||
memcpy(Tracker.packets[new_index].sender_BD_ADDR, sender_BD_ADDR, BD_ADDR_SIZE);
|
||||
Tracker.packets[new_index].packet_type = packet_type;
|
||||
Tracker.packets[new_index].event_number = event_number;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize the MessageTracker
|
||||
void BLE_ClearPacketTracker(void)
|
||||
{
|
||||
Tracker.count = 0;
|
||||
Tracker.head = 0;
|
||||
}
|
42
BLE/BLE_Packet_Tracker.h
Normal file
42
BLE/BLE_Packet_Tracker.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief This file defines a tracker for Bluetooth Low Energy advertising packets used by KTag.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BLE_PACKET_TRACKER_H
|
||||
#define BLE_PACKET_TRACKER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool BLE_IsPacketNew(const uint8_t* sender_BD_ADDR, BLE_PacketType_T packet_type, uint8_t event_number);
|
||||
void BLE_ClearPacketTracker(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // BLE_PACKET_TRACKER_H
|
301
BLE/BLE_Packets.c
Normal file
301
BLE/BLE_Packets.c
Normal file
|
@ -0,0 +1,301 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#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()
|
||||
{
|
||||
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] = 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_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!");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
191
BLE/BLE_Packets.h
Normal file
191
BLE/BLE_Packets.h
Normal file
|
@ -0,0 +1,191 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief This file defines the Bluetooth Low Energy advertising packets used by KTag.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BLE_PACKETS_H
|
||||
#define BLE_PACKETS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Preprocessor and Type Definitions */
|
||||
#include <stdint.h>
|
||||
|
||||
#define BLE_MAX_ADVERTISING_BYTES 31
|
||||
#define BLE_MAX_SCAN_RESPONSE_BYTES 31
|
||||
|
||||
#define BD_ADDR_SIZE 6
|
||||
#define BLE_KTAG_PACKET_TOTAL_SIZE 31
|
||||
#define BLE_KTAG_PACKET_DATA_SIZE 21
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t data[BLE_MAX_ADVERTISING_BYTES];
|
||||
uint8_t length;
|
||||
} BLE_AdvertisingData_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t data[BLE_MAX_SCAN_RESPONSE_BYTES];
|
||||
uint8_t length;
|
||||
} BLE_ScanResponseData_T;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BLE_PACKET_TYPE_BUFFER_FREE = 0,
|
||||
BLE_PACKET_TYPE_INSTIGATE_GAME = 1,
|
||||
BLE_FIRST_VALID_PACKET_TYPE = BLE_PACKET_TYPE_INSTIGATE_GAME,
|
||||
BLE_PACKET_TYPE_JOIN_NOW = 2,
|
||||
BLE_PACKET_TYPE_TAG = 3,
|
||||
BLE_PACKET_TYPE_CONSOLE = 4,
|
||||
BLE_PACKET_TYPE_STATUS = 5,
|
||||
BLE_LAST_VALID_PACKET_TYPE = BLE_PACKET_TYPE_STATUS,
|
||||
BLE_PACKET_TYPE_UNKNOWN
|
||||
} BLE_PacketType_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BLE_PacketType_T type;
|
||||
uint8_t BD_ADDR[BD_ADDR_SIZE];
|
||||
int8_t RSSI;
|
||||
uint8_t event_number;
|
||||
|
||||
uint8_t data[BLE_KTAG_PACKET_DATA_SIZE];
|
||||
} __attribute__((packed, aligned(1))) BLE_GenericPacketType_T;
|
||||
|
||||
//! Contents of the BLE packet #BLE_PACKET_TYPE_INSTIGATE_GAME.
|
||||
typedef struct
|
||||
{
|
||||
BLE_PacketType_T type;
|
||||
uint8_t BD_ADDR[BD_ADDR_SIZE];
|
||||
int8_t RSSI;
|
||||
uint8_t event_number;
|
||||
|
||||
uint32_t game_length_in_ms;
|
||||
uint32_t time_remaining_until_countdown_in_ms;
|
||||
uint8_t random_time_after_countdown_in_ms_x100;
|
||||
uint8_t unused[12];
|
||||
} __attribute__((packed, aligned(1))) BLE_InstigationPacket_T;
|
||||
|
||||
//! Contents of the BLE packet #BLE_PACKET_TYPE_JOIN_NOW.
|
||||
typedef struct
|
||||
{
|
||||
BLE_PacketType_T type;
|
||||
uint8_t BD_ADDR[BD_ADDR_SIZE];
|
||||
int8_t RSSI;
|
||||
uint8_t event_number;
|
||||
|
||||
uint32_t game_length_in_ms;
|
||||
uint32_t time_remaining_in_game_in_ms;
|
||||
uint8_t unused[13];
|
||||
} __attribute__((packed, aligned(1))) BLE_JoinNowPacket_T;
|
||||
|
||||
//! Contents of the BLE packet #BLE_PACKET_TYPE_TAG.
|
||||
typedef struct
|
||||
{
|
||||
BLE_PacketType_T type;
|
||||
uint8_t BD_ADDR[BD_ADDR_SIZE];
|
||||
int8_t RSSI;
|
||||
uint8_t event_number;
|
||||
|
||||
uint8_t tx_power_level;
|
||||
uint8_t protocol;
|
||||
uint8_t team_ID;
|
||||
uint8_t player_ID;
|
||||
int16_t damage;
|
||||
color_t color;
|
||||
uint8_t target_BD_ADDR[BD_ADDR_SIZE];
|
||||
uint8_t unused[5];
|
||||
} __attribute__((packed, aligned(1)))BLE_TagPacket_T;
|
||||
|
||||
//! Contents of the BLE packet #BLE_PACKET_TYPE_CONSOLE.
|
||||
typedef struct
|
||||
{
|
||||
BLE_PacketType_T type;
|
||||
uint8_t BD_ADDR[BD_ADDR_SIZE];
|
||||
int8_t RSSI;
|
||||
uint8_t event_number;
|
||||
|
||||
uint8_t console_data[BLE_KTAG_PACKET_DATA_SIZE];
|
||||
} __attribute__((packed, aligned(1)))BLE_ConsolePacket_T;
|
||||
|
||||
//! Contents of the BLE packet #BLE_PACKET_TYPE_STATUS.
|
||||
typedef struct
|
||||
{
|
||||
BLE_PacketType_T type;
|
||||
uint8_t BD_ADDR[BD_ADDR_SIZE];
|
||||
int8_t RSSI;
|
||||
uint8_t event_number;
|
||||
|
||||
int8_t tx_power_level;
|
||||
uint8_t protocol;
|
||||
uint8_t team_ID;
|
||||
uint8_t player_ID;
|
||||
uint16_t health;
|
||||
uint16_t maximum_health;
|
||||
color_t primary_color;
|
||||
color_t secondary_color;
|
||||
uint8_t unused[5];
|
||||
} __attribute__((packed, aligned(1)))BLE_StatusPacket_T;
|
||||
|
||||
typedef union
|
||||
{
|
||||
BLE_GenericPacketType_T Generic;
|
||||
BLE_InstigationPacket_T Instigation;
|
||||
BLE_JoinNowPacket_T JoinNow;
|
||||
BLE_TagPacket_T Tag;
|
||||
BLE_ConsolePacket_T Console;
|
||||
BLE_StatusPacket_T Status;
|
||||
} BLE_Packet_T;
|
||||
|
||||
/* Include Files */
|
||||
|
||||
/* Public Variables */
|
||||
|
||||
/* Public Functions */
|
||||
|
||||
inline void BLE_FreePacketBuffer(void * buffer)
|
||||
{
|
||||
if (buffer != NULL)
|
||||
{
|
||||
((BLE_GenericPacketType_T *)buffer)->type = BLE_PACKET_TYPE_BUFFER_FREE;
|
||||
}
|
||||
}
|
||||
|
||||
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_UpdateStatusPacket();
|
||||
void BLE_UpdateTagPacket(int16_t damage, color_t color, uint8_t target_BD_ADDR[BD_ADDR_SIZE]);
|
||||
bool BLE_IsBLEPacketForMe(const uint8_t BD_ADDR[BD_ADDR_SIZE]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // BLE_PACKETS_H
|
72
CMakeLists.txt
Executable file
72
CMakeLists.txt
Executable file
|
@ -0,0 +1,72 @@
|
|||
idf_component_register(
|
||||
SRCS
|
||||
"BLE/BLE_Packets.c"
|
||||
"BLE/BLE_Packet_Tracker.c"
|
||||
"Events/KEvents.c"
|
||||
"Game/Game.c"
|
||||
"Game/Weapons.c"
|
||||
"Logging/KLog.c"
|
||||
"Menu/GameSettings/GameMenuItem.c"
|
||||
"Menu/GameSettings/PlayerIDMenuItem.c"
|
||||
"Menu/GameSettings/TeamIDMenuItem.c"
|
||||
"Menu/HardwareSettings/HandedMenuItem.c"
|
||||
"Menu/HardwareSettings/HardwareMenuItem.c"
|
||||
"Menu/HardwareSettings/VolumeMenuItem.c"
|
||||
"Menu/Menu.c"
|
||||
"NeoPixels/Gamma.c"
|
||||
"NeoPixels/NeoPixels.c"
|
||||
"NeoPixels/Sine.c"
|
||||
"NeoPixels/Animations/All_Off.c"
|
||||
"NeoPixels/Animations/All_On.c"
|
||||
"NeoPixels/Animations/BLE_Nearby.c"
|
||||
"NeoPixels/Animations/BLE_RSSI.c"
|
||||
"NeoPixels/Animations/Countdown.c"
|
||||
"NeoPixels/Animations/Flamethrower.c"
|
||||
"NeoPixels/Animations/Flashlight.c"
|
||||
"NeoPixels/Animations/Health_Report.c"
|
||||
"NeoPixels/Animations/Idle_Animation.c"
|
||||
"NeoPixels/Animations/Menu_Animation.c"
|
||||
"NeoPixels/Animations/Shot_Fired.c"
|
||||
"NeoPixels/Animations/Tagged_Out.c"
|
||||
"NeoPixels/Animations/Tag_Received.c"
|
||||
"NeoPixels/Animations/Team_Colors.c"
|
||||
"NeoPixels/Animations/Test_Pattern.c"
|
||||
"Protocols/Dubuque.c"
|
||||
"Protocols/Dynasty.c"
|
||||
"Protocols/Laser_X.c"
|
||||
"Protocols/Miles_Tag_II.c"
|
||||
"Protocols/NEC.c"
|
||||
"Protocols/Nerf_Laser_Ops_Pro.c"
|
||||
"Protocols/Nerf_Laser_Strike.c"
|
||||
"Protocols/Nerf_Phoenix_LTX.c"
|
||||
"Protocols/Protocols.c"
|
||||
"Protocols/Squad_Hero.c"
|
||||
"Protocols/Test.c"
|
||||
"States/Playing/State_Playing.c"
|
||||
"States/Playing/State_Playing__Interacting.c"
|
||||
"States/Playing/State_Playing__Tagged_Out.c"
|
||||
"States/Starting_Game/State_Starting_Game.c"
|
||||
"States/Starting_Game/State_Starting_Game__Counting_Down.c"
|
||||
"States/Starting_Game/State_Starting_Game__Instigating.c"
|
||||
"States/Starting_Game/State_Starting_Game__Responding.c"
|
||||
"States/State_Configuring.c"
|
||||
"States/State_Initializing.c"
|
||||
"States/State_Machine.c"
|
||||
"States/State_Ready.c"
|
||||
"States/State_Reprogramming.c"
|
||||
"States/State_Wrapping_Up.c"
|
||||
"SystemK.c"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
"Audio"
|
||||
"BLE"
|
||||
"Events"
|
||||
"Game"
|
||||
"IR"
|
||||
"Logging"
|
||||
"Menu"
|
||||
"NeoPixels"
|
||||
"Protocols"
|
||||
"Settings"
|
||||
"States"
|
||||
)
|
110
Colors.h
Executable file
110
Colors.h
Executable file
|
@ -0,0 +1,110 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief This file defines colors used by the KTag system (SystemK).
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef COLORS_H
|
||||
#define COLORS_H
|
||||
|
||||
//! Represents a color with 8 bits per color, plus an 8-bit brightness mask, as MMRRGGBB.
|
||||
typedef uint32_t color_t;
|
||||
|
||||
#define COLOR_BLACK ((color_t)0xFF000000)
|
||||
#define COLOR_WHITE ((color_t)0xFFFFFFFF)
|
||||
#define COLOR_RED ((color_t)0xFFFF0000)
|
||||
#define COLOR_ORANGE ((color_t)0xFFFF7F00)
|
||||
#define COLOR_YELLOW ((color_t)0xFFFFFF00)
|
||||
#define COLOR_GREEN ((color_t)0xFF00FF00)
|
||||
#define COLOR_CYAN ((color_t)0xFF00FFFF)
|
||||
#define COLOR_BLUE ((color_t)0xFF0000FF)
|
||||
#define COLOR_MAGENTA ((color_t)0xFFFF0033)
|
||||
#define COLOR_PURPLE ((color_t)0xFFFF00FF)
|
||||
#define COLOR_PINK ((color_t)0xFFFF3377)
|
||||
#define COLOR_AQUA ((color_t)0xFF557DFF)
|
||||
|
||||
inline color_t GetColorFromTeamID(uint8_t team_ID)
|
||||
{
|
||||
color_t result = COLOR_BLACK;
|
||||
|
||||
switch (team_ID)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
result = COLOR_PURPLE;
|
||||
break;
|
||||
case 1:
|
||||
result = COLOR_RED;
|
||||
break;
|
||||
case 2:
|
||||
result = COLOR_GREEN;
|
||||
break;
|
||||
case 3:
|
||||
result = COLOR_WHITE;
|
||||
break;
|
||||
case 4:
|
||||
result = COLOR_BLUE;
|
||||
break;
|
||||
case 5:
|
||||
result = COLOR_ORANGE;
|
||||
break;
|
||||
case 6:
|
||||
result = COLOR_YELLOW;
|
||||
break;
|
||||
case 7:
|
||||
result = COLOR_BLACK;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline color_t Color(uint8_t mask, uint8_t red, uint8_t green, uint8_t blue)
|
||||
{
|
||||
return (((color_t)mask << 24) | ((color_t)red << 16) | ((color_t)green << 8) | ((color_t)blue));
|
||||
}
|
||||
|
||||
inline color_t ApplyMask(color_t color, uint8_t mask)
|
||||
{
|
||||
return (((color_t)mask << 24) | (color & 0x00FFFFFF));
|
||||
}
|
||||
|
||||
inline uint8_t Red(color_t color)
|
||||
{
|
||||
return ((color >> 24) & ((color >> 16) & 0xFF));
|
||||
}
|
||||
|
||||
inline uint8_t Green(color_t color)
|
||||
{
|
||||
return ((color >> 24) & ((color >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
inline uint8_t Blue(color_t color)
|
||||
{
|
||||
return ((color >> 24) & (color & 0xFF));
|
||||
}
|
||||
|
||||
#endif // COLORS_H
|
28
Console_HW_Interface.h
Normal file
28
Console_HW_Interface.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CONSOLE_HW_INTERFACE_H
|
||||
#define CONSOLE_HW_INTERFACE_H
|
||||
|
||||
SystemKResult_T HW_Execute_Console_Command(const uint8_t * const command);
|
||||
|
||||
#endif // CONSOLE_HW_INTERFACE_H
|
35
Developer Certificate of Origin.txt
Normal file
35
Developer Certificate of Origin.txt
Normal file
|
@ -0,0 +1,35 @@
|
|||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
|
148
Events/Command_Mapping.h
Executable file
148
Events/Command_Mapping.h
Executable file
|
@ -0,0 +1,148 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief This file maps IR remote control codes to events used by the KTag system (SystemK).
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMMAND_MAPPING_H
|
||||
#define COMMAND_MAPPING_H
|
||||
|
||||
// First Roku Remote Replacement
|
||||
// https://www.amazon.com/gp/product/B01G7OX9WS/
|
||||
#define NEC_COMMAND_ROKU_01_BACK_ARROW 2573648618
|
||||
#define NEC_COMMAND_ROKU_01_HOME 4228104938
|
||||
#define NEC_COMMAND_ROKU_01_UP_ARROW 3860447978
|
||||
#define NEC_COMMAND_ROKU_01_RIGHT_ARROW 3526214378
|
||||
#define NEC_COMMAND_ROKU_01_DOWN_ARROW 3425944298
|
||||
#define NEC_COMMAND_ROKU_01_LEFT_ARROW 3776889578
|
||||
#define NEC_COMMAND_ROKU_01_OK 3576349418
|
||||
#define NEC_COMMAND_ROKU_01_COUNTERCLOCKWISE 3576349418
|
||||
#define NEC_COMMAND_ROKU_01_ASTERIX 2657207018
|
||||
#define NEC_COMMAND_ROKU_01_REWIND 3409232618
|
||||
#define NEC_COMMAND_ROKU_01_PLAY_PAUSE 3008152298
|
||||
#define NEC_COMMAND_ROKU_01_FAST_FORWARD 2857747178
|
||||
#define NEC_COMMAND_ROKU_01_CHANNEL_CRACKLE 2155856618
|
||||
#define NEC_COMMAND_ROKU_01_CHANNEL_PANDORA 2907882218
|
||||
#define NEC_COMMAND_ROKU_01_CHANNEL_MGO 4027564778
|
||||
#define NEC_COMMAND_ROKU_01_CHANNEL_AMAZON 4027564778
|
||||
#define NEC_COMMAND_ROKU_01_CHANNEL_NETFLIX 3024863978
|
||||
#define NEC_COMMAND_ROKU_01_CHANNEL_BLOCKBUSTER 3743466218
|
||||
|
||||
// Second Roku Remote Replacement
|
||||
// https://www.amazon.com/dp/B09Z6Q2MLC/
|
||||
#define NEC_COMMAND_ROKU_02_BACK_ARROW 2573649898
|
||||
#define NEC_COMMAND_ROKU_02_HOME 4228106218
|
||||
#define NEC_COMMAND_ROKU_02_UP_ARROW 3860449258
|
||||
#define NEC_COMMAND_ROKU_02_RIGHT_ARROW 3526215658
|
||||
#define NEC_COMMAND_ROKU_02_DOWN_ARROW 3425945578
|
||||
#define NEC_COMMAND_ROKU_02_LEFT_ARROW 3776890858
|
||||
#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_REWIND 3409233898
|
||||
#define NEC_COMMAND_ROKU_02_PLAY_PAUSE 3008153578
|
||||
#define NEC_COMMAND_ROKU_02_FAST_FORWARD 2857748458
|
||||
#define NEC_COMMAND_ROKU_02_CHANNEL_NETFLIX 2907883498
|
||||
#define NEC_COMMAND_ROKU_02_CHANNEL_HULU 2991441898
|
||||
#define NEC_COMMAND_ROKU_02_CHANNEL_YOUTUBE 2841036778
|
||||
#define NEC_COMMAND_ROKU_02_CHANNEL_DISNEYPLUS 4077701098
|
||||
|
||||
// Samsung BN59-01301A TV Remote Control
|
||||
// https://samsungparts.com/products/bn59-01301a
|
||||
#define NEC_COMMAND_SAMSUNG_TV_POWER 4244768519
|
||||
#define NEC_COMMAND_SAMSUNG_TV_SOURCE 4261480199
|
||||
#define NEC_COMMAND_SAMSUNG_TV_SLEEP 4228056839
|
||||
#define NEC_COMMAND_SAMSUNG_TV_HOME 2256078599
|
||||
#define NEC_COMMAND_SAMSUNG_TV_GUIDE 2957969159
|
||||
#define NEC_COMMAND_SAMSUNG_TV_SETTINGS 3843688199
|
||||
#define NEC_COMMAND_SAMSUNG_TV_INFO 3760129799
|
||||
#define NEC_COMMAND_SAMSUNG_TV_UP_ARROW 2673870599
|
||||
#define NEC_COMMAND_SAMSUNG_TV_RIGHT_ARROW 2640447239
|
||||
#define NEC_COMMAND_SAMSUNG_TV_DOWN_ARROW 2657158919
|
||||
#define NEC_COMMAND_SAMSUNG_TV_LEFT_ARROW 2590312199
|
||||
#define NEC_COMMAND_SAMSUNG_TV_CENTER_SELECT 2540177159
|
||||
#define NEC_COMMAND_SAMSUNG_TV_RETURN 2807564039
|
||||
#define NEC_COMMAND_SAMSUNG_TV_EXIT 3526166279
|
||||
|
||||
__attribute__((always_inline)) inline void Remap_NEC_Command(KEvent_T *event, uint32_t NEC_data)
|
||||
{
|
||||
switch (NEC_data)
|
||||
{
|
||||
case NEC_COMMAND_ROKU_01_HOME:
|
||||
case NEC_COMMAND_ROKU_02_HOME:
|
||||
case NEC_COMMAND_SAMSUNG_TV_HOME:
|
||||
case NEC_COMMAND_SAMSUNG_TV_SETTINGS:
|
||||
event->ID = KEVENT_MENU_ENTER;
|
||||
break;
|
||||
|
||||
case NEC_COMMAND_ROKU_01_BACK_ARROW:
|
||||
case NEC_COMMAND_ROKU_02_BACK_ARROW:
|
||||
case NEC_COMMAND_SAMSUNG_TV_EXIT:
|
||||
event->ID = KEVENT_MENU_EXIT;
|
||||
break;
|
||||
|
||||
case NEC_COMMAND_ROKU_01_OK:
|
||||
case NEC_COMMAND_ROKU_02_OK:
|
||||
case NEC_COMMAND_SAMSUNG_TV_CENTER_SELECT:
|
||||
event->ID = KEVENT_MENU_SELECT;
|
||||
break;
|
||||
|
||||
case NEC_COMMAND_ROKU_01_UP_ARROW:
|
||||
case NEC_COMMAND_ROKU_02_UP_ARROW:
|
||||
case NEC_COMMAND_SAMSUNG_TV_UP_ARROW:
|
||||
event->ID = KEVENT_MENU_UP;
|
||||
break;
|
||||
|
||||
case NEC_COMMAND_ROKU_01_DOWN_ARROW:
|
||||
case NEC_COMMAND_ROKU_02_DOWN_ARROW:
|
||||
case NEC_COMMAND_SAMSUNG_TV_DOWN_ARROW:
|
||||
event->ID = KEVENT_MENU_DOWN;
|
||||
break;
|
||||
|
||||
case NEC_COMMAND_ROKU_01_LEFT_ARROW:
|
||||
case NEC_COMMAND_ROKU_02_LEFT_ARROW:
|
||||
case NEC_COMMAND_ROKU_01_RIGHT_ARROW:
|
||||
case NEC_COMMAND_ROKU_02_RIGHT_ARROW:
|
||||
case NEC_COMMAND_SAMSUNG_TV_LEFT_ARROW:
|
||||
case NEC_COMMAND_SAMSUNG_TV_RIGHT_ARROW:
|
||||
event->ID = KEVENT_MENU_BACK;
|
||||
break;
|
||||
|
||||
case NEC_COMMAND_ROKU_01_ASTERIX:
|
||||
case NEC_COMMAND_ROKU_02_ASTERIX:
|
||||
case NEC_COMMAND_SAMSUNG_TV_SOURCE:
|
||||
event->ID = KEVENT_REPROGRAM;
|
||||
break;
|
||||
|
||||
case NEC_COMMAND_ROKU_01_PLAY_PAUSE:
|
||||
case NEC_COMMAND_ROKU_02_PLAY_PAUSE:
|
||||
event->ID = KEVENT_PLAY_PRESSED_ON_REMOTE;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // COMMAND_MAPPING_H
|
76
Events/KEvents.c
Executable file
76
Events/KEvents.c
Executable file
|
@ -0,0 +1,76 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
#include "Command_Mapping.h"
|
||||
|
||||
#define QUEUE_LENGTH 10
|
||||
#define ITEM_SIZE sizeof(KEvent_T)
|
||||
static StaticQueue_t StaticQueue;
|
||||
static uint8_t QueueStorageArea[QUEUE_LENGTH * ITEM_SIZE];
|
||||
QueueHandle_t xQueueEvents;
|
||||
|
||||
static void Remap_Event(KEvent_T *event)
|
||||
{
|
||||
switch (event->ID)
|
||||
{
|
||||
case KEVENT_COMMAND_RECEIVED:
|
||||
{
|
||||
if (((DecodedPacket_T *)(event->Data))->Command.protocol == NEC_PROTOCOL)
|
||||
{
|
||||
uint32_t NEC_data = ((DecodedPacket_T *)(event->Data))->Command.data;
|
||||
Remap_NEC_Command(event, NEC_data);
|
||||
}
|
||||
FreeDecodedPacketBuffer(event->Data);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// No remapping necessary.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Init_KEvents(void)
|
||||
{
|
||||
xQueueEvents = xQueueCreateStatic(QUEUE_LENGTH, ITEM_SIZE, QueueStorageArea, &StaticQueue);
|
||||
}
|
||||
|
||||
void Post_KEvent(KEvent_T *event)
|
||||
{
|
||||
Remap_Event(event);
|
||||
xQueueSend(xQueueEvents, event, 0);
|
||||
}
|
||||
|
||||
void Post_KEvent_From_ISR(KEvent_T *event, portBASE_TYPE *xHigherPriorityTaskWoken)
|
||||
{
|
||||
Remap_Event(event);
|
||||
xQueueSendFromISR(xQueueEvents, event, xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
portBASE_TYPE Receive_KEvent(KEvent_T *event)
|
||||
{
|
||||
return xQueueReceive(xQueueEvents, event, 0);
|
||||
}
|
104
Events/KEvents.h
Executable file
104
Events/KEvents.h
Executable file
|
@ -0,0 +1,104 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief This file defines the events used by the KTag system (SystemK).
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef EVENTS_H
|
||||
#define EVENTS_H
|
||||
|
||||
//! Event identifiers used by the SystemK.
|
||||
typedef enum
|
||||
{
|
||||
KEVENT_NO_EVENT = 0,
|
||||
KEVENT_AUTOMATIC_TRANSITION = 1,
|
||||
KEVENT_CAPSENSE_ONE_PRESSED = 2,
|
||||
KEVENT_CAPSENSE_ONE_RELEASED = 3,
|
||||
KEVENT_CAPSENSE_TWO_PRESSED = 4,
|
||||
KEVENT_CAPSENSE_TWO_RELEASED = 5,
|
||||
KEVENT_TRIGGER_SWITCH_PRESSED = 6,
|
||||
KEVENT_CENTER_SWITCH_PRESSED = KEVENT_TRIGGER_SWITCH_PRESSED,
|
||||
KEVENT_TRIGGER_SWITCH_RELEASED = 7,
|
||||
//! For #KEVENT_CENTER_SWITCH_RELEASED, #KEvent_T::Data contains the duration of the press in milliseconds.
|
||||
KEVENT_CENTER_SWITCH_RELEASED = KEVENT_TRIGGER_SWITCH_RELEASED,
|
||||
KEVENT_UP_SWITCH_PRESSED = 8,
|
||||
KEVENT_UP_SWITCH_LONG_PRESSED = 9,
|
||||
KEVENT_UP_SWITCH_RELEASED = 10,
|
||||
KEVENT_DOWN_SWITCH_PRESSED = 11,
|
||||
KEVENT_DOWN_SWITCH_LONG_PRESSED = 12,
|
||||
KEVENT_DOWN_SWITCH_RELEASED = 13,
|
||||
KEVENT_FORWARD_SWITCH_PRESSED = 14,
|
||||
KEVENT_FORWARD_SWITCH_LONG_PRESSED = 15,
|
||||
KEVENT_FORWARD_SWITCH_RELEASED = 16,
|
||||
KEVENT_BACKWARD_SWITCH_PRESSED = 17,
|
||||
KEVENT_BACKWARD_SWITCH_LONG_PRESSED = 18,
|
||||
KEVENT_BACKWARD_SWITCH_RELEASED = 19,
|
||||
KEVENT_TAG_SENT = 20,
|
||||
//! For #KEVENT_TAG_RECEIVED, #KEvent_T::Data points to #TagPacket_T.
|
||||
KEVENT_TAG_RECEIVED = 21,
|
||||
KEVENT_TAGGED_OUT = 22,
|
||||
KEVENT_MISFIRE = 23,
|
||||
KEVENT_NEAR_MISS = 24,
|
||||
//! For #KEVENT_BLE_PACKET_RECEIVED, #KEvent_T::Data points to #COMM_BLE_Packet_T.
|
||||
KEVENT_BLE_PACKET_RECEIVED = 25,
|
||||
//! For #KEVENT_COMMAND_RECEIVED, #KEvent_T::Data points to #CommandPacket_T.
|
||||
KEVENT_COMMAND_RECEIVED = 26,
|
||||
KEVENT_MENU_ENTER = 27,
|
||||
KEVENT_MENU_SELECT = 28,
|
||||
KEVENT_MENU_BACK = 29,
|
||||
KEVENT_MENU_UP = 30,
|
||||
KEVENT_MENU_DOWN = 31,
|
||||
KEVENT_MENU_EXIT = 32,
|
||||
KEVENT_REPROGRAM = 33,
|
||||
//! For #KEVENT_ACCESSORY_SWITCH_PRESSED, #KEvent_T::Data contains the duration the switch has not been pressed in milliseconds.
|
||||
KEVENT_ACCESSORY_SWITCH_PRESSED = 34,
|
||||
//! For #KEVENT_ACCESSORY_SWITCH_PRESSED, #KEvent_T::Data contains the duration of the press in milliseconds.
|
||||
KEVENT_ACCESSORY_SWITCH_RELEASED = 35,
|
||||
//! For #KEVENT_AUDIO_COMPLETED, #KEvent_T::Data contains the AudioActionID_T of the completed action.
|
||||
KEVENT_AUDIO_COMPLETED = 36,
|
||||
KEVENT_GAME_OVER = 37,
|
||||
KEVENT_PLAY_PRESSED_ON_REMOTE = 38,
|
||||
// KEVENT_IS_OUT_OF_RANGE is one more than the last valid event.
|
||||
KEVENT_IS_OUT_OF_RANGE
|
||||
} KEvent_ID_T;
|
||||
|
||||
//! Events used by the SystemK.
|
||||
/*!
|
||||
* The #Data is event-dependent.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
KEvent_ID_T ID;
|
||||
void *Data;
|
||||
} KEvent_T;
|
||||
|
||||
// Verify the pointer size, since sometime we store uint32_t's in the KEvent_T::Data pointer.
|
||||
_Static_assert(sizeof(uintptr_t) >= sizeof(uint32_t), "Pointer size is less than 32 bits! KEvents will not work properly on this system.");
|
||||
|
||||
void Init_KEvents(void);
|
||||
void Post_KEvent(KEvent_T *event);
|
||||
void Post_KEvent_From_ISR(KEvent_T *event, portBASE_TYPE *xHigherPriorityTaskWoken);
|
||||
portBASE_TYPE Receive_KEvent(KEvent_T *event);
|
||||
|
||||
#endif // EVENTS_H
|
31
Game/Game.c
Normal file
31
Game/Game.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
KTag_Game_Data_T KTAG_Game_Data =
|
||||
{
|
||||
.My_Health = MAX_HEALTH,
|
||||
.My_Weapon = TEST_PATTERN_ID,
|
||||
.My_Shield_Strength = 100,
|
||||
.Time_Remaining_in_Game_in_ms = UINT32_MAX,
|
||||
.Time_Remaining_Until_Countdown_in_ms = CONFIG_KTAG_T_DEFAULT_START_GAME_in_ms
|
||||
};
|
238
Game/Game.h
Normal file
238
Game/Game.h
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
#include "Weapons.h"
|
||||
|
||||
#define MAX_HEALTH 10
|
||||
|
||||
//! Implemented according to the 2024-07-20 "KTag Teams Compatibility Matrix".
|
||||
typedef enum
|
||||
{
|
||||
TEAM_PURPLE = 0,
|
||||
TEAM_RED = 1,
|
||||
TEAM_BLUE = 2,
|
||||
TEAM_WHITE = 3,
|
||||
COMMON_TEAM_ID_MASK = 3,
|
||||
TEAM_ORANGE = 4,
|
||||
TEAM_GREEN = 5,
|
||||
TEAM_YELLOW = 6,
|
||||
TEAM_BLACK = 7,
|
||||
EXTENDED_TEAM_ID_MASK = 7
|
||||
} TeamID_t;
|
||||
|
||||
__attribute__((always_inline)) inline TeamID_t Resolve_Common_Team_ID(uint8_t team_ID)
|
||||
{
|
||||
return (team_ID & COMMON_TEAM_ID_MASK);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline bool Team_Can_Tag_Me(uint8_t tagging_team_ID)
|
||||
{
|
||||
bool can_tag = false;
|
||||
uint8_t team_ID;
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_TEAMID, &team_ID);
|
||||
TeamID_t my_common_team_ID = Resolve_Common_Team_ID(team_ID);
|
||||
|
||||
if (tagging_team_ID == TEAM_PURPLE)
|
||||
{
|
||||
can_tag = true;
|
||||
}
|
||||
else if (tagging_team_ID != my_common_team_ID)
|
||||
{
|
||||
if ((tagging_team_ID == TEAM_RED) || (tagging_team_ID == TEAM_BLUE))
|
||||
{
|
||||
can_tag = true;
|
||||
}
|
||||
}
|
||||
|
||||
return can_tag;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t My_Health;
|
||||
WeaponID_t My_Weapon;
|
||||
uint8_t My_Shield_Strength;
|
||||
uint32_t Time_Remaining_in_Game_in_ms;
|
||||
uint32_t Time_Remaining_Until_Countdown_in_ms;
|
||||
uint16_t Shots_Fired;
|
||||
uint16_t Tags_Received;
|
||||
uint16_t Times_Tagged_Out;
|
||||
uint8_t Bombs_Remaining;
|
||||
} KTag_Game_Data_T;
|
||||
|
||||
extern KTag_Game_Data_T KTAG_Game_Data;
|
||||
|
||||
__attribute__((always_inline)) inline uint8_t Get_Health()
|
||||
{
|
||||
return KTAG_Game_Data.My_Health;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Set_Health(uint8_t health)
|
||||
{
|
||||
KTAG_Game_Data.My_Health = health;
|
||||
|
||||
if (KTAG_Game_Data.My_Health == 0)
|
||||
{
|
||||
KEvent_T tagged_out_event = { .ID = KEVENT_TAGGED_OUT, .Data = (void *)0x00 };
|
||||
Post_KEvent(&tagged_out_event);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Reduce_Health(uint8_t reduction)
|
||||
{
|
||||
if (reduction < KTAG_Game_Data.My_Health)
|
||||
{
|
||||
KTAG_Game_Data.My_Health -= reduction;
|
||||
}
|
||||
else
|
||||
{
|
||||
KTAG_Game_Data.My_Health = 0;
|
||||
KEvent_T tagged_out_event = { .ID = KEVENT_TAGGED_OUT, .Data = (void *)0x00 };
|
||||
Post_KEvent(&tagged_out_event);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline WeaponID_t Get_Weapon()
|
||||
{
|
||||
return KTAG_Game_Data.My_Weapon;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Set_Weapon(WeaponID_t weapon)
|
||||
{
|
||||
KTAG_Game_Data.My_Weapon = weapon;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline uint16_t Get_Shield_Strength()
|
||||
{
|
||||
return KTAG_Game_Data.My_Shield_Strength;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Set_Shield_Strength(uint16_t strength)
|
||||
{
|
||||
KTAG_Game_Data.My_Shield_Strength = strength;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Reset_Shots_Fired()
|
||||
{
|
||||
KTAG_Game_Data.Shots_Fired = 0;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Increment_Shots_Fired()
|
||||
{
|
||||
if (KTAG_Game_Data.Shots_Fired < UINT16_MAX)
|
||||
{
|
||||
KTAG_Game_Data.Shots_Fired++;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Reset_Tags_Received()
|
||||
{
|
||||
KTAG_Game_Data.Tags_Received = 0;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Increment_Tags_Received()
|
||||
{
|
||||
if (KTAG_Game_Data.Tags_Received < UINT16_MAX)
|
||||
{
|
||||
KTAG_Game_Data.Tags_Received++;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Reset_Times_Tagged_Out()
|
||||
{
|
||||
KTAG_Game_Data.Times_Tagged_Out = 0;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Increment_Times_Tagged_Out()
|
||||
{
|
||||
if (KTAG_Game_Data.Times_Tagged_Out < UINT16_MAX)
|
||||
{
|
||||
KTAG_Game_Data.Times_Tagged_Out++;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline bool Still_Playing()
|
||||
{
|
||||
return (KTAG_Game_Data.My_Health > 0);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline bool Back_In()
|
||||
{
|
||||
return Still_Playing();
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Set_Time_Remaining_in_Game(uint32_t time_in_ms)
|
||||
{
|
||||
KTAG_Game_Data.Time_Remaining_in_Game_in_ms = time_in_ms;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline uint32_t Get_Time_Remaining_in_Game_in_ms()
|
||||
{
|
||||
return KTAG_Game_Data.Time_Remaining_in_Game_in_ms;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Set_Time_Remaining_Until_Countdown(uint32_t time_in_ms)
|
||||
{
|
||||
KTAG_Game_Data.Time_Remaining_Until_Countdown_in_ms = time_in_ms;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline uint32_t Get_Time_Remaining_Until_Countdown_in_ms()
|
||||
{
|
||||
return KTAG_Game_Data.Time_Remaining_Until_Countdown_in_ms;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline uint16_t Get_Shots_Fired()
|
||||
{
|
||||
return KTAG_Game_Data.Shots_Fired;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline uint16_t Get_Tags_Received()
|
||||
{
|
||||
return KTAG_Game_Data.Tags_Received;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline uint16_t Get_Times_Tagged_Out()
|
||||
{
|
||||
return KTAG_Game_Data.Times_Tagged_Out;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void Set_Available_Bombs(uint8_t n_bombs)
|
||||
{
|
||||
KTAG_Game_Data.Bombs_Remaining = n_bombs;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline bool Use_Bomb_If_Available()
|
||||
{
|
||||
bool available = false;
|
||||
|
||||
if (KTAG_Game_Data.Bombs_Remaining > 0)
|
||||
{
|
||||
KTAG_Game_Data.Bombs_Remaining--;
|
||||
available = true;
|
||||
}
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
#endif // GAME_H
|
171
Game/Weapons.c
Normal file
171
Game/Weapons.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
//! All the weapons, in the same order as #WeaponID_t.
|
||||
Weapon_t WeaponsByID[NUMBER_OF_WEAPONS] =
|
||||
{
|
||||
// Test Pattern
|
||||
{
|
||||
.ID = TEST_PATTERN_ID,
|
||||
.Type = ENERGY,
|
||||
.Protocol = TEST_PROTOCOL,
|
||||
.Damage_Per_Shot = 1,
|
||||
.Shots_Per_Reload = UINT16_MAX,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 1000,
|
||||
.Time_To_Reload_in_ms = 0
|
||||
},
|
||||
|
||||
// Bayonette
|
||||
{
|
||||
.ID = BAYONETTE_ID,
|
||||
.Type = EDGE,
|
||||
.Protocol = MILES_TAG_II_PROTOCOL, // For now...
|
||||
.Damage_Per_Shot = 1,
|
||||
.Shots_Per_Reload = UINT16_MAX,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 1000,
|
||||
.Time_To_Reload_in_ms = 0
|
||||
},
|
||||
|
||||
// Pistol
|
||||
{
|
||||
.ID = PISTOL_ID,
|
||||
.Type = PROJECTILE,
|
||||
.Protocol = MILES_TAG_II_PROTOCOL, // For now...
|
||||
.Damage_Per_Shot = 10,
|
||||
.Shots_Per_Reload = 8,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 300,
|
||||
.Time_To_Reload_in_ms = 3000
|
||||
},
|
||||
|
||||
// Shotgun
|
||||
{
|
||||
.ID = SHOTGUN_ID,
|
||||
.Type = PROJECTILE,
|
||||
.Protocol = MILES_TAG_II_PROTOCOL, // For now...
|
||||
.Damage_Per_Shot = 20,
|
||||
.Shots_Per_Reload = 2,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 500,
|
||||
.Time_To_Reload_in_ms = 10000
|
||||
},
|
||||
|
||||
// Rifle
|
||||
{
|
||||
.ID = RIFLE_ID,
|
||||
.Type = PROJECTILE,
|
||||
.Protocol = MILES_TAG_II_PROTOCOL, // For now...
|
||||
.Damage_Per_Shot = 10,
|
||||
.Shots_Per_Reload = 8,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 300,
|
||||
.Time_To_Reload_in_ms = 5000
|
||||
},
|
||||
|
||||
// Machine Gun
|
||||
{
|
||||
.ID = MACHINE_GUN_ID,
|
||||
.Type = PROJECTILE,
|
||||
.Protocol = MILES_TAG_II_PROTOCOL, // For now...
|
||||
.Damage_Per_Shot = 10,
|
||||
.Shots_Per_Reload = 250,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 0,
|
||||
.Time_To_Reload_in_ms = 2*60*1000
|
||||
},
|
||||
|
||||
// Nerf Laser Ops Pro
|
||||
{
|
||||
.ID = NERF_LASER_OPS_ID,
|
||||
.Type = ENERGY,
|
||||
.Protocol = NERF_LASER_OPS_PRO_PROTOCOL,
|
||||
.Damage_Per_Shot = 10,
|
||||
.Shots_Per_Reload = 10,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 300,
|
||||
.Time_To_Reload_in_ms = 3000
|
||||
},
|
||||
|
||||
// Nerf Laser Strike Blaster
|
||||
{
|
||||
.ID = NERF_LASER_STRIKE_BLASTER_ID,
|
||||
.Type = ENERGY,
|
||||
.Protocol = NERF_LASER_STRIKE_PROTOCOL,
|
||||
.Damage_Per_Shot = 10,
|
||||
.Shots_Per_Reload = 12,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 300,
|
||||
.Time_To_Reload_in_ms = 3000
|
||||
},
|
||||
|
||||
// Nerf Phoenix LTX
|
||||
{
|
||||
.ID = NERF_PHOENIX_LTX_ID,
|
||||
.Type = ENERGY,
|
||||
.Protocol = NERF_PHOENIX_LTX_PROTOCOL,
|
||||
.Damage_Per_Shot = 10,
|
||||
.Shots_Per_Reload = 10,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 300,
|
||||
.Time_To_Reload_in_ms = 3000
|
||||
},
|
||||
|
||||
// Squad Hero Pistol
|
||||
{
|
||||
.ID = SQUAD_HERO_PISTOL,
|
||||
.Type = ENERGY,
|
||||
.Protocol = SQUAD_HERO_PROTOCOL,
|
||||
.Damage_Per_Shot = 1,
|
||||
.Shots_Per_Reload = 10,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 300,
|
||||
.Time_To_Reload_in_ms = 3000
|
||||
},
|
||||
|
||||
// Laser X
|
||||
{
|
||||
.ID = LASER_X_ID,
|
||||
.Type = ENERGY,
|
||||
.Protocol = LASER_X_PROTOCOL,
|
||||
.Damage_Per_Shot = 1,
|
||||
.Shots_Per_Reload = 10,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 300,
|
||||
.Time_To_Reload_in_ms = 3000
|
||||
},
|
||||
|
||||
// The Dubuque Protocol
|
||||
{
|
||||
.ID = LASER_X_ID,
|
||||
.Type = ENERGY,
|
||||
.Protocol = DUBUQUE_PROTOCOL,
|
||||
.Damage_Per_Shot = 1,
|
||||
.Shots_Per_Reload = 10,
|
||||
.Delay_Before_Sending_Shot_in_ms = 0,
|
||||
.Time_Between_Shots_in_ms = 300,
|
||||
.Time_To_Reload_in_ms = 3000
|
||||
}
|
||||
};
|
||||
|
82
Game/Weapons.h
Normal file
82
Game/Weapons.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WEAPON_H
|
||||
#define WEAPON_H
|
||||
|
||||
//! These have assigned values because they are stored in #NVM_WEAPON_ID.
|
||||
typedef enum
|
||||
{
|
||||
TEST_PATTERN_ID = 0,
|
||||
BAYONETTE_ID = 1,
|
||||
PISTOL_ID = 2,
|
||||
SHOTGUN_ID = 3,
|
||||
RIFLE_ID = 4,
|
||||
MACHINE_GUN_ID = 5,
|
||||
NERF_LASER_OPS_ID = 6,
|
||||
NERF_LASER_STRIKE_BLASTER_ID = 7,
|
||||
NERF_PHOENIX_LTX_ID = 8,
|
||||
SQUAD_HERO_PISTOL = 9,
|
||||
LASER_X_ID = 10,
|
||||
DUBUQUE_PROTOCOL_ID = 11,
|
||||
// Leave this last!
|
||||
NUMBER_OF_WEAPONS
|
||||
} WeaponID_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
EDGE,
|
||||
PROJECTILE,
|
||||
EXPLOSION,
|
||||
ENERGY
|
||||
} WeaponType_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WeaponID_t ID;
|
||||
WeaponType_t Type;
|
||||
Protocol_T Protocol;
|
||||
uint16_t Damage_Per_Shot;
|
||||
uint16_t Shots_Per_Reload;
|
||||
|
||||
uint32_t Delay_Before_Sending_Shot_in_ms;
|
||||
//! This is the inverse of "Rate of Fire".
|
||||
uint32_t Time_Between_Shots_in_ms;
|
||||
uint32_t Time_To_Reload_in_ms;
|
||||
|
||||
} Weapon_t;
|
||||
|
||||
extern Weapon_t WeaponsByID[NUMBER_OF_WEAPONS];
|
||||
|
||||
__attribute__((always_inline)) inline Weapon_t GetWeaponFromID(WeaponID_t id)
|
||||
{
|
||||
Weapon_t result = WeaponsByID[TEST_PATTERN_ID];
|
||||
|
||||
if (id < NUMBER_OF_WEAPONS)
|
||||
{
|
||||
result = WeaponsByID[id];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // WEAPON_H
|
||||
|
28
IR/IR_HW_Interface.h
Normal file
28
IR/IR_HW_Interface.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IR_HW_INTERFACE_H
|
||||
#define IR_HW_INTERFACE_H
|
||||
|
||||
SystemKResult_T Prepare_Tag(void);
|
||||
SystemKResult_T Send_Tag(void);
|
||||
|
||||
#endif // IR_HW_INTERFACE_H
|
38
Kconfig
Normal file
38
Kconfig
Normal file
|
@ -0,0 +1,38 @@
|
|||
menu "KTag SystemK"
|
||||
|
||||
config KTAG_N_NEOPIXEL_CHANNELS
|
||||
int "Number of NeoPixel channels"
|
||||
range 1 4
|
||||
default 4
|
||||
help
|
||||
Only values of '1' or '4' are supported.
|
||||
|
||||
config KTAG_MAX_NEOPIXELS_PER_CHANNEL
|
||||
int "Maximum Neopixels per channel"
|
||||
default 8
|
||||
help
|
||||
Maximum number of NeoPixels on a single channel.
|
||||
|
||||
config KTAG_ANIMATION_STEP_TIME_IN_ms
|
||||
int "Time between animation updates (in milliseconds)"
|
||||
default 10
|
||||
|
||||
config KTAG_T_DEFAULT_START_GAME_in_ms
|
||||
int "Default time between game initiation and game start (in milliseconds)"
|
||||
default 30000
|
||||
|
||||
config KTAG_MAX_AUDIO_VOLUME
|
||||
int "Maximum audio volume"
|
||||
range 1 100
|
||||
default 100
|
||||
help
|
||||
Value of audio volume representing the maximum volume possible for this device.
|
||||
|
||||
config KTAG_MIN_AUDIO_VOLUME
|
||||
int "Minimum audio volume"
|
||||
range 0 99
|
||||
default 0
|
||||
help
|
||||
Value of audio volume representing the minimum volume possible for this device.
|
||||
|
||||
endmenu
|
7
LICENSE
7
LICENSE
|
@ -219,8 +219,11 @@ If you develop a new program, and you want it to be of the greatest possible use
|
|||
|
||||
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
SystemK
|
||||
Copyright (C) 2025 Software
|
||||
This program source code file is part of SystemK, a library in the KTag project.
|
||||
|
||||
🛡️ <https://ktag.clubk.club> 🃞
|
||||
|
||||
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.
|
||||
|
||||
|
|
29
Logging/KLog.c
Executable file
29
Logging/KLog.c
Executable file
|
@ -0,0 +1,29 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#ifdef PSOC_PLATFORM
|
||||
char8 KLog_Buffer[MAX_KLOG_STRING_LENGTH];
|
||||
#endif // PSOC_PLATFORM
|
95
Logging/KLog.h
Executable file
95
Logging/KLog.h
Executable file
|
@ -0,0 +1,95 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#define KLOG_ERROR(tag, format, ...) ESP_LOGE(tag, format, ##__VA_ARGS__)
|
||||
#define KLOG_WARN(tag, format, ...) ESP_LOGW(tag, format, ##__VA_ARGS__)
|
||||
#define KLOG_INFO(tag, format, ...) ESP_LOGI(tag, format, ##__VA_ARGS__)
|
||||
#define KLOG_DEBUG(tag, format, ...) ESP_LOGD(tag, format, ##__VA_ARGS__)
|
||||
#define KLOG_TRACE(tag, format, ...) ESP_LOGV(tag, format, ##__VA_ARGS__)
|
||||
|
||||
#endif // ESP_PLATFORM
|
||||
|
||||
#ifdef PSOC_PLATFORM
|
||||
|
||||
/* Include PSoC system and component APIs and defines */
|
||||
#include <project.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAX_KLOG_STRING_LENGTH (255)
|
||||
extern char8 KLog_Buffer[MAX_KLOG_STRING_LENGTH];
|
||||
|
||||
#define KLOG_ERROR(tag, format, ...) \
|
||||
do \
|
||||
{ \
|
||||
UART_Console_PutString("ERROR ["); \
|
||||
UART_Console_PutString(tag); \
|
||||
UART_Console_PutString("] "); \
|
||||
sprintf((char8 *)KLog_Buffer, format, ##__VA_ARGS__); \
|
||||
UART_Console_PutString((const char8 *)KLog_Buffer); \
|
||||
UART_Console_PutString("\n"); \
|
||||
} while (false)
|
||||
#define KLOG_WARN(tag, format, ...) \
|
||||
do \
|
||||
{ \
|
||||
UART_Console_PutString("WARN ["); \
|
||||
UART_Console_PutString(tag); \
|
||||
UART_Console_PutString("] "); \
|
||||
sprintf((char8 *)KLog_Buffer, format, ##__VA_ARGS__); \
|
||||
UART_Console_PutString((const char8 *)KLog_Buffer); \
|
||||
UART_Console_PutString("\n"); \
|
||||
} while (false)
|
||||
#define KLOG_INFO(tag, format, ...) \
|
||||
do \
|
||||
{ \
|
||||
UART_Console_PutString("INFO ["); \
|
||||
UART_Console_PutString(tag); \
|
||||
UART_Console_PutString("] "); \
|
||||
sprintf((char8 *)KLog_Buffer, format, ##__VA_ARGS__); \
|
||||
UART_Console_PutString((const char8 *)KLog_Buffer); \
|
||||
UART_Console_PutString("\n"); \
|
||||
} while (false)
|
||||
#define KLOG_DEBUG(tag, format, ...) \
|
||||
do \
|
||||
{ \
|
||||
UART_Console_PutString("DEBUG ["); \
|
||||
UART_Console_PutString(tag); \
|
||||
UART_Console_PutString("] "); \
|
||||
sprintf((char8 *)KLog_Buffer, format, ##__VA_ARGS__); \
|
||||
UART_Console_PutString((const char8 *)KLog_Buffer); \
|
||||
UART_Console_PutString("\n"); \
|
||||
} while (false)
|
||||
#define KLOG_TRACE(tag, format, ...) \
|
||||
do \
|
||||
{ \
|
||||
UART_Console_PutString("TRACE ["); \
|
||||
UART_Console_PutString(tag); \
|
||||
UART_Console_PutString("] "); \
|
||||
sprintf((char8 *)KLog_Buffer, format, ##__VA_ARGS__); \
|
||||
UART_Console_PutString((const char8 *)KLog_Buffer); \
|
||||
UART_Console_PutString("\n"); \
|
||||
} while (false)
|
||||
|
||||
#endif // PSOC_PLATFORM
|
104
Menu/GameSettings/GameMenuItem.c
Normal file
104
Menu/GameSettings/GameMenuItem.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint8_t SubmenuIndex = 0;
|
||||
static MenuItem_T const * const Submenus[] =
|
||||
{
|
||||
&TeamIDMenuItem,
|
||||
&PlayerIDMenuItem
|
||||
};
|
||||
static const uint8_t N_SUBMENUS = (sizeof(Submenus) / sizeof(MenuItem_T *));
|
||||
|
||||
static const char * KLOG_TAG = "Menu | Game Settings";
|
||||
|
||||
static void OnFocus(bool IncludeDetails);
|
||||
static MenuItem_T const * OnSelect();
|
||||
static void OnIncrement();
|
||||
static void OnDecrement();
|
||||
|
||||
const MenuItem_T GameMenuItem =
|
||||
{
|
||||
.OnFocus = OnFocus,
|
||||
.OnSelect = OnSelect,
|
||||
.OnIncrement = OnIncrement,
|
||||
.OnDecrement = OnDecrement
|
||||
|
||||
};
|
||||
|
||||
static void OnFocus(bool IncludeDetails)
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_GAME_SETTINGS_PROMPT, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
KLOG_INFO(KLOG_TAG, "<--");
|
||||
|
||||
if (IncludeDetails == true)
|
||||
{
|
||||
if (Submenus[SubmenuIndex]->OnFocus != NULL)
|
||||
{
|
||||
Submenus[SubmenuIndex]->OnFocus(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static MenuItem_T const * OnSelect()
|
||||
{
|
||||
return Submenus[SubmenuIndex];
|
||||
}
|
||||
|
||||
static void OnIncrement()
|
||||
{
|
||||
if (SubmenuIndex < (N_SUBMENUS -1))
|
||||
{
|
||||
SubmenuIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wrap around.
|
||||
SubmenuIndex = 0;
|
||||
}
|
||||
|
||||
if (Submenus[SubmenuIndex]->OnFocus != NULL)
|
||||
{
|
||||
Submenus[SubmenuIndex]->OnFocus(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnDecrement()
|
||||
{
|
||||
if (SubmenuIndex > 0)
|
||||
{
|
||||
SubmenuIndex--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wrap around.
|
||||
SubmenuIndex = (N_SUBMENUS -1);
|
||||
}
|
||||
|
||||
if (Submenus[SubmenuIndex]->OnFocus != NULL)
|
||||
{
|
||||
Submenus[SubmenuIndex]->OnFocus(false);
|
||||
}
|
||||
}
|
27
Menu/GameSettings/GameMenuItem.h
Normal file
27
Menu/GameSettings/GameMenuItem.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GAMEMENUITEM_H
|
||||
#define GAMEMENUITEM_H
|
||||
|
||||
extern const MenuItem_T GameMenuItem;
|
||||
|
||||
#endif // GAMEMENUITEM_H
|
104
Menu/GameSettings/PlayerIDMenuItem.c
Normal file
104
Menu/GameSettings/PlayerIDMenuItem.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define MIN_PLAYER_ID 0b0000000
|
||||
#define MAX_PLAYER_ID 0b1111111
|
||||
|
||||
static uint8_t Player_ID;
|
||||
|
||||
static void OnFocus(bool IncludeDetails);
|
||||
static MenuItem_T const * OnSelect();
|
||||
static void OnIncrement();
|
||||
static void OnDecrement();
|
||||
|
||||
const MenuItem_T PlayerIDMenuItem =
|
||||
{
|
||||
.OnFocus = OnFocus,
|
||||
.OnSelect = OnSelect,
|
||||
.OnIncrement = OnIncrement,
|
||||
.OnDecrement = OnDecrement
|
||||
|
||||
};
|
||||
|
||||
static void OnFocus(bool IncludeDetails)
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_PLAYER_ID_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
if (IncludeDetails == true)
|
||||
{
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_PLAYERID, &Player_ID);
|
||||
AudioAction_T volume_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Player_ID};
|
||||
Perform_Audio_Action(&volume_action);
|
||||
}
|
||||
}
|
||||
|
||||
static MenuItem_T const * OnSelect()
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_SELECTION_INDICATOR, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void OnIncrement()
|
||||
{
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_PLAYERID, &Player_ID);
|
||||
|
||||
if (Player_ID < MAX_PLAYER_ID)
|
||||
{
|
||||
Player_ID++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Player_ID = MAX_PLAYER_ID;
|
||||
}
|
||||
(void) SETTINGS_set_uint8_t(SYSTEMK_SETTING_PLAYERID, Player_ID);
|
||||
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_PLAYER_ID_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
AudioAction_T volume_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Player_ID};
|
||||
Perform_Audio_Action(&volume_action);
|
||||
}
|
||||
|
||||
static void OnDecrement()
|
||||
{
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_PLAYERID, &Player_ID);
|
||||
|
||||
if (Player_ID > MIN_PLAYER_ID)
|
||||
{
|
||||
Player_ID--;
|
||||
}
|
||||
else
|
||||
{
|
||||
Player_ID = MIN_PLAYER_ID;
|
||||
}
|
||||
(void) SETTINGS_set_uint8_t(SYSTEMK_SETTING_PLAYERID, Player_ID);
|
||||
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_PLAYER_ID_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
AudioAction_T volume_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Player_ID};
|
||||
Perform_Audio_Action(&volume_action);
|
||||
}
|
27
Menu/GameSettings/PlayerIDMenuItem.h
Normal file
27
Menu/GameSettings/PlayerIDMenuItem.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PLAYERIDMENUITEM_H
|
||||
#define PLAYERIDMENUITEM_H
|
||||
|
||||
extern const MenuItem_T PlayerIDMenuItem;
|
||||
|
||||
#endif // PLAYERIDMENUITEM_H
|
104
Menu/GameSettings/TeamIDMenuItem.c
Normal file
104
Menu/GameSettings/TeamIDMenuItem.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define MIN_TEAM_ID 0
|
||||
#define MAX_TEAM_ID 3
|
||||
|
||||
static uint8_t Team_ID;
|
||||
|
||||
static void OnFocus(bool IncludeDetails);
|
||||
static MenuItem_T const * OnSelect();
|
||||
static void OnIncrement();
|
||||
static void OnDecrement();
|
||||
|
||||
const MenuItem_T TeamIDMenuItem =
|
||||
{
|
||||
.OnFocus = OnFocus,
|
||||
.OnSelect = OnSelect,
|
||||
.OnIncrement = OnIncrement,
|
||||
.OnDecrement = OnDecrement
|
||||
|
||||
};
|
||||
|
||||
static void OnFocus(bool IncludeDetails)
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_TEAM_ID_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
if (IncludeDetails == true)
|
||||
{
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_TEAMID, &Team_ID);
|
||||
AudioAction_T volume_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Team_ID};
|
||||
Perform_Audio_Action(&volume_action);
|
||||
}
|
||||
}
|
||||
|
||||
static MenuItem_T const * OnSelect()
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_SELECTION_INDICATOR, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void OnIncrement()
|
||||
{
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_TEAMID, &Team_ID);
|
||||
|
||||
if (Team_ID < MAX_TEAM_ID)
|
||||
{
|
||||
Team_ID++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Team_ID = MAX_TEAM_ID;
|
||||
}
|
||||
(void) SETTINGS_set_uint8_t(SYSTEMK_SETTING_TEAMID, Team_ID);
|
||||
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_TEAM_ID_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
AudioAction_T volume_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Team_ID};
|
||||
Perform_Audio_Action(&volume_action);
|
||||
}
|
||||
|
||||
static void OnDecrement()
|
||||
{
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_TEAMID, &Team_ID);
|
||||
|
||||
if (Team_ID > MIN_TEAM_ID)
|
||||
{
|
||||
Team_ID--;
|
||||
}
|
||||
else
|
||||
{
|
||||
Team_ID = MIN_TEAM_ID;
|
||||
}
|
||||
(void) SETTINGS_set_uint8_t(SYSTEMK_SETTING_TEAMID, Team_ID);
|
||||
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_TEAM_ID_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
AudioAction_T volume_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Team_ID};
|
||||
Perform_Audio_Action(&volume_action);
|
||||
}
|
27
Menu/GameSettings/TeamIDMenuItem.h
Normal file
27
Menu/GameSettings/TeamIDMenuItem.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TEAMIDMENUITEM_H
|
||||
#define TEAMIDMENUITEM_H
|
||||
|
||||
extern const MenuItem_T TeamIDMenuItem;
|
||||
|
||||
#endif // TEAMIDMENUITEM_H
|
93
Menu/HardwareSettings/HandedMenuItem.c
Normal file
93
Menu/HardwareSettings/HandedMenuItem.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint8_t Is_Right_Handed;
|
||||
|
||||
static void OnFocus(bool IncludeDetails);
|
||||
static MenuItem_T const * OnSelect();
|
||||
static void OnIncrement();
|
||||
static void OnDecrement();
|
||||
static void ToggleHanded();
|
||||
|
||||
const MenuItem_T HandedMenuItem =
|
||||
{
|
||||
.OnFocus = OnFocus,
|
||||
.OnSelect = OnSelect,
|
||||
.OnIncrement = OnIncrement,
|
||||
.OnDecrement = OnDecrement
|
||||
|
||||
};
|
||||
|
||||
static void OnFocus(bool IncludeDetails)
|
||||
{
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_IS_RIGHT_HANDED, &Is_Right_Handed);
|
||||
if (Is_Right_Handed == true)
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_RIGHT_HANDED, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_LEFT_HANDED, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);;
|
||||
}
|
||||
}
|
||||
|
||||
static MenuItem_T const * OnSelect()
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_SELECTION_INDICATOR, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void OnIncrement()
|
||||
{
|
||||
ToggleHanded();
|
||||
}
|
||||
|
||||
static void OnDecrement()
|
||||
{
|
||||
ToggleHanded();
|
||||
}
|
||||
|
||||
static void ToggleHanded()
|
||||
{
|
||||
uint8_t Is_Right_Handed;
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_IS_RIGHT_HANDED, &Is_Right_Handed);
|
||||
|
||||
if (Is_Right_Handed == true)
|
||||
{
|
||||
Is_Right_Handed = false;
|
||||
(void) SETTINGS_set_uint8_t(SYSTEMK_SETTING_IS_RIGHT_HANDED, Is_Right_Handed);
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_LEFT_HANDED, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
}
|
||||
else
|
||||
{
|
||||
Is_Right_Handed = true;
|
||||
(void) SETTINGS_set_uint8_t(SYSTEMK_SETTING_IS_RIGHT_HANDED, Is_Right_Handed);
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_RIGHT_HANDED, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);;
|
||||
}
|
||||
}
|
27
Menu/HardwareSettings/HandedMenuItem.h
Normal file
27
Menu/HardwareSettings/HandedMenuItem.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HANDEDMENUITEM_H
|
||||
#define HANDEDMENUITEM_H
|
||||
|
||||
extern const MenuItem_T HandedMenuItem;
|
||||
|
||||
#endif // HANDEDMENUITEM_H
|
104
Menu/HardwareSettings/HardwareMenuItem.c
Normal file
104
Menu/HardwareSettings/HardwareMenuItem.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint8_t SubmenuIndex = 0;
|
||||
static MenuItem_T const * const Submenus[] =
|
||||
{
|
||||
&VolumeMenuItem,
|
||||
&HandedMenuItem
|
||||
};
|
||||
static const uint8_t N_SUBMENUS = (sizeof(Submenus) / sizeof(MenuItem_T *));
|
||||
|
||||
static const char * KLOG_TAG = "Menu | Hardware Settings";
|
||||
|
||||
static void OnFocus(bool IncludeDetails);
|
||||
static MenuItem_T const * OnSelect();
|
||||
static void OnIncrement();
|
||||
static void OnDecrement();
|
||||
|
||||
const MenuItem_T HardwareMenuItem =
|
||||
{
|
||||
.OnFocus = OnFocus,
|
||||
.OnSelect = OnSelect,
|
||||
.OnIncrement = OnIncrement,
|
||||
.OnDecrement = OnDecrement
|
||||
|
||||
};
|
||||
|
||||
static void OnFocus(bool IncludeDetails)
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_HARDWARE_SETTINGS_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
KLOG_INFO(KLOG_TAG, "<--");
|
||||
|
||||
if (IncludeDetails == true)
|
||||
{
|
||||
if (Submenus[SubmenuIndex]->OnFocus != NULL)
|
||||
{
|
||||
Submenus[SubmenuIndex]->OnFocus(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static MenuItem_T const * OnSelect()
|
||||
{
|
||||
return Submenus[SubmenuIndex];
|
||||
}
|
||||
|
||||
static void OnIncrement()
|
||||
{
|
||||
if (SubmenuIndex < (N_SUBMENUS -1))
|
||||
{
|
||||
SubmenuIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wrap around.
|
||||
SubmenuIndex = 0;
|
||||
}
|
||||
|
||||
if (Submenus[SubmenuIndex]->OnFocus != NULL)
|
||||
{
|
||||
Submenus[SubmenuIndex]->OnFocus(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnDecrement()
|
||||
{
|
||||
if (SubmenuIndex > 0)
|
||||
{
|
||||
SubmenuIndex--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wrap around.
|
||||
SubmenuIndex = (N_SUBMENUS -1);
|
||||
}
|
||||
|
||||
if (Submenus[SubmenuIndex]->OnFocus != NULL)
|
||||
{
|
||||
Submenus[SubmenuIndex]->OnFocus(false);
|
||||
}
|
||||
}
|
27
Menu/HardwareSettings/HardwareMenuItem.h
Normal file
27
Menu/HardwareSettings/HardwareMenuItem.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HARDWAREMENUITEM_H
|
||||
#define HARDWAREMENUITEM_H
|
||||
|
||||
extern const MenuItem_T HardwareMenuItem;
|
||||
|
||||
#endif // HARDWAREMENUITEM_H
|
116
Menu/HardwareSettings/VolumeMenuItem.c
Normal file
116
Menu/HardwareSettings/VolumeMenuItem.c
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define MAX_VOLUME (CONFIG_KTAG_MAX_AUDIO_VOLUME)
|
||||
#if (MAX_VOLUME > 100)
|
||||
#error "CONFIG_KTAG_MAX_AUDIO_VOLUME is too large. Volume has to be between 0 and 100."
|
||||
#endif
|
||||
#define MIN_VOLUME (CONFIG_KTAG_MIN_AUDIO_VOLUME)
|
||||
#if (MAX_VOLUME < MIN_VOLUME)
|
||||
#error "CONFIG_KTAG_MAX_AUDIO_VOLUME is less than CONFIG_KTAG_MIN_AUDIO_VOLUME. Something is not quite right."
|
||||
#endif
|
||||
|
||||
static uint8_t Volume;
|
||||
static const char * KLOG_TAG = "Menu | Volume";
|
||||
|
||||
static void OnFocus(bool IncludeDetails);
|
||||
static MenuItem_T const * OnSelect();
|
||||
static void OnIncrement();
|
||||
static void OnDecrement();
|
||||
|
||||
const MenuItem_T VolumeMenuItem =
|
||||
{
|
||||
.OnFocus = OnFocus,
|
||||
.OnSelect = OnSelect,
|
||||
.OnIncrement = OnIncrement,
|
||||
.OnDecrement = OnDecrement
|
||||
|
||||
};
|
||||
|
||||
static void OnFocus(bool IncludeDetails)
|
||||
{
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_AUDIO_VOLUME, &Volume);
|
||||
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_VOLUME_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
if (IncludeDetails == true)
|
||||
{
|
||||
AudioAction_T volume_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Volume};
|
||||
Perform_Audio_Action(&volume_action);
|
||||
}
|
||||
}
|
||||
|
||||
static MenuItem_T const * OnSelect()
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_SELECTION_INDICATOR, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void OnIncrement()
|
||||
{
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_AUDIO_VOLUME, &Volume);
|
||||
|
||||
if (Volume < MAX_VOLUME)
|
||||
{
|
||||
Volume++;
|
||||
}
|
||||
|
||||
KLOG_INFO(KLOG_TAG, "Volume incremented--is now %u. (Max is %u.)", Volume, MAX_VOLUME);
|
||||
|
||||
(void) SETTINGS_set_uint8_t(SYSTEMK_SETTING_AUDIO_VOLUME, Volume);
|
||||
|
||||
AudioAction_T set_volume_action = {.ID = AUDIO_SET_VOLUME, .Data = (void *)&Volume};
|
||||
Perform_Audio_Action(&set_volume_action);
|
||||
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_VOLUME_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
AudioAction_T volume_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Volume};
|
||||
Perform_Audio_Action(&volume_action);
|
||||
}
|
||||
|
||||
static void OnDecrement()
|
||||
{
|
||||
(void) SETTINGS_get_uint8_t(SYSTEMK_SETTING_AUDIO_VOLUME, &Volume);
|
||||
|
||||
if (Volume > MIN_VOLUME)
|
||||
{
|
||||
Volume--;
|
||||
}
|
||||
|
||||
KLOG_INFO(KLOG_TAG, "Volume decremented--is now %u. (Min is %u.)", Volume, MIN_VOLUME);
|
||||
|
||||
(void) SETTINGS_set_uint8_t(SYSTEMK_SETTING_AUDIO_VOLUME, Volume);
|
||||
|
||||
AudioAction_T set_volume_action = {.ID = AUDIO_SET_VOLUME, .Data = (void *)&Volume};
|
||||
Perform_Audio_Action(&set_volume_action);
|
||||
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_VOLUME_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
AudioAction_T volume_action = {.ID = AUDIO_PRONOUNCE_NUMBER_0_TO_100, .Play_To_Completion = true, .Data = (void *)&Volume};
|
||||
Perform_Audio_Action(&volume_action);
|
||||
}
|
27
Menu/HardwareSettings/VolumeMenuItem.h
Normal file
27
Menu/HardwareSettings/VolumeMenuItem.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef VOLUMEMENUITEM_H
|
||||
#define VOLUMEMENUITEM_H
|
||||
|
||||
extern const MenuItem_T VolumeMenuItem;
|
||||
|
||||
#endif // VOLUMEMENUITEM_H
|
107
Menu/Menu.c
Normal file
107
Menu/Menu.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint8_t SubmenuIndex = 0;
|
||||
static MenuItem_T const * const Submenus[] =
|
||||
{
|
||||
&GameMenuItem,
|
||||
&HardwareMenuItem
|
||||
};
|
||||
static const uint8_t N_SUBMENUS = (sizeof(Submenus) / sizeof(MenuItem_T *));
|
||||
|
||||
static void RootMenuOnFocus(bool IncludeDetails);
|
||||
static MenuItem_T const * RootMenuOnSelect();
|
||||
static void RootMenuOnIncrement();
|
||||
static void RootMenuOnDecrement();
|
||||
|
||||
static const MenuItem_T Root_Menu_Item =
|
||||
{
|
||||
.OnFocus = RootMenuOnFocus,
|
||||
.OnSelect = RootMenuOnSelect,
|
||||
.OnIncrement = RootMenuOnIncrement,
|
||||
.OnDecrement = RootMenuOnDecrement
|
||||
|
||||
};
|
||||
|
||||
MenuItem_T const * const RootMenu = &Root_Menu_Item;
|
||||
|
||||
static void RootMenuOnFocus(bool IncludeDetails)
|
||||
{
|
||||
AudioAction_T audio_action = {.ID = AUDIO_PLAY_MENU_PROMPT, .Play_To_Completion = true, .Data = (void *)0x00};
|
||||
Perform_Audio_Action(&audio_action);
|
||||
|
||||
if (IncludeDetails == true)
|
||||
{
|
||||
if (Submenus[SubmenuIndex]->OnFocus != NULL)
|
||||
{
|
||||
Submenus[SubmenuIndex]->OnFocus(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static MenuItem_T const * RootMenuOnSelect()
|
||||
{
|
||||
if (Submenus[SubmenuIndex]->OnSelect != NULL)
|
||||
{
|
||||
Submenus[SubmenuIndex]->OnSelect();
|
||||
}
|
||||
|
||||
return Submenus[SubmenuIndex];
|
||||
}
|
||||
|
||||
static void RootMenuOnIncrement()
|
||||
{
|
||||
if (SubmenuIndex < (N_SUBMENUS -1))
|
||||
{
|
||||
SubmenuIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wrap around.
|
||||
SubmenuIndex = 0;
|
||||
}
|
||||
|
||||
if (Submenus[SubmenuIndex]->OnFocus != NULL)
|
||||
{
|
||||
Submenus[SubmenuIndex]->OnFocus(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void RootMenuOnDecrement()
|
||||
{
|
||||
if (SubmenuIndex > 0)
|
||||
{
|
||||
SubmenuIndex--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wrap around.
|
||||
SubmenuIndex = (N_SUBMENUS -1);
|
||||
}
|
||||
|
||||
if (Submenus[SubmenuIndex]->OnFocus != NULL)
|
||||
{
|
||||
Submenus[SubmenuIndex]->OnFocus(false);
|
||||
}
|
||||
}
|
49
Menu/Menu.h
Normal file
49
Menu/Menu.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MENU_H
|
||||
#define MENU_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef struct MenuItem_S
|
||||
{
|
||||
// Performs the actions required when this MenuItem receives focus.
|
||||
void (*OnFocus)(bool IncludeDetails);
|
||||
// Performs the actions required when this MenuItem receives focus.
|
||||
struct MenuItem_S const * (*OnSelect)(void);
|
||||
void (*OnIncrement)(void);
|
||||
void (*OnDecrement)(void);
|
||||
|
||||
} MenuItem_T;
|
||||
|
||||
extern MenuItem_T const * const RootMenu;
|
||||
|
||||
#include "GameSettings/GameMenuItem.h"
|
||||
#include "GameSettings/PlayerIDMenuItem.h"
|
||||
#include "GameSettings/TeamIDMenuItem.h"
|
||||
#include "HardwareSettings/HardwareMenuItem.h"
|
||||
#include "HardwareSettings/VolumeMenuItem.h"
|
||||
#include "HardwareSettings/HandedMenuItem.h"
|
||||
|
||||
#endif // MENU_H
|
39
NeoPixels/Animation.h
Normal file
39
NeoPixels/Animation.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ANIMATION_H
|
||||
#define ANIMATION_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ANIMATION_ONGOING,
|
||||
ANIMATION_COMPLETE
|
||||
} AnimationStepResult_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void (*Reset)(void * Data);
|
||||
AnimationStepResult_T (*NextStep)(void);
|
||||
} Animation_T;
|
||||
|
||||
|
||||
#endif // ANIMATION_H
|
40
NeoPixels/Animations/All_Off.c
Normal file
40
NeoPixels/Animations/All_Off.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
NeoPixels_Set_Color_Range_On_All_Channels(0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, COLOR_BLACK);
|
||||
|
||||
return ANIMATION_COMPLETE;
|
||||
}
|
||||
|
||||
Animation_T All_Off_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/All_Off.h
Normal file
28
NeoPixels/Animations/All_Off.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ALL_OFF_H
|
||||
#define ALL_OFF_H
|
||||
|
||||
extern Animation_T All_Off_Animation;
|
||||
|
||||
#endif // ALL_OFF_H
|
108
NeoPixels/Animations/All_On.c
Normal file
108
NeoPixels/Animations/All_On.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static color_t All_On_Color = COLOR_WHITE;
|
||||
static DisplayStyle_T All_On_Style = DISPLAY_STYLE_DEFAULT;
|
||||
static uint32_t Time_in_Animation_in_ms = 0;
|
||||
|
||||
static void Reset(void *Data)
|
||||
{
|
||||
All_On_Color = ((All_On_Data_T *)Data)->color;
|
||||
All_On_Style = ((All_On_Data_T *)Data)->style;
|
||||
Time_in_Animation_in_ms = 0;
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
if (All_On_Style == DISPLAY_STYLE_ALTERNATE)
|
||||
{
|
||||
if ((Time_in_Animation_in_ms % 500) < 250)
|
||||
{
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(All_On_Color);
|
||||
Set_Heart(COLOR_BLACK);
|
||||
Set_Square(All_On_Color);
|
||||
Set_Circle(COLOR_BLACK);
|
||||
Set_Arrow(All_On_Color);
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
for (uint_fast8_t pixel = 0; pixel < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL; pixel++)
|
||||
{
|
||||
if (pixel % 2)
|
||||
{
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, pixel, All_On_Color);
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, pixel, All_On_Color);
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, pixel, All_On_Color);
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, pixel, All_On_Color);
|
||||
}
|
||||
else
|
||||
{
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, pixel, COLOR_BLACK);
|
||||
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);
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
}
|
||||
else
|
||||
{
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(COLOR_BLACK);
|
||||
Set_Heart(All_On_Color);
|
||||
Set_Square(COLOR_BLACK);
|
||||
Set_Circle(All_On_Color);
|
||||
Set_Arrow(COLOR_BLACK);
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
for (uint_fast8_t pixel = 0; pixel < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL; pixel++)
|
||||
{
|
||||
if (pixel % 2)
|
||||
{
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, pixel, COLOR_BLACK);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, pixel, All_On_Color);
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, pixel, All_On_Color);
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, pixel, All_On_Color);
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, pixel, All_On_Color);
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NeoPixels_Set_Color_Range_On_All_Channels(0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, All_On_Color);
|
||||
}
|
||||
|
||||
Time_in_Animation_in_ms += CONFIG_KTAG_ANIMATION_STEP_TIME_IN_ms;
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T All_On_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep};
|
27
NeoPixels/Animations/All_On.h
Normal file
27
NeoPixels/Animations/All_On.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ALL_ON_H
|
||||
#define ALL_ON_H
|
||||
|
||||
extern Animation_T All_On_Animation;
|
||||
|
||||
#endif // ALL_ON_H
|
87
NeoPixels/Animations/BLE_Nearby.c
Normal file
87
NeoPixels/Animations/BLE_Nearby.c
Normal file
|
@ -0,0 +1,87 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static BLENearby_T * RXd_Data;
|
||||
|
||||
static inline uint8_t RSSItoBrightness(int8_t RSSI)
|
||||
{
|
||||
uint8_t brightness = 255;
|
||||
|
||||
if (RSSI < 0)
|
||||
{
|
||||
brightness += (2*RSSI);
|
||||
}
|
||||
|
||||
return brightness;
|
||||
}
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
RXd_Data = (BLENearby_T *)Data;
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
AnimationStepResult_T result = ANIMATION_COMPLETE;
|
||||
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
// TODO: Implement the "BLE Nearby" animation for a single NeoPixel channel.
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
for (uint_fast8_t slot = 0; slot < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL; slot++)
|
||||
{
|
||||
if (RXd_Data->neighbors[slot].TimeToLive_in_ms > 0)
|
||||
{
|
||||
//NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, slot, ApplyMask(RXd_Data->neighbors[slot].Color, RSSItoBrightness(RXd_Data->neighbors[slot].RSSI)));
|
||||
|
||||
// TODO: Set a low brightness, and a blink rate proportional to RSSI.
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, slot, ApplyMask(RXd_Data->neighbors[slot].Color, 25));
|
||||
}
|
||||
else
|
||||
{
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, slot, COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
|
||||
for (uint_fast8_t slot = 0; slot < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL; slot++)
|
||||
{
|
||||
if (RXd_Data->neighbors[slot].TimeToLive_in_ms > CONFIG_KTAG_ANIMATION_STEP_TIME_IN_ms)
|
||||
{
|
||||
result = ANIMATION_ONGOING;
|
||||
RXd_Data->neighbors[slot].TimeToLive_in_ms -= CONFIG_KTAG_ANIMATION_STEP_TIME_IN_ms;
|
||||
}
|
||||
else
|
||||
{
|
||||
RXd_Data->neighbors[slot].TimeToLive_in_ms = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Animation_T BLE_Nearby_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
42
NeoPixels/Animations/BLE_Nearby.h
Normal file
42
NeoPixels/Animations/BLE_Nearby.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLE_NEARBY_H
|
||||
#define BLE_NEARBY_H
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t BD_ADDR[6];
|
||||
int8_t RSSI;
|
||||
color_t Color;
|
||||
uint16_t TimeToLive_in_ms;
|
||||
|
||||
} BLENeighbor_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BLENeighbor_T neighbors[CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL];
|
||||
} BLENearby_T;
|
||||
|
||||
extern Animation_T BLE_Nearby_Animation;
|
||||
|
||||
#endif // BLE_NEARBY_H
|
53
NeoPixels/Animations/BLE_RSSI.c
Normal file
53
NeoPixels/Animations/BLE_RSSI.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static color_t RSSSIColor = (color_t) 0x00000000;
|
||||
static uint8_t Brightness = 255;
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
RSSSIColor = *((color_t *)Data);
|
||||
Brightness = 255 + *((int8_t *)Data) + *((int8_t *)Data);
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(Color(0x00, 0x00, 0x00, 0x00));
|
||||
Set_Heart(ApplyMask(RSSSIColor, Brightness));
|
||||
Set_Square(ApplyMask(RSSSIColor, Brightness));
|
||||
Set_Circle(ApplyMask(RSSSIColor, Brightness));
|
||||
Set_Arrow(ApplyMask(RSSSIColor, Brightness));
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
NeoPixels_Set_Color_Range_On_All_Channels(0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL-1, ApplyMask(RSSSIColor, Brightness));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T BLE_RSSI_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/BLE_RSSI.h
Normal file
28
NeoPixels/Animations/BLE_RSSI.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLE_RSSI_H
|
||||
#define BLE_RSSI_H
|
||||
|
||||
extern Animation_T BLE_RSSI_Animation;
|
||||
|
||||
#endif // BLE_RSSI_H
|
165
NeoPixels/Animations/Countdown.c
Normal file
165
NeoPixels/Animations/Countdown.c
Normal file
|
@ -0,0 +1,165 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint8_t N_Lights;
|
||||
static color_t My_Color = COLOR_BLACK;
|
||||
|
||||
static const uint8_t BRIGHTNESS = 0x7F;
|
||||
|
||||
static void Reset(void *Data)
|
||||
{
|
||||
N_Lights = *((uint8_t *)Data);
|
||||
My_Color = HW_NeoPixels_Get_My_Color();
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
switch (N_Lights)
|
||||
{
|
||||
case 0:
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(ApplyMask(My_Color, 0x00));
|
||||
Set_Heart(ApplyMask(My_Color, 0x00));
|
||||
Set_Square(ApplyMask(My_Color, 0x00));
|
||||
Set_Circle(ApplyMask(My_Color, 0x00));
|
||||
Set_Arrow(ApplyMask(My_Color, 0x00));
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_RECEIVER, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, 0x00));
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_DISPLAY, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, 0x00));
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_EFFECTS, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, 0x00));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
break;
|
||||
|
||||
case 1:
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(ApplyMask(My_Color, 0x00));
|
||||
Set_Heart(ApplyMask(My_Color, 0x00));
|
||||
Set_Square(ApplyMask(My_Color, 0x00));
|
||||
Set_Circle(ApplyMask(My_Color, 0x00));
|
||||
Set_Arrow(ApplyMask(My_Color, BRIGHTNESS));
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, RIGHT_RECEIVER_INDICATOR_PIXEL, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, LEFT_RECEIVER_INDICATOR_PIXEL, COLOR_BLACK);
|
||||
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_DISPLAY, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, 0x00));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, 4, ApplyMask(My_Color, BRIGHTNESS));
|
||||
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_EFFECTS, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, 0x00));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, 4, ApplyMask(My_Color, BRIGHTNESS));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
break;
|
||||
|
||||
case 2:
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(ApplyMask(My_Color, 0x00));
|
||||
Set_Heart(ApplyMask(My_Color, 0x00));
|
||||
Set_Square(ApplyMask(My_Color, 0x00));
|
||||
Set_Circle(ApplyMask(My_Color, BRIGHTNESS));
|
||||
Set_Arrow(ApplyMask(My_Color, BRIGHTNESS));
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, RIGHT_RECEIVER_INDICATOR_PIXEL, COLOR_BLACK);
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, LEFT_RECEIVER_INDICATOR_PIXEL, ApplyMask(My_Color, BRIGHTNESS));
|
||||
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_DISPLAY, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, 0x00));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, 3, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, 4, ApplyMask(My_Color, BRIGHTNESS));
|
||||
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_EFFECTS, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, 0x00));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, 3, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, 4, ApplyMask(My_Color, BRIGHTNESS));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
break;
|
||||
|
||||
case 3:
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(ApplyMask(My_Color, 0x00));
|
||||
Set_Heart(ApplyMask(My_Color, 0x00));
|
||||
Set_Square(ApplyMask(My_Color, BRIGHTNESS));
|
||||
Set_Circle(ApplyMask(My_Color, BRIGHTNESS));
|
||||
Set_Arrow(ApplyMask(My_Color, BRIGHTNESS));
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, RIGHT_RECEIVER_INDICATOR_PIXEL, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, LEFT_RECEIVER_INDICATOR_PIXEL, COLOR_BLACK);
|
||||
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_DISPLAY, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, 0x00));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, 2, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, 3, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, 4, ApplyMask(My_Color, BRIGHTNESS));
|
||||
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_EFFECTS, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, 0x00));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, 2, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, 3, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, 4, ApplyMask(My_Color, BRIGHTNESS));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
break;
|
||||
|
||||
case 4:
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(ApplyMask(My_Color, 0x00));
|
||||
Set_Heart(ApplyMask(My_Color, BRIGHTNESS));
|
||||
Set_Square(ApplyMask(My_Color, BRIGHTNESS));
|
||||
Set_Circle(ApplyMask(My_Color, BRIGHTNESS));
|
||||
Set_Arrow(ApplyMask(My_Color, BRIGHTNESS));
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, RIGHT_RECEIVER_INDICATOR_PIXEL, COLOR_BLACK);
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, LEFT_RECEIVER_INDICATOR_PIXEL, ApplyMask(My_Color, BRIGHTNESS));
|
||||
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_DISPLAY, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, 0x00));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, 1, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, 2, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, 3, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_DISPLAY, 4, ApplyMask(My_Color, BRIGHTNESS));
|
||||
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_EFFECTS, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, 0x00));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, 1, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, 2, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, 3, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_EFFECTS, 4, ApplyMask(My_Color, BRIGHTNESS));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
break;
|
||||
|
||||
case 5:
|
||||
default:
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(ApplyMask(My_Color, BRIGHTNESS));
|
||||
Set_Heart(ApplyMask(My_Color, BRIGHTNESS));
|
||||
Set_Square(ApplyMask(My_Color, BRIGHTNESS));
|
||||
Set_Circle(ApplyMask(My_Color, BRIGHTNESS));
|
||||
Set_Arrow(ApplyMask(My_Color, BRIGHTNESS));
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_RECEIVER, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_DISPLAY, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, BRIGHTNESS));
|
||||
NeoPixels_Set_Color_Range(NEOPIXEL_CHANNEL_EFFECTS, 0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, ApplyMask(My_Color, BRIGHTNESS));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
break;
|
||||
}
|
||||
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T Countdown_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Countdown.h
Normal file
28
NeoPixels/Animations/Countdown.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef COUNTDOWN_H
|
||||
#define COUNTDOWN_H
|
||||
|
||||
extern Animation_T Countdown_Animation;
|
||||
|
||||
#endif // COUNTDOWN_H
|
107
NeoPixels/Animations/Flamethrower.c
Normal file
107
NeoPixels/Animations/Flamethrower.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define RED_DELTA_MAX 15
|
||||
#define GREEN_DELTA_MAX 8
|
||||
#define MAX_RED 128
|
||||
#define MIN_RED RED_DELTA_MAX
|
||||
#define MIN_GREEN 1
|
||||
#define LESS_GREEN_THAN_RED 20
|
||||
|
||||
static int16_t RedLevel = 0;
|
||||
static int16_t GreenLevel = 0;
|
||||
|
||||
// From https://stackoverflow.com/a/1180465
|
||||
static uint8_t lfsr113_Bits (uint32_t min, uint32_t max)
|
||||
{
|
||||
static uint32_t z1 = 12345, z2 = 12345, z3 = 12345, z4 = 12345;
|
||||
uint32_t b;
|
||||
b = ((z1 << 6) ^ z1) >> 13;
|
||||
z1 = ((z1 & 4294967294U) << 18) ^ b;
|
||||
b = ((z2 << 2) ^ z2) >> 27;
|
||||
z2 = ((z2 & 4294967288U) << 2) ^ b;
|
||||
b = ((z3 << 13) ^ z3) >> 21;
|
||||
z3 = ((z3 & 4294967280U) << 7) ^ b;
|
||||
b = ((z4 << 3) ^ z4) >> 12;
|
||||
z4 = ((z4 & 4294967168U) << 13) ^ b;
|
||||
uint64_t raw = (z1 ^ z2 ^ z3 ^ z4);
|
||||
uint32_t scaled = ((((max - min) * raw) + (UINT32_MAX / 2)) / UINT32_MAX) + min;
|
||||
return (uint8_t) scaled;
|
||||
}
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
RedLevel = MIN_RED;
|
||||
GreenLevel = MIN_GREEN;
|
||||
}
|
||||
|
||||
// TODO: Need to reimplement this with HSB--see https://blog.adafruit.com/2012/03/14/constant-brightness-hsb-to-rgb-algorithm/
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
// get our next changes in red and green
|
||||
int16_t delta_red = (lfsr113_Bits(0, RED_DELTA_MAX*2) - RED_DELTA_MAX);
|
||||
int16_t delta_green = (lfsr113_Bits(0, GREEN_DELTA_MAX*2) - GREEN_DELTA_MAX);
|
||||
|
||||
RedLevel = RedLevel + delta_red;
|
||||
|
||||
if (RedLevel > MAX_RED)
|
||||
{
|
||||
RedLevel = MAX_RED;
|
||||
}
|
||||
else if (RedLevel < MIN_RED)
|
||||
{
|
||||
RedLevel = MIN_RED;
|
||||
}
|
||||
RedLevel = (uint8_t) RedLevel;
|
||||
|
||||
GreenLevel = GreenLevel + delta_green;
|
||||
|
||||
// Green has to be less than red, to keep the flame red-orange-yellow.
|
||||
if (RedLevel > LESS_GREEN_THAN_RED)
|
||||
{
|
||||
if (GreenLevel > (RedLevel - LESS_GREEN_THAN_RED))
|
||||
{
|
||||
GreenLevel = (RedLevel - LESS_GREEN_THAN_RED);
|
||||
}
|
||||
else if (GreenLevel < MIN_GREEN)
|
||||
{
|
||||
GreenLevel = MIN_GREEN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GreenLevel = MIN_GREEN;
|
||||
}
|
||||
GreenLevel = (uint8_t) GreenLevel;
|
||||
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, BARREL_FLASH_PIXEL, Color(0xFF, RedLevel, GreenLevel, 0x00));
|
||||
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T Flamethrower_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Flamethrower.h
Normal file
28
NeoPixels/Animations/Flamethrower.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FLAMETHROWER_H
|
||||
#define FLAMETHROWER_H
|
||||
|
||||
extern Animation_T Flamethrower_Animation;
|
||||
|
||||
#endif // FLAMETHROWER_H
|
40
NeoPixels/Animations/Flashlight.c
Normal file
40
NeoPixels/Animations/Flashlight.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, BARREL_FLASH_PIXEL, COLOR_WHITE);
|
||||
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T Flashlight_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Flashlight.h
Normal file
28
NeoPixels/Animations/Flashlight.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FLASHLIGHT_H
|
||||
#define FLASHLIGHT_H
|
||||
|
||||
extern Animation_T Flashlight_Animation;
|
||||
|
||||
#endif // FLASHLIGHT_H
|
116
NeoPixels/Animations/Health_Report.c
Normal file
116
NeoPixels/Animations/Health_Report.c
Normal file
|
@ -0,0 +1,116 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint8_t Health_in_percent;
|
||||
static uint8_t On = 0xFF / 2;
|
||||
static uint8_t Blink;
|
||||
static uint8_t State = 0;
|
||||
static uint16_t Time_in_Current_Scene_in_ms = 0;
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
Health_in_percent = *((uint8_t *)Data);
|
||||
Time_in_Current_Scene_in_ms = 0;
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
if (State > 1)
|
||||
{
|
||||
State = 0;
|
||||
}
|
||||
|
||||
Blink = On * State;
|
||||
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
if (Health_in_percent > 0) // 10
|
||||
{
|
||||
Set_Heart(Color(Blink, 0xFF, 0x00, 0x00));
|
||||
}
|
||||
|
||||
if (Health_in_percent > 10) // 20 - 30
|
||||
{
|
||||
Set_Heart(Color(On, 0xFF, 0x00, 0x00));
|
||||
}
|
||||
|
||||
if (Health_in_percent > 30) // 40
|
||||
{
|
||||
Set_Heart(Color(On, 0xFF, 0xFF, 0x00));
|
||||
Set_Square(Color(Blink, 0xFF, 0xFF, 0x00));
|
||||
}
|
||||
if (Health_in_percent > 40) // 50
|
||||
{
|
||||
Set_Heart(Color(On, 0xFF, 0xFF, 0x00));
|
||||
Set_Square(Color(On, 0xFF, 0xFF, 0x00));
|
||||
}
|
||||
|
||||
if (Health_in_percent > 50) // 60
|
||||
{
|
||||
Set_Heart(Color(On, 0x00, 0xFF, 0x00));
|
||||
Set_Square(Color(On, 0x00, 0xFF, 0x00));
|
||||
Set_Circle(Color(Blink, 0x00, 0xFF, 0x00));
|
||||
}
|
||||
|
||||
if (Health_in_percent > 60) // 70 - 80
|
||||
{
|
||||
Set_Heart(Color(On, 0x00, 0xFF, 0x00));
|
||||
Set_Square(Color(On, 0x00, 0xFF, 0x00));
|
||||
Set_Circle(Color(On, 0x00, 0xFF, 0x00));
|
||||
}
|
||||
|
||||
if (Health_in_percent > 80) // 90 - 100
|
||||
{
|
||||
Set_Heart(Color(On, 0x00, 0xFF, 0x00));
|
||||
Set_Square(Color(On, 0x00, 0xFF, 0x00));
|
||||
Set_Circle(Color(On, 0x00, 0xFF, 0x00));
|
||||
Set_Arrow(Color(Blink, 0x00, 0xFF, 0x00));
|
||||
}
|
||||
|
||||
if (Health_in_percent == 100) // 100
|
||||
{
|
||||
Set_Heart(Color(On, 0x00, 0xFF, 0x00));
|
||||
Set_Square(Color(On, 0x00, 0xFF, 0x00));
|
||||
Set_Circle(Color(On, 0x00, 0xFF, 0x00));
|
||||
Set_Arrow(Color(On, 0x00, 0xFF, 0x00));
|
||||
}
|
||||
|
||||
Time_in_Current_Scene_in_ms += CONFIG_KTAG_ANIMATION_STEP_TIME_IN_ms;
|
||||
|
||||
if (Time_in_Current_Scene_in_ms > 100)
|
||||
{
|
||||
Time_in_Current_Scene_in_ms = 0;
|
||||
State++;
|
||||
}
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
// TODO: Implement the "Health Report" animation for four NeoPixel channels.
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T Health_Report_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Health_Report.h
Normal file
28
NeoPixels/Animations/Health_Report.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HEALTH_REPORT_H
|
||||
#define HEALTH_REPORT_H
|
||||
|
||||
extern Animation_T Health_Report_Animation;
|
||||
|
||||
#endif // HEALTH_REPORT_H
|
121
NeoPixels/Animations/Idle_Animation.c
Normal file
121
NeoPixels/Animations/Idle_Animation.c
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint8_t Scene = 0;
|
||||
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 = 0;
|
||||
My_Color = HW_NeoPixels_Get_My_Color();
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
switch (Scene)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, BARREL_FLASH_PIXEL, ApplyMask(COLOR_WHITE, 0x70));
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, RECEIVER_INDICATOR_PIXEL, ApplyMask(COLOR_WHITE, 0x70));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
break;
|
||||
|
||||
case 1:
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(ApplyMask(COLOR_WHITE, 0x70));
|
||||
Set_Heart(COLOR_BLACK);
|
||||
Set_Square(ApplyMask(My_Color, 0x70));
|
||||
Set_Circle(COLOR_BLACK);
|
||||
Set_Arrow(ApplyMask(My_Color, 0x70));
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
for (uint_fast8_t pixel = 0; pixel < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL; pixel++)
|
||||
{
|
||||
if (pixel % 2)
|
||||
{
|
||||
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
|
||||
break;
|
||||
|
||||
case 2:
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(ApplyMask(COLOR_WHITE, 0x70));
|
||||
Set_Heart(ApplyMask(My_Color, 0x70));
|
||||
Set_Square(COLOR_BLACK);
|
||||
Set_Circle(ApplyMask(My_Color, 0x70));
|
||||
Set_Arrow(COLOR_BLACK);
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
for (uint_fast8_t pixel = 0; pixel < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL; pixel++)
|
||||
{
|
||||
if (pixel % 2)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, RECEIVER_INDICATOR_PIXEL, ApplyMask(COLOR_WHITE, 0x70));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
break;
|
||||
}
|
||||
|
||||
Time_in_Current_Scene_in_ms += CONFIG_KTAG_ANIMATION_STEP_TIME_IN_ms;
|
||||
|
||||
if (Time_in_Current_Scene_in_ms > 500)
|
||||
{
|
||||
Time_in_Current_Scene_in_ms = 0;
|
||||
Scene++;
|
||||
if (Scene > 2)
|
||||
{
|
||||
Scene = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T Idle_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Idle_Animation.h
Normal file
28
NeoPixels/Animations/Idle_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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IDLE_ANIMATION_H
|
||||
#define IDLE_ANIMATION_H
|
||||
|
||||
extern Animation_T Idle_Animation;
|
||||
|
||||
#endif // IDLE_ANIMATION_H
|
115
NeoPixels/Animations/Menu_Animation.c
Normal file
115
NeoPixels/Animations/Menu_Animation.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint8_t Scene = 0;
|
||||
static uint8_t N_SCENES = 4;
|
||||
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 = 0;
|
||||
}
|
||||
|
||||
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 > 100)
|
||||
{
|
||||
Time_in_Current_Scene_in_ms = 0;
|
||||
Scene++;
|
||||
if (Scene >= N_SCENES)
|
||||
{
|
||||
Scene = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T Menu_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Menu_Animation.h
Normal file
28
NeoPixels/Animations/Menu_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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MENU_ANIMATION_H
|
||||
#define MENU_ANIMATION_H
|
||||
|
||||
extern Animation_T Menu_Animation;
|
||||
|
||||
#endif // MENU_ANIMATION_H
|
59
NeoPixels/Animations/Shot_Fired.c
Normal file
59
NeoPixels/Animations/Shot_Fired.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint16_t Time_in_Animation_in_ms = 0;
|
||||
static color_t Flash_Color = COLOR_BLACK;
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
Time_in_Animation_in_ms = 0;
|
||||
Flash_Color = HW_NeoPixels_Get_My_Color();
|
||||
NeoPixels_Set_Color_Range_On_All_Channels(0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, COLOR_BLACK);
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
AnimationStepResult_T result;
|
||||
|
||||
if (Time_in_Animation_in_ms < 250)
|
||||
{
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, BARREL_FLASH_PIXEL, Flash_Color);
|
||||
|
||||
Time_in_Animation_in_ms += CONFIG_KTAG_ANIMATION_STEP_TIME_IN_ms;
|
||||
result = ANIMATION_ONGOING;
|
||||
}
|
||||
else
|
||||
{
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, BARREL_FLASH_PIXEL, COLOR_BLACK);
|
||||
result = ANIMATION_COMPLETE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Animation_T Shot_Fired_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Shot_Fired.h
Normal file
28
NeoPixels/Animations/Shot_Fired.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SHOT_FIRED_H
|
||||
#define SHOT_FIRED_H
|
||||
|
||||
extern Animation_T Shot_Fired_Animation;
|
||||
|
||||
#endif // SHOT_FIRED_H
|
70
NeoPixels/Animations/Tag_Received.c
Normal file
70
NeoPixels/Animations/Tag_Received.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint16_t Time_in_Animation_in_ms = 0;
|
||||
static color_t TagTeamColor = (color_t) 0x00000000;
|
||||
static uint8_t Fade = 0x40;
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
Time_in_Animation_in_ms = 0;
|
||||
TagTeamColor = *((color_t *)Data);
|
||||
Fade = 128;
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
AnimationStepResult_T result;
|
||||
|
||||
if (Fade < 255)
|
||||
{
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(ApplyMask(TagTeamColor, Sine8[Fade]));
|
||||
Set_Heart(ApplyMask(TagTeamColor, Sine8[Fade]));
|
||||
Set_Square(ApplyMask(TagTeamColor, Sine8[Fade]));
|
||||
Set_Circle(ApplyMask(TagTeamColor, Sine8[Fade]));
|
||||
Set_Arrow(ApplyMask(TagTeamColor, Sine8[Fade]));
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, BARREL_FLASH_PIXEL, ApplyMask(TagTeamColor, Sine8[Fade]));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, RECEIVER_INDICATOR_PIXEL, ApplyMask(TagTeamColor, Sine8[Fade]));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
|
||||
Fade += 1;
|
||||
result = ANIMATION_ONGOING;
|
||||
}
|
||||
else
|
||||
{
|
||||
NeoPixels_Set_Color_Range_On_All_Channels(0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, COLOR_BLACK);
|
||||
|
||||
result = ANIMATION_COMPLETE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Animation_T Tag_Received_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Tag_Received.h
Normal file
28
NeoPixels/Animations/Tag_Received.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TAG_RECEIVED_H
|
||||
#define TAG_RECEIVED_H
|
||||
|
||||
extern Animation_T Tag_Received_Animation;
|
||||
|
||||
#endif // TAG_RECEIVED_H
|
82
NeoPixels/Animations/Tagged_Out.c
Normal file
82
NeoPixels/Animations/Tagged_Out.c
Normal file
|
@ -0,0 +1,82 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint8_t Fade = 0xFF;
|
||||
static color_t My_Color = COLOR_BLACK;
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
Fade = 0xFF;
|
||||
My_Color = HW_NeoPixels_Get_My_Color();
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
uint8_t phase_0 = Fade;
|
||||
uint8_t phase_1 = phase_0 - (uint8_t)64;
|
||||
uint8_t phase_2 = phase_1 - (uint8_t)64;
|
||||
uint8_t phase_3 = phase_2 - (uint8_t)64;
|
||||
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(Color(Sine8[phase_0], 0xFF, 0x00, 0x00));
|
||||
Set_Heart(Color(Sine8[phase_1], 0xFF, 0x00, 0x00));
|
||||
Set_Square(Color(Sine8[phase_2], 0xFF, 0x00, 0x00));
|
||||
Set_Circle(Color(Sine8[phase_3], 0xFF, 0x00, 0x00));
|
||||
Set_Arrow(Color(Sine8[phase_0], 0xFF, 0x00, 0x00));
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
for (uint_fast8_t pixel = 0; pixel < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL; pixel++)
|
||||
{
|
||||
switch (pixel % 4)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
NeoPixels_Set_Color_On_All_Channels(pixel, ApplyMask(My_Color, Sine8[phase_0]));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
NeoPixels_Set_Color_On_All_Channels(pixel, ApplyMask(My_Color, Sine8[phase_1]));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
NeoPixels_Set_Color_On_All_Channels(pixel, ApplyMask(My_Color, Sine8[phase_2]));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
NeoPixels_Set_Color_On_All_Channels(pixel, ApplyMask(My_Color, Sine8[phase_3]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_BARREL, BARREL_FLASH_PIXEL, COLOR_WHITE);
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
|
||||
Fade += 2;
|
||||
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T Tagged_Out_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Tagged_Out.h
Normal file
28
NeoPixels/Animations/Tagged_Out.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TAGGED_OUT_H
|
||||
#define TAGGED_OUT_H
|
||||
|
||||
extern Animation_T Tagged_Out_Animation;
|
||||
|
||||
#endif // TAGGED_OUT_H
|
162
NeoPixels/Animations/Team_Colors.c
Normal file
162
NeoPixels/Animations/Team_Colors.c
Normal file
|
@ -0,0 +1,162 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
STEP_LONG_DARK,
|
||||
STEP_BLINK_ONE,
|
||||
STEP_BLINK_TWO,
|
||||
STEP_BLINK_THREE,
|
||||
STEP_HEARTBEAT_GLOW
|
||||
} TeamColorsStep_T;
|
||||
|
||||
static DisplayStyle_T Style = DISPLAY_STYLE_BLINK;
|
||||
static TeamColorsStep_T Step = STEP_LONG_DARK;
|
||||
static uint16_t Time_in_Animation_Step_in_ms = 0;
|
||||
static color_t Team_Color = COLOR_BLACK;
|
||||
static uint8_t Brightness = 0;
|
||||
static uint8_t Phase = 0;
|
||||
static const uint8_t LOW_BRIGHTNESS = 10;
|
||||
static const uint8_t HIGH_BRIGHTNESS = 50;
|
||||
static const uint16_t LONG_DARK_TIME_IN_ms = 20000;
|
||||
static const uint16_t BLINK_TIME_ON_IN_ms = 100;
|
||||
static const uint16_t BLINK_TIME_OFF_IN_ms = 100;
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
// Start just before the dawn.
|
||||
Time_in_Animation_Step_in_ms = LONG_DARK_TIME_IN_ms;
|
||||
Style = NeoPixels_ToDisplayStyle(Data);
|
||||
Team_Color = HW_NeoPixels_Get_My_Color();
|
||||
NeoPixels_Set_Color_Range_On_All_Channels(0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, COLOR_BLACK);
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
Time_in_Animation_Step_in_ms += CONFIG_KTAG_ANIMATION_STEP_TIME_IN_ms;
|
||||
|
||||
switch (Step)
|
||||
{
|
||||
default:
|
||||
case STEP_LONG_DARK:
|
||||
Brightness = LOW_BRIGHTNESS;
|
||||
if (Time_in_Animation_Step_in_ms > LONG_DARK_TIME_IN_ms)
|
||||
{
|
||||
Time_in_Animation_Step_in_ms = 0;
|
||||
if (Style == DISPLAY_STYLE_HEARTBEAT)
|
||||
{
|
||||
Step = STEP_HEARTBEAT_GLOW;
|
||||
Phase = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Step = STEP_BLINK_ONE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STEP_BLINK_ONE:
|
||||
if (Time_in_Animation_Step_in_ms > (BLINK_TIME_ON_IN_ms + BLINK_TIME_OFF_IN_ms))
|
||||
{
|
||||
Brightness = HIGH_BRIGHTNESS;
|
||||
Time_in_Animation_Step_in_ms = 0;
|
||||
Step = STEP_BLINK_TWO;
|
||||
}
|
||||
else if (Time_in_Animation_Step_in_ms > BLINK_TIME_ON_IN_ms)
|
||||
{
|
||||
Brightness = LOW_BRIGHTNESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
Brightness = HIGH_BRIGHTNESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case STEP_BLINK_TWO:
|
||||
if (Time_in_Animation_Step_in_ms > (BLINK_TIME_ON_IN_ms + BLINK_TIME_OFF_IN_ms))
|
||||
{
|
||||
Brightness = HIGH_BRIGHTNESS;
|
||||
Time_in_Animation_Step_in_ms = 0;
|
||||
Step = STEP_BLINK_THREE;
|
||||
}
|
||||
else if (Time_in_Animation_Step_in_ms > BLINK_TIME_ON_IN_ms)
|
||||
{
|
||||
Brightness = LOW_BRIGHTNESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
Brightness = HIGH_BRIGHTNESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case STEP_BLINK_THREE:
|
||||
if (Time_in_Animation_Step_in_ms > (BLINK_TIME_ON_IN_ms + BLINK_TIME_OFF_IN_ms))
|
||||
{
|
||||
Brightness = LOW_BRIGHTNESS;
|
||||
Time_in_Animation_Step_in_ms = 0;
|
||||
Step = STEP_LONG_DARK;
|
||||
}
|
||||
else if (Time_in_Animation_Step_in_ms > BLINK_TIME_ON_IN_ms)
|
||||
{
|
||||
Brightness = LOW_BRIGHTNESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
Brightness = HIGH_BRIGHTNESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case STEP_HEARTBEAT_GLOW:
|
||||
Brightness = Sine8[Phase] / 2;
|
||||
Phase++;
|
||||
|
||||
if (Phase == 0)
|
||||
{
|
||||
Brightness = LOW_BRIGHTNESS;
|
||||
Time_in_Animation_Step_in_ms = 0;
|
||||
Step = STEP_LONG_DARK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
Set_Barrel_Flash(ApplyMask(Team_Color, Brightness));
|
||||
Set_Heart(COLOR_BLACK);
|
||||
Set_Square(COLOR_BLACK);
|
||||
Set_Circle(COLOR_BLACK);
|
||||
Set_Arrow(COLOR_BLACK);
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, RECEIVER_INDICATOR_PIXEL, ApplyMask(Team_Color, Brightness));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, RIGHT_RECEIVER_INDICATOR_PIXEL, ApplyMask(Team_Color, Brightness));
|
||||
NeoPixels_Set_Color(NEOPIXEL_CHANNEL_RECEIVER, LEFT_RECEIVER_INDICATOR_PIXEL, ApplyMask(Team_Color, Brightness));
|
||||
#endif // CONFIG_KTAG_N_NEOPIXEL_CHANNELS
|
||||
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T Team_Colors_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Team_Colors.h
Normal file
28
NeoPixels/Animations/Team_Colors.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TEAM_COLORS_H
|
||||
#define TEAM_COLORS_H
|
||||
|
||||
extern Animation_T Team_Colors_Animation;
|
||||
|
||||
#endif // TEAM_COLORS_H
|
116
NeoPixels/Animations/Test_Pattern.c
Normal file
116
NeoPixels/Animations/Test_Pattern.c
Normal file
|
@ -0,0 +1,116 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static uint8_t Scene = 0;
|
||||
static uint16_t Time_in_Current_Scene_in_ms = 0;
|
||||
|
||||
static void Reset(void * Data)
|
||||
{
|
||||
Time_in_Current_Scene_in_ms = 0;
|
||||
Scene = 0;
|
||||
}
|
||||
|
||||
static AnimationStepResult_T NextStep(void)
|
||||
{
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
switch (Scene)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
Set_Barrel_Flash(Color(0xFF, 0xFF, 0x00, 0x00));
|
||||
Set_Heart(Color(0xFF / 2, 0xFF, 0x00, 0x00));
|
||||
Set_Square(Color(0xFF / 2, 0x00, 0xFF, 0x00));
|
||||
Set_Circle(Color(0xFF / 2, 0xFF, 0x00, 0xFF));
|
||||
Set_Arrow(Color(0xFF / 2, 0xFF, 0xFF, 0x00));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Set_Barrel_Flash(Color(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
Set_Heart(Color(0xFF / 2, 0xFF, 0xFF, 0x00));
|
||||
Set_Square(Color(0xFF / 2, 0xFF, 0x00, 0x00));
|
||||
Set_Circle(Color(0xFF / 2, 0x00, 0xFF, 0x00));
|
||||
Set_Arrow(Color(0xFF / 2, 0xFF, 0x00, 0xFF));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Set_Barrel_Flash(Color(0xFF, 0x00, 0x00, 0xFF));
|
||||
Set_Heart(Color(0xFF / 2, 0xFF, 0x00, 0xFF));
|
||||
Set_Square(Color(0xFF / 2, 0xFF, 0xFF, 0x00));
|
||||
Set_Circle(Color(0xFF / 2, 0xFF, 0x00, 0x00));
|
||||
Set_Arrow(Color(0xFF / 2, 0x00, 0xFF, 0x00));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
Set_Barrel_Flash(Color(0xFF, 0x00, 0x00, 0x00));
|
||||
Set_Heart(Color(0xFF / 2, 0x00, 0xFF, 0x00));
|
||||
Set_Square(Color(0xFF / 2, 0xFF, 0x00, 0xFF));
|
||||
Set_Circle(Color(0xFF / 2, 0xFF, 0xFF, 0x00));
|
||||
Set_Arrow(Color(0xFF / 2, 0xFF, 0x00, 0x00));
|
||||
break;
|
||||
}
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
for (uint_fast8_t pixel = 0; pixel < CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL; pixel++)
|
||||
{
|
||||
switch ((pixel + Scene) % 4)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
NeoPixels_Set_Color_On_All_Channels(pixel, ApplyMask(COLOR_RED, 0xFF / 2));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
NeoPixels_Set_Color_On_All_Channels(pixel, ApplyMask(COLOR_WHITE, 0xFF / 2));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
NeoPixels_Set_Color_On_All_Channels(pixel, ApplyMask(COLOR_BLUE, 0xFF / 2));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
NeoPixels_Set_Color_On_All_Channels(pixel, COLOR_BLACK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#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 > 100)
|
||||
{
|
||||
Time_in_Current_Scene_in_ms = 0;
|
||||
Scene++;
|
||||
if (Scene > 3)
|
||||
{
|
||||
Scene = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ANIMATION_ONGOING;
|
||||
}
|
||||
|
||||
Animation_T Test_Pattern_Animation =
|
||||
{
|
||||
.Reset = Reset,
|
||||
.NextStep = NextStep
|
||||
};
|
28
NeoPixels/Animations/Test_Pattern.h
Normal file
28
NeoPixels/Animations/Test_Pattern.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TEST_PATTERN_H
|
||||
#define TEST_PATTERN_H
|
||||
|
||||
extern Animation_T Test_Pattern_Animation;
|
||||
|
||||
#endif // TEST_PATTERN_H
|
48
NeoPixels/Displays.h
Normal file
48
NeoPixels/Displays.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DISPLAYS_H
|
||||
#define DISPLAYS_H
|
||||
|
||||
#include "Gamma.h"
|
||||
#include "Sine.h"
|
||||
#include "Animation.h"
|
||||
|
||||
#include "NeoPixel_Hardware_Config.h"
|
||||
#include "NeoPixels.h"
|
||||
|
||||
#include "Animations/All_Off.h"
|
||||
#include "Animations/BLE_Nearby.h"
|
||||
#include "Animations/BLE_RSSI.h"
|
||||
#include "Animations/Countdown.h"
|
||||
#include "Animations/Flamethrower.h"
|
||||
#include "Animations/Flashlight.h"
|
||||
#include "Animations/Health_Report.h"
|
||||
#include "Animations/Idle_Animation.h"
|
||||
#include "Animations/Menu_Animation.h"
|
||||
#include "Animations/Shot_Fired.h"
|
||||
#include "Animations/Tag_Received.h"
|
||||
#include "Animations/Tagged_Out.h"
|
||||
#include "Animations/Team_Colors.h"
|
||||
#include "Animations/Test_Pattern.h"
|
||||
|
||||
#endif // DISPLAYS_H
|
60
NeoPixels/Four_Channel_Helpers.h
Normal file
60
NeoPixels/Four_Channel_Helpers.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// /\ /\\ /\ /\\ TM
|
||||
// ( ) || || ( ) || || |''||''| '||''|. ..|'''.|
|
||||
// // || || // || || || || || .|' '
|
||||
// // || || // || || || ||...|' ||
|
||||
// /( || || /( || || || || '|. .
|
||||
// {___ \\/ {___ \\/ .||. .||. ''|....'
|
||||
//
|
||||
//
|
||||
// _____ _____ _____ ___________ _ _
|
||||
// |____ |/ __ \| ___/ ___| ___ \ (_) | |
|
||||
// / /`' / /'| |__ \ `--.| |_/ /__ ___ _ __ _| |
|
||||
// \ \ / / | __| `--. \ __/ _ \/ __| |/ _` | |
|
||||
// .___/ /./ /___| |___/\__/ / | | __/ (__| | (_| | |
|
||||
// \____/ \_____/\____/\____/\_| \___|\___|_|\__,_|_|
|
||||
|
||||
|
||||
|
||||
//! Zero-based index of the Barrel Flash NeoPixel in the sequence of NeoPixels.
|
||||
#define BARREL_FLASH_PIXEL 0
|
||||
|
||||
//! Zero-based index of the Receiver Indicator NeoPixel in the sequence of NeoPixels.
|
||||
#define RECEIVER_INDICATOR_PIXEL 0
|
||||
|
||||
//! Zero-based index of the right Receiver Indicator NeoPixel in the sequence of NeoPixels.
|
||||
#define RIGHT_RECEIVER_INDICATOR_PIXEL 1
|
||||
|
||||
//! Zero-based index of the left Receiver Indicator NeoPixel in the sequence of NeoPixels.
|
||||
#define LEFT_RECEIVER_INDICATOR_PIXEL 2
|
||||
|
||||
static inline __attribute__((always_inline)) void NeoPixels_Set_Color_On_All_Channels(uint8_t position, color_t color)
|
||||
{
|
||||
HW_NeoPixels_Set_RGB(NEOPIXEL_CHANNEL_BARREL, position, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
HW_NeoPixels_Set_RGB(NEOPIXEL_CHANNEL_RECEIVER, position, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
HW_NeoPixels_Set_RGB(NEOPIXEL_CHANNEL_DISPLAY, position, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
HW_NeoPixels_Set_RGB(NEOPIXEL_CHANNEL_EFFECTS, position, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
}
|
44
NeoPixels/Gamma.c
Normal file
44
NeoPixels/Gamma.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Gamma.h"
|
||||
|
||||
// https://learn.adafruit.com/led-tricks-gamma-correction/the-quick-fix
|
||||
|
||||
const uint_fast8_t Gamma8[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
|
||||
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
|
||||
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
|
||||
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
|
||||
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
|
||||
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
|
||||
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
|
||||
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
|
||||
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
|
||||
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
|
||||
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
|
||||
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
|
||||
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
|
28
NeoPixels/Gamma.h
Normal file
28
NeoPixels/Gamma.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GAMMA_H
|
||||
#define GAMMA_H
|
||||
|
||||
extern const uint_fast8_t Gamma8[];
|
||||
|
||||
#endif // GAMMA_H
|
30
NeoPixels/NeoPixel_HW_Interface.h
Normal file
30
NeoPixels/NeoPixel_HW_Interface.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NEOPIXEL_HW_INTERFACE_H
|
||||
#define NEOPIXEL_HW_INTERFACE_H
|
||||
|
||||
SystemKResult_T HW_NeoPixels_Init(void);
|
||||
SystemKResult_T HW_NeoPixels_Set_RGB(NeoPixelsChannel_T channel, uint8_t position, uint8_t red, uint8_t green, uint8_t blue);
|
||||
SystemKResult_T HW_NeoPixels_Publish(void);
|
||||
color_t HW_NeoPixels_Get_My_Color(void);
|
||||
|
||||
#endif // NEOPIXEL_HW_INTERFACE_H
|
164
NeoPixels/NeoPixels.c
Normal file
164
NeoPixels/NeoPixels.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
QueueHandle_t xQueueNeoPixels;
|
||||
TaskHandle_t NeoPixels_Task_Handle;
|
||||
SemaphoreHandle_t NeoPixels_Semaphore;
|
||||
|
||||
static const TickType_t Animation_Delay = CONFIG_KTAG_ANIMATION_STEP_TIME_IN_ms / portTICK_PERIOD_MS;
|
||||
static Animation_T * Current_Foreground_Animation = &All_Off_Animation;
|
||||
static Animation_T * Current_Background_Animation = &All_Off_Animation;
|
||||
|
||||
void NeoPixels_Task(void * pvParameters)
|
||||
{
|
||||
portBASE_TYPE xStatus;
|
||||
TickType_t xLastWakeTime;
|
||||
Animation_T * New_Animation;
|
||||
|
||||
xQueueNeoPixels = xQueueCreate(5, sizeof(NeoPixelsAction_T));
|
||||
NeoPixels_Semaphore = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(NeoPixels_Semaphore);
|
||||
|
||||
// TODO: Wait for NVM to be initialized and configurations read.
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
HW_NeoPixels_Init();
|
||||
NeoPixels_Set_Color_Range_On_All_Channels(0, CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL - 1, COLOR_BLACK);
|
||||
|
||||
// Initialize the xLastWakeTime variable with the current time.
|
||||
xLastWakeTime = xTaskGetTickCount();
|
||||
|
||||
while (true)
|
||||
{
|
||||
NeoPixelsAction_T action;
|
||||
|
||||
// Check for a new animation.
|
||||
xStatus = xQueueReceive(xQueueNeoPixels, &action, 0);
|
||||
|
||||
if (xStatus == pdPASS)
|
||||
{
|
||||
switch (action.ID)
|
||||
{
|
||||
case NEOPIXELS_ALL_OFF:
|
||||
default:
|
||||
New_Animation = &All_Off_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_ALL_ON:
|
||||
New_Animation = &All_On_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_TEST_PATTERN:
|
||||
New_Animation = &Test_Pattern_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_PLAY_SHOT_FIRED:
|
||||
New_Animation = &Shot_Fired_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_FLASHLIGHT_ON:
|
||||
New_Animation = &Flashlight_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_FLAMETHROWER:
|
||||
New_Animation = &Flamethrower_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_TAG_RECEIVED:
|
||||
New_Animation = &Tag_Received_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_TAGGED_OUT:
|
||||
New_Animation = &Tagged_Out_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_MENU:
|
||||
New_Animation = &Menu_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_HEALTH_REPORT:
|
||||
New_Animation = &Health_Report_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_BLE_RSSI:
|
||||
New_Animation = &BLE_RSSI_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_IDLE:
|
||||
New_Animation = &Idle_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_COUNTDOWN:
|
||||
New_Animation = &Countdown_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_TEAM_COLORS:
|
||||
New_Animation = &Team_Colors_Animation;
|
||||
break;
|
||||
|
||||
case NEOPIXELS_BLE_NEARBY:
|
||||
New_Animation = &BLE_Nearby_Animation;
|
||||
break;
|
||||
}
|
||||
|
||||
if (New_Animation != NULL)
|
||||
{
|
||||
New_Animation->Reset(action.Data);
|
||||
|
||||
if (action.Prominence == NEOPIXELS_FOREGROUND)
|
||||
{
|
||||
Current_Foreground_Animation = New_Animation;
|
||||
}
|
||||
if (action.Prominence == NEOPIXELS_BACKGROUND)
|
||||
{
|
||||
Current_Background_Animation = New_Animation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Current_Foreground_Animation != NULL)
|
||||
{
|
||||
AnimationStepResult_T result = Current_Foreground_Animation->NextStep();
|
||||
|
||||
if (result == ANIMATION_COMPLETE)
|
||||
{
|
||||
Current_Foreground_Animation = NULL;
|
||||
}
|
||||
}
|
||||
else if (Current_Background_Animation != NULL)
|
||||
{
|
||||
AnimationStepResult_T result = Current_Background_Animation->NextStep();
|
||||
|
||||
if (result == ANIMATION_COMPLETE)
|
||||
{
|
||||
Current_Background_Animation = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// The animation has conigured all the colors. Now send them to the NeoPixels.
|
||||
HW_NeoPixels_Publish();
|
||||
|
||||
vTaskDelayUntil(&xLastWakeTime, Animation_Delay);
|
||||
}
|
||||
}
|
211
NeoPixels/NeoPixels.h
Normal file
211
NeoPixels/NeoPixels.h
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief This file defines the interface to the NeoPixels used by the KTag system (SystemK).
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NEOPIXELS_H
|
||||
#define NEOPIXELS_H
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "sdkconfig.h"
|
||||
#endif // ESP_PLATFORM
|
||||
|
||||
#ifdef PSOC_PLATFORM
|
||||
#include "CONFIG.h"
|
||||
#endif // PSOC_PLATFORM
|
||||
|
||||
#include "Gamma.h"
|
||||
#include "Sine.h"
|
||||
#include "Animation.h"
|
||||
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
typedef enum
|
||||
{
|
||||
// The Lil' Bruv and the Little Boy BLuE only have one NeoPixel channel.
|
||||
NEOPIXEL_CHANNEL_BARREL = 0,
|
||||
NEOPIXEL_CHANNEL_RECEIVER = 0,
|
||||
NEOPIXEL_CHANNEL_DISPLAY = 0,
|
||||
NEOPIXEL_CHANNEL_EFFECTS = 0,
|
||||
NEOPIXEL_CHANNEL_ALL = 100,
|
||||
NEOPIXEL_CHANNEL_NONE = 200
|
||||
} NeoPixelsChannel_T;
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
typedef enum
|
||||
{
|
||||
// The 2020TPC and the 32ESPecial have four NeoPixel channels.
|
||||
NEOPIXEL_CHANNEL_BARREL = 0,
|
||||
NEOPIXEL_CHANNEL_RECEIVER = 1,
|
||||
NEOPIXEL_CHANNEL_DISPLAY = 2,
|
||||
NEOPIXEL_CHANNEL_EFFECTS = 3,
|
||||
NEOPIXEL_CHANNEL_ALL = 100,
|
||||
NEOPIXEL_CHANNEL_NONE = 200
|
||||
} NeoPixelsChannel_T;
|
||||
#else
|
||||
#error "Unsupported number of NeoPixel channels defined. Supported configurations are 1 and 4."
|
||||
#endif
|
||||
|
||||
#include "NeoPixel_HW_Interface.h"
|
||||
|
||||
#include "Animations/All_Off.h"
|
||||
#include "Animations/All_On.h"
|
||||
#include "Animations/BLE_Nearby.h"
|
||||
#include "Animations/BLE_RSSI.h"
|
||||
#include "Animations/Countdown.h"
|
||||
#include "Animations/Flamethrower.h"
|
||||
#include "Animations/Flashlight.h"
|
||||
#include "Animations/Health_Report.h"
|
||||
#include "Animations/Idle_Animation.h"
|
||||
#include "Animations/Menu_Animation.h"
|
||||
#include "Animations/Shot_Fired.h"
|
||||
#include "Animations/Tag_Received.h"
|
||||
#include "Animations/Tagged_Out.h"
|
||||
#include "Animations/Team_Colors.h"
|
||||
#include "Animations/Test_Pattern.h"
|
||||
|
||||
extern QueueHandle_t xQueueNeoPixels;
|
||||
extern TaskHandle_t NeoPixels_Task_Handle;
|
||||
extern SemaphoreHandle_t NeoPixels_Semaphore;
|
||||
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1)
|
||||
#include "Single_Channel_Helpers.h"
|
||||
#elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4)
|
||||
#include "Four_Channel_Helpers.h"
|
||||
#else
|
||||
#error "Unsupported number of NeoPixel channels defined. Supported configurations are 1 and 4."
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
COLOR_ORDER_RGB = 0,
|
||||
COLOR_ORDER_RBG = 1,
|
||||
COLOR_ORDER_GRB = 2,
|
||||
COLOR_ORDER_GBR = 3,
|
||||
COLOR_ORDER_BRG = 4,
|
||||
COLOR_ORDER_BGR = 5
|
||||
} ColorOrder_T;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DISPLAY_STYLE_DEFAULT = 0,
|
||||
DISPLAY_STYLE_SOLID = 1,
|
||||
DISPLAY_STYLE_ALTERNATE = 2,
|
||||
DISPLAY_STYLE_BLINK = 3,
|
||||
DISPLAY_STYLE_HEARTBEAT = 4,
|
||||
//-----------------------------------------------
|
||||
FIRST_DISPLAY_STYLE = DISPLAY_STYLE_DEFAULT,
|
||||
LAST_DISPLAY_STYLE = DISPLAY_STYLE_HEARTBEAT
|
||||
|
||||
} DisplayStyle_T;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NEOPIXELS_ALL_OFF,
|
||||
NEOPIXELS_ALL_ON,
|
||||
NEOPIXELS_TEST_PATTERN,
|
||||
NEOPIXELS_PLAY_SHOT_FIRED,
|
||||
NEOPIXELS_FLASHLIGHT_ON,
|
||||
NEOPIXELS_FLAMETHROWER,
|
||||
NEOPIXELS_TAG_RECEIVED,
|
||||
NEOPIXELS_TAGGED_OUT,
|
||||
NEOPIXELS_MENU,
|
||||
NEOPIXELS_HEALTH_REPORT,
|
||||
NEOPIXELS_BLE_RSSI,
|
||||
NEOPIXELS_IDLE,
|
||||
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
|
||||
} NeoPixelsActionID_T;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NEOPIXELS_FOREGROUND,
|
||||
NEOPIXELS_BACKGROUND,
|
||||
} NeoPixelsProminence_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NeoPixelsActionID_T ID;
|
||||
NeoPixelsProminence_T Prominence;
|
||||
void * Data;
|
||||
} NeoPixelsAction_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
color_t color;
|
||||
DisplayStyle_T style;
|
||||
} All_On_Data_T;
|
||||
|
||||
void NeoPixels_Task(void * pvParameters);
|
||||
|
||||
static inline __attribute__((always_inline)) void NeoPixels_Set_Color(NeoPixelsChannel_T channel, uint8_t position, color_t color)
|
||||
{
|
||||
HW_NeoPixels_Set_RGB(channel, position, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
}
|
||||
|
||||
static inline void NeoPixel_Set_Range(NeoPixelsChannel_T channel, uint8_t start, uint8_t end, uint8_t red, uint8_t green ,uint8_t blue)
|
||||
{
|
||||
for (uint_fast8_t position = start; position <= end; position++)
|
||||
{
|
||||
HW_NeoPixels_Set_RGB(channel, position, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline)) void NeoPixels_Set_Color_Range(NeoPixelsChannel_T channel, uint8_t start, uint8_t end, color_t color)
|
||||
{
|
||||
NeoPixel_Set_Range(channel, start, end, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline)) void NeoPixels_Set_Color_Range_On_All_Channels(uint8_t start, uint8_t end, color_t color)
|
||||
{
|
||||
#if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS > 1)
|
||||
NeoPixel_Set_Range(NEOPIXEL_CHANNEL_BARREL, start, end, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
NeoPixel_Set_Range(NEOPIXEL_CHANNEL_RECEIVER, start, end, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
NeoPixel_Set_Range(NEOPIXEL_CHANNEL_DISPLAY, start, end, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
NeoPixel_Set_Range(NEOPIXEL_CHANNEL_EFFECTS, start, end, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
#else // (CONFIG_KTAG_N_NEOPIXEL_CHANNELS > 1)
|
||||
NeoPixel_Set_Range(NEOPIXEL_CHANNEL_BARREL, start, end, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
#endif // (CONFIG_KTAG_N_NEOPIXEL_CHANNELS > 1)
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline)) DisplayStyle_T NeoPixels_ToDisplayStyle(void * value)
|
||||
{
|
||||
DisplayStyle_T result = FIRST_DISPLAY_STYLE;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
if (((DisplayStyle_T)value >= FIRST_DISPLAY_STYLE) && ((DisplayStyle_T)value <= LAST_DISPLAY_STYLE))
|
||||
{
|
||||
result = (DisplayStyle_T)value;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // NEOPIXELS_H
|
42
NeoPixels/Sine.c
Normal file
42
NeoPixels/Sine.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Sine.h"
|
||||
|
||||
const uint_fast8_t Sine8[] = {
|
||||
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9,
|
||||
10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
|
||||
37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
|
||||
79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124,
|
||||
128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
|
||||
176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
|
||||
218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
|
||||
245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
|
||||
255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
|
||||
245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
|
||||
218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
|
||||
176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
|
||||
128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82,
|
||||
79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40,
|
||||
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11,
|
||||
10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0};
|
28
NeoPixels/Sine.h
Normal file
28
NeoPixels/Sine.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 © 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SINE_H
|
||||
#define SINE_H
|
||||
|
||||
extern const uint_fast8_t Sine8[];
|
||||
|
||||
#endif // SINE_H
|
87
NeoPixels/Single_Channel_Helpers.h
Normal file
87
NeoPixels/Single_Channel_Helpers.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// '||' || '|| '||''|. TM
|
||||
// || ... || || || ... .. ... ... .... ...
|
||||
// || || || ||'''|. ||' '' || || '|. |
|
||||
// || || || || || || || || '|.|
|
||||
// .||.....| .||. .||. .||...|' .||. '|..'|. '|
|
||||
|
||||
|
||||
|
||||
//! Zero-based index of the Barrel Flash NeoPixel in the sequence of NeoPixels.
|
||||
#define BARREL_FLASH_PIXEL 0
|
||||
//! Zero-based index of the Heart NeoPixel in the sequence of NeoPixels.
|
||||
#define HEART_PIXEL 1
|
||||
//! Zero-based index of the Square NeoPixel in the sequence of NeoPixels.
|
||||
#define SQUARE_PIXEL 2
|
||||
//! Zero-based index of the Circle NeoPixel in the sequence of NeoPixels.
|
||||
#define CIRCLE_PIXEL 3
|
||||
//! Zero-based index of the Arrow NeoPixel in the sequence of NeoPixels.
|
||||
#define ARROW_PIXEL 4
|
||||
|
||||
//! Sets the color of the Barrel Flash NeoPixel, applying gamma correction.
|
||||
/*!
|
||||
* \note The diffused 5mm LEDs (from AdaFruit and Ali Express) for barrel flash have a different color order (RGB vs. GRB)!
|
||||
*/
|
||||
static inline __attribute__((always_inline)) void Set_Barrel_Flash(color_t color)
|
||||
{
|
||||
HW_NeoPixels_Set_RGB(NEOPIXEL_CHANNEL_BARREL, BARREL_FLASH_PIXEL, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]);
|
||||
}
|
||||
|
||||
//! Sets the color of the Heart NeoPixel, applying gamma correction.
|
||||
/*!
|
||||
* \note The [WS2812B NeoPixels](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf) use a GRB color order.
|
||||
*/
|
||||
static inline __attribute__((always_inline)) void Set_Heart(color_t color)
|
||||
{
|
||||
HW_NeoPixels_Set_RGB(NEOPIXEL_CHANNEL_BARREL, HEART_PIXEL, Gamma8[Green(color)], Gamma8[Red(color)], Gamma8[Blue(color)]);
|
||||
}
|
||||
|
||||
//! Sets the color of the Square NeoPixel, applying gamma correction.
|
||||
/*!
|
||||
* \note The [WS2812B NeoPixels](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf) use a GRB color order.
|
||||
*/
|
||||
static inline __attribute__((always_inline)) void Set_Square(color_t color)
|
||||
{
|
||||
HW_NeoPixels_Set_RGB(NEOPIXEL_CHANNEL_BARREL, SQUARE_PIXEL, Gamma8[Green(color)], Gamma8[Red(color)], Gamma8[Blue(color)]);
|
||||
}
|
||||
|
||||
//! Sets the color of the Circle NeoPixel, applying gamma correction.
|
||||
/*!
|
||||
* \note The [WS2812B NeoPixels](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf) use a GRB color order.
|
||||
*/
|
||||
static inline __attribute__((always_inline)) void Set_Circle(color_t color)
|
||||
{
|
||||
HW_NeoPixels_Set_RGB(NEOPIXEL_CHANNEL_BARREL, CIRCLE_PIXEL, Gamma8[Green(color)], Gamma8[Red(color)], Gamma8[Blue(color)]);
|
||||
}
|
||||
|
||||
//! Sets the color of the Arrow NeoPixel, applying gamma correction.
|
||||
/*!
|
||||
* \note The [WS2812B NeoPixels](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf) use a GRB color order.
|
||||
*/
|
||||
static inline __attribute__((always_inline)) void Set_Arrow(color_t color)
|
||||
{
|
||||
HW_NeoPixels_Set_RGB(NEOPIXEL_CHANNEL_BARREL, ARROW_PIXEL, Gamma8[Green(color)], Gamma8[Red(color)], Gamma8[Blue(color)]);
|
||||
}
|
325
Protocols/Dubuque.c
Normal file
325
Protocols/Dubuque.c
Normal file
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* Copyright © 2024-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 <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define DBQ_PURPLE_TEAM 0b00
|
||||
#define DBQ_RED_TEAM 0b01
|
||||
#define DBQ_BLUE_TEAM 0b10
|
||||
#define DBQ_WHITE_TEAM 0b11
|
||||
|
||||
#define DBQ_ZERO_MARK_DURATION_IN_us 400
|
||||
#define DBQ_ZERO_SPACE_DURATION_IN_us 800
|
||||
#define DBQ_ONE_MARK_DURATION_IN_us 800
|
||||
#define DBQ_ONE_SPACE_DURATION_IN_us 400
|
||||
#define DBQ_TOLERANCE_IN_us 199
|
||||
|
||||
#define DBQ_PURPLE_TX_GAP_IN_us 2600
|
||||
#define DBQ_RED_TX_GAP_IN_us 5000
|
||||
#define DBQ_BLUE_TX_GAP_IN_us 7400
|
||||
#define DBQ_WHITE_TX_GAP_IN_us 9800
|
||||
|
||||
#define MIN_ZERO_MARK_IN_us (DBQ_ZERO_MARK_DURATION_IN_us - DBQ_TOLERANCE_IN_us)
|
||||
#define MAX_ZERO_MARK_IN_us (DBQ_ZERO_MARK_DURATION_IN_us + DBQ_TOLERANCE_IN_us)
|
||||
#define MIN_ZERO_SPACE_IN_us (DBQ_ZERO_SPACE_DURATION_IN_us - DBQ_TOLERANCE_IN_us)
|
||||
#define MAX_ZERO_SPACE_IN_us (DBQ_ZERO_SPACE_DURATION_IN_us + DBQ_TOLERANCE_IN_us)
|
||||
#define MIN_ONE_MARK_IN_us (DBQ_ONE_MARK_DURATION_IN_us - DBQ_TOLERANCE_IN_us)
|
||||
#define MAX_ONE_MARK_IN_us (DBQ_ONE_MARK_DURATION_IN_us + DBQ_TOLERANCE_IN_us)
|
||||
#define MIN_ONE_SPACE_IN_us (DBQ_ONE_SPACE_DURATION_IN_us - DBQ_TOLERANCE_IN_us)
|
||||
#define MAX_ONE_SPACE_IN_us (DBQ_ONE_SPACE_DURATION_IN_us + DBQ_TOLERANCE_IN_us)
|
||||
|
||||
static const TimedBit_T DBQ_ZERO_MARK = {.symbol = MARK, .duration = DBQ_ZERO_MARK_DURATION_IN_us};
|
||||
static const TimedBit_T DBQ_ONE_MARK = {.symbol = MARK, .duration = DBQ_ONE_MARK_DURATION_IN_us};
|
||||
static const TimedBit_T DBQ_ZERO_SPACE = {.symbol = SPACE, .duration = DBQ_ZERO_SPACE_DURATION_IN_us};
|
||||
static const TimedBit_T DBQ_ONE_SPACE = {.symbol = SPACE, .duration = DBQ_ONE_SPACE_DURATION_IN_us};
|
||||
static const TimedBit_T DBQ_PURPLE_GAP = {.symbol = SPACE, .duration = DBQ_ZERO_SPACE_DURATION_IN_us + DBQ_PURPLE_TX_GAP_IN_us};
|
||||
static const TimedBit_T DBQ_RED_GAP = {.symbol = SPACE, .duration = DBQ_ONE_SPACE_DURATION_IN_us + DBQ_RED_TX_GAP_IN_us};
|
||||
static const TimedBit_T DBQ_BLUE_GAP = {.symbol = SPACE, .duration = DBQ_ZERO_SPACE_DURATION_IN_us + DBQ_BLUE_TX_GAP_IN_us};
|
||||
static const TimedBit_T DBQ_WHITE_GAP = {.symbol = SPACE, .duration = DBQ_ONE_SPACE_DURATION_IN_us + DBQ_WHITE_TX_GAP_IN_us};
|
||||
static const TimedBit_T DBQ_END = {.symbol = SPACE, .duration = LAST_PULSE};
|
||||
|
||||
static TimedPulseTrain_T Tag_Send_Buffer;
|
||||
static DecodedPacket_T DBQ_Tag_Received_Buffers[3] =
|
||||
{
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE}
|
||||
};
|
||||
|
||||
static uint_fast16_t Team_To_Ignore = DBQ_WHITE_TEAM;
|
||||
|
||||
static inline DecodedPacket_T *Get_Tag_Packet_Buffer(void)
|
||||
{
|
||||
if (DBQ_Tag_Received_Buffers[0].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &DBQ_Tag_Received_Buffers[0];
|
||||
}
|
||||
else if (DBQ_Tag_Received_Buffers[1].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &DBQ_Tag_Received_Buffers[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just use it.
|
||||
return &DBQ_Tag_Received_Buffers[2];
|
||||
}
|
||||
}
|
||||
|
||||
TimedPulseTrain_T *DBQ_EncodePacket(TagPacket_T *packet)
|
||||
{
|
||||
if (packet->team_ID == DBQ_PURPLE_TEAM)
|
||||
{
|
||||
Tag_Send_Buffer.bitstream[0] = DBQ_ZERO_MARK;
|
||||
Tag_Send_Buffer.bitstream[1] = DBQ_ZERO_SPACE;
|
||||
Tag_Send_Buffer.bitstream[2] = DBQ_ZERO_MARK;
|
||||
Tag_Send_Buffer.bitstream[3] = DBQ_PURPLE_GAP;
|
||||
Tag_Send_Buffer.bitstream[4] = DBQ_ZERO_MARK;
|
||||
Tag_Send_Buffer.bitstream[5] = DBQ_ZERO_SPACE;
|
||||
Tag_Send_Buffer.bitstream[6] = DBQ_ZERO_MARK;
|
||||
Tag_Send_Buffer.bitstream[7] = DBQ_ZERO_SPACE;
|
||||
Tag_Send_Buffer.bitstream[8] = DBQ_END;
|
||||
}
|
||||
else if (packet->team_ID == DBQ_RED_TEAM)
|
||||
{
|
||||
Team_To_Ignore = DBQ_RED_TEAM;
|
||||
|
||||
Tag_Send_Buffer.bitstream[0] = DBQ_ZERO_MARK;
|
||||
Tag_Send_Buffer.bitstream[1] = DBQ_ZERO_SPACE;
|
||||
Tag_Send_Buffer.bitstream[2] = DBQ_ONE_MARK;
|
||||
Tag_Send_Buffer.bitstream[3] = DBQ_RED_GAP;
|
||||
Tag_Send_Buffer.bitstream[4] = DBQ_ZERO_MARK;
|
||||
Tag_Send_Buffer.bitstream[5] = DBQ_ZERO_SPACE;
|
||||
Tag_Send_Buffer.bitstream[6] = DBQ_ONE_MARK;
|
||||
Tag_Send_Buffer.bitstream[7] = DBQ_ONE_SPACE;
|
||||
Tag_Send_Buffer.bitstream[8] = DBQ_END;
|
||||
}
|
||||
else if (packet->team_ID == DBQ_BLUE_TEAM)
|
||||
{
|
||||
Team_To_Ignore = DBQ_BLUE_TEAM;
|
||||
|
||||
Tag_Send_Buffer.bitstream[0] = DBQ_ONE_MARK;
|
||||
Tag_Send_Buffer.bitstream[1] = DBQ_ONE_SPACE;
|
||||
Tag_Send_Buffer.bitstream[2] = DBQ_ZERO_MARK;
|
||||
Tag_Send_Buffer.bitstream[3] = DBQ_BLUE_GAP;
|
||||
Tag_Send_Buffer.bitstream[4] = DBQ_ONE_MARK;
|
||||
Tag_Send_Buffer.bitstream[5] = DBQ_ONE_SPACE;
|
||||
Tag_Send_Buffer.bitstream[6] = DBQ_ZERO_MARK;
|
||||
Tag_Send_Buffer.bitstream[7] = DBQ_ZERO_SPACE;
|
||||
Tag_Send_Buffer.bitstream[8] = DBQ_END;
|
||||
}
|
||||
else // if (packet->team_ID == DBQ_WHITE_TEAM)
|
||||
{
|
||||
Tag_Send_Buffer.bitstream[0] = DBQ_ONE_MARK;
|
||||
Tag_Send_Buffer.bitstream[1] = DBQ_ONE_SPACE;
|
||||
Tag_Send_Buffer.bitstream[2] = DBQ_ONE_MARK;
|
||||
Tag_Send_Buffer.bitstream[3] = DBQ_WHITE_GAP;
|
||||
Tag_Send_Buffer.bitstream[4] = DBQ_ONE_MARK;
|
||||
Tag_Send_Buffer.bitstream[5] = DBQ_ONE_SPACE;
|
||||
Tag_Send_Buffer.bitstream[6] = DBQ_ONE_MARK;
|
||||
Tag_Send_Buffer.bitstream[7] = DBQ_ONE_SPACE;
|
||||
Tag_Send_Buffer.bitstream[8] = DBQ_END;
|
||||
}
|
||||
|
||||
Tag_Send_Buffer.count = 8;
|
||||
|
||||
return &Tag_Send_Buffer;
|
||||
}
|
||||
|
||||
DecodedPacket_T *DBQ_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
||||
{
|
||||
uint_fast8_t decoded_data = UINT_FAST8_MAX;
|
||||
uint_fast8_t consecutive_bits = 0;
|
||||
uint_fast8_t ignored_data = UINT_FAST8_MAX;
|
||||
|
||||
// Special handling for the purple team.
|
||||
uint_fast16_t collisions = 0;
|
||||
uint_fast16_t purple_completions = 0;
|
||||
|
||||
// Loop through all the pulses, looking for a match.
|
||||
for (uint8_t n = 0; n < packet->count; n += 2)
|
||||
{
|
||||
// Even pulses are marks; odd pulses are spaces.
|
||||
if (consecutive_bits == 0)
|
||||
{
|
||||
// Perform full checking on the first bit.
|
||||
if ((packet->bitstream[n].duration > MIN_ONE_MARK_IN_us) && (packet->bitstream[n].duration < MAX_ONE_MARK_IN_us) &&
|
||||
(packet->bitstream[n + 1].duration > MIN_ONE_SPACE_IN_us) && (packet->bitstream[n + 1].duration < MAX_ONE_SPACE_IN_us))
|
||||
{
|
||||
// This is a "one".
|
||||
decoded_data = 2;
|
||||
consecutive_bits = 1;
|
||||
}
|
||||
else if ((packet->bitstream[n].duration > MIN_ZERO_MARK_IN_us) && (packet->bitstream[n].duration < MAX_ZERO_MARK_IN_us) &&
|
||||
(packet->bitstream[n + 1].duration > MIN_ZERO_SPACE_IN_us) && (packet->bitstream[n + 1].duration < MAX_ZERO_SPACE_IN_us))
|
||||
{
|
||||
// This is a "zero".
|
||||
decoded_data = 0;
|
||||
consecutive_bits = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This mark/space combination is neither a zero or a one; continue.
|
||||
collisions++;
|
||||
}
|
||||
}
|
||||
else // consecutive_bits > 0
|
||||
{
|
||||
// One the second bit, full check the mark, and make sure the space is long enough.
|
||||
if ((packet->bitstream[n].duration > MIN_ONE_MARK_IN_us) && (packet->bitstream[n].duration < MAX_ONE_MARK_IN_us) &&
|
||||
((packet->bitstream[n + 1].duration > MIN_ONE_SPACE_IN_us) || (packet->bitstream[n + 1].duration == LAST_PULSE)))
|
||||
{
|
||||
// This is a "one".
|
||||
decoded_data += 1;
|
||||
|
||||
if (decoded_data == Team_To_Ignore)
|
||||
{
|
||||
// We received a packet we should ignore. Remember this, and keep looking.
|
||||
ignored_data = decoded_data;
|
||||
decoded_data = UINT_FAST8_MAX;
|
||||
consecutive_bits = 0;
|
||||
}
|
||||
else if (decoded_data == DBQ_PURPLE_TEAM)
|
||||
{
|
||||
// We received a purple packet. Remember this, and keep looking.
|
||||
purple_completions++;
|
||||
decoded_data = UINT_FAST8_MAX;
|
||||
consecutive_bits = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((packet->bitstream[n].duration > MIN_ZERO_MARK_IN_us) && (packet->bitstream[n].duration < MAX_ZERO_MARK_IN_us) &&
|
||||
((packet->bitstream[n + 1].duration > MIN_ZERO_SPACE_IN_us) || (packet->bitstream[n + 1].duration == LAST_PULSE)))
|
||||
{
|
||||
// This is a "zero".
|
||||
decoded_data += 0;
|
||||
|
||||
if (decoded_data == Team_To_Ignore)
|
||||
{
|
||||
// We received a packet we should ignore. Remember this, and keep looking.
|
||||
ignored_data = decoded_data;
|
||||
decoded_data = UINT_FAST8_MAX;
|
||||
consecutive_bits = 0;
|
||||
}
|
||||
else if (decoded_data == DBQ_PURPLE_TEAM)
|
||||
{
|
||||
// We received a purple packet. Remember this, and keep looking.
|
||||
purple_completions++;
|
||||
decoded_data = UINT_FAST8_MAX;
|
||||
consecutive_bits = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This mark/space combination is neither a zero or a one; reset.
|
||||
collisions++;
|
||||
decoded_data = UINT_FAST8_MAX;
|
||||
consecutive_bits = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we ignored the only data we received, restore it here.
|
||||
if ((ignored_data != UINT_FAST8_MAX) && (decoded_data == UINT_FAST8_MAX))
|
||||
{
|
||||
decoded_data = ignored_data;
|
||||
}
|
||||
|
||||
if (purple_completions > 0)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = DUBUQUE_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = DBQ_PURPLE_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10 * (purple_completions + collisions);
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_PURPLE;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
else if (decoded_data == DBQ_RED_TEAM)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = DUBUQUE_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = DBQ_RED_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_RED;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
else if (decoded_data == DBQ_BLUE_TEAM)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = DUBUQUE_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = DBQ_BLUE_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_BLUE;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
else if (decoded_data == DBQ_WHITE_TEAM)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = DUBUQUE_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = DBQ_WHITE_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_WHITE;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
color_t DBQ_GetTeamColor(uint8_t team_ID)
|
||||
{
|
||||
color_t result = COLOR_PURPLE;
|
||||
|
||||
if (team_ID == DBQ_RED_TEAM)
|
||||
{
|
||||
result = COLOR_RED;
|
||||
}
|
||||
else if (team_ID == DBQ_BLUE_TEAM)
|
||||
{
|
||||
result = COLOR_BLUE;
|
||||
}
|
||||
else if (team_ID == DBQ_WHITE_TEAM)
|
||||
{
|
||||
result = COLOR_WHITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
39
Protocols/Dubuque.h
Normal file
39
Protocols/Dubuque.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* Copyright © 2024-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 DUBUQUE_H
|
||||
#define DUBUQUE_H
|
||||
|
||||
/** \file
|
||||
* \brief This is the Dubuque protocol.
|
||||
*
|
||||
* \note The Dubuque protocol uses a 38kHz carrier for On-Off Keying.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
TimedPulseTrain_T *DBQ_EncodePacket(TagPacket_T *packet);
|
||||
DecodedPacket_T *DBQ_MaybeDecodePacket(TimedPulseTrain_T *packet);
|
||||
color_t DBQ_GetTeamColor(uint8_t team_ID);
|
||||
|
||||
#endif // DUBUQUE_H
|
214
Protocols/Dynasty.c
Executable file
214
Protocols/Dynasty.c
Executable file
|
@ -0,0 +1,214 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define DYNASTY_BLUE_TEAM 0x01
|
||||
#define DYNASTY_RED_TEAM 0x02
|
||||
#define DYNASTY_GREEN_TEAM 0x03
|
||||
#define DYNASTY_WHITE_TEAM 0x04
|
||||
|
||||
#define DYNASTY_HEADER_MARK_DURATION_IN_us 1700
|
||||
#define DYNASTY_ZERO_DURATION_IN_us 450
|
||||
#define DYNASTY_ONE_DURATION_IN_us 800
|
||||
#define DYNASTY_TOLERANCE_IN_us 150
|
||||
|
||||
#define MIN_ZERO_IN_us (DYNASTY_ZERO_DURATION_IN_us - DYNASTY_TOLERANCE_IN_us)
|
||||
#define MAX_ZERO_IN_us (DYNASTY_ZERO_DURATION_IN_us + DYNASTY_TOLERANCE_IN_us)
|
||||
#define MIN_ONE_IN_us (DYNASTY_ONE_DURATION_IN_us - DYNASTY_TOLERANCE_IN_us)
|
||||
#define MAX_ONE_IN_us (DYNASTY_ONE_DURATION_IN_us + DYNASTY_TOLERANCE_IN_us)
|
||||
|
||||
static const TimedBit_T DYNASTY_HEADER_MARK = {.symbol = MARK, .duration = DYNASTY_HEADER_MARK_DURATION_IN_us};
|
||||
static const TimedBit_T DYNASTY_ZERO_SPACE = {.symbol = SPACE, .duration = DYNASTY_ZERO_DURATION_IN_us};
|
||||
static const TimedBit_T DYNASTY_ONE_SPACE = {.symbol = SPACE, .duration = DYNASTY_ONE_DURATION_IN_us};
|
||||
static const TimedBit_T DYNASTY_ZERO_MARK = {.symbol = MARK, .duration = DYNASTY_ZERO_DURATION_IN_us};
|
||||
static const TimedBit_T DYNASTY_ONE_MARK = {.symbol = MARK, .duration = DYNASTY_ONE_DURATION_IN_us};
|
||||
static const TimedBit_T DYNASTY_END = {.symbol = SPACE, .duration = LAST_PULSE};
|
||||
|
||||
static TimedPulseTrain_T Tag_Send_Buffer;
|
||||
static DecodedPacket_T Dynasty_Tag_Received_Buffers[3] =
|
||||
{
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE}};
|
||||
|
||||
static inline DecodedPacket_T *Get_Tag_Packet_Buffer(void)
|
||||
{
|
||||
if (Dynasty_Tag_Received_Buffers[0].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Dynasty_Tag_Received_Buffers[0];
|
||||
}
|
||||
else if (Dynasty_Tag_Received_Buffers[1].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Dynasty_Tag_Received_Buffers[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just use it.
|
||||
return &Dynasty_Tag_Received_Buffers[2];
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t Calculate_Checksum(uint32_t data)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
|
||||
// uint8_t byte_1 = (uint8_t)((data >> 8) & 0xFF);
|
||||
// uint8_t byte_0 = (uint8_t)(data & 0xFF);
|
||||
|
||||
// TODO: Finish implementing the Dynasty checksum.
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void PackPulseTrain(TimedPulseTrain_T *pulsetrain, uint32_t data)
|
||||
{
|
||||
pulsetrain->bitstream[0] = DYNASTY_HEADER_MARK;
|
||||
|
||||
for (uint8_t n = 0; n < 16; n++)
|
||||
{
|
||||
if ((data & 0x0001) == 0x0001)
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = DYNASTY_ONE_SPACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = DYNASTY_ZERO_SPACE;
|
||||
}
|
||||
data = data >> 1;
|
||||
if ((data & 0x0001) == 0x0001)
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = DYNASTY_ONE_MARK;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = DYNASTY_ZERO_MARK;
|
||||
}
|
||||
data = data >> 1;
|
||||
}
|
||||
|
||||
// TODO: Append the Dynasty checksum.
|
||||
|
||||
pulsetrain->bitstream[(2 * 20) - 1] = DYNASTY_END;
|
||||
}
|
||||
|
||||
TimedPulseTrain_T *DYNASTY_EncodePacket(TagPacket_T *packet)
|
||||
{
|
||||
uint16_t packed_data = 0;
|
||||
|
||||
PackPulseTrain(&Tag_Send_Buffer, packed_data);
|
||||
|
||||
return &Tag_Send_Buffer;
|
||||
}
|
||||
|
||||
DecodedPacket_T *DYNASTY_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
||||
{
|
||||
// Some implementations (ESP32) do not report odd numbers of pulses.
|
||||
if ((packet->count != 41) && (packet->count != 42))
|
||||
{
|
||||
// Fail fast!
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert pulse durations to bits.
|
||||
// packet->bitstream[0] is the header mark--ignore it.
|
||||
|
||||
uint32_t data = 0;
|
||||
for (uint8_t n = 0; n < 32; n++)
|
||||
{
|
||||
if ((packet->bitstream[n + 1].duration > MIN_ONE_IN_us) && (packet->bitstream[n + 1].duration < MAX_ONE_IN_us))
|
||||
{
|
||||
// One
|
||||
data |= ((uint32_t)1 << (31 - n));
|
||||
}
|
||||
else if ((packet->bitstream[n + 1].duration > MIN_ZERO_IN_us) && (packet->bitstream[n + 1].duration < MAX_ZERO_IN_us))
|
||||
{
|
||||
// Zero--nothing to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
// This mark is neither a zero or a one; abort.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t checksum = 0;
|
||||
for (uint8_t n = 0; n < 8; n++)
|
||||
{
|
||||
if ((packet->bitstream[n + 33].duration > MIN_ONE_IN_us) && (packet->bitstream[n + 33].duration < MAX_ONE_IN_us))
|
||||
{
|
||||
// One
|
||||
checksum |= ((uint8_t)1 << (7 - n));
|
||||
}
|
||||
else if ((packet->bitstream[n + 33].duration > MIN_ZERO_IN_us) && (packet->bitstream[n + 33].duration < MAX_ZERO_IN_us))
|
||||
{
|
||||
// Zero--nothing to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
// This mark is neither a zero or a one; abort.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = DYNASTY_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = DYNASTY_RED_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_RED;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
}
|
||||
|
||||
color_t DYNASTY_GetTeamColor(uint8_t team_ID)
|
||||
{
|
||||
color_t result = COLOR_RED;
|
||||
|
||||
if (team_ID == DYNASTY_RED_TEAM)
|
||||
{
|
||||
result = COLOR_RED;
|
||||
}
|
||||
else if (team_ID == DYNASTY_BLUE_TEAM)
|
||||
{
|
||||
result = COLOR_BLUE;
|
||||
}
|
||||
else if (team_ID == DYNASTY_GREEN_TEAM)
|
||||
{
|
||||
result = COLOR_GREEN;
|
||||
}
|
||||
else if (team_ID == DYNASTY_WHITE_TEAM)
|
||||
{
|
||||
result = COLOR_WHITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
44
Protocols/Dynasty.h
Executable file
44
Protocols/Dynasty.h
Executable file
|
@ -0,0 +1,44 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DYNASTY_H
|
||||
#define DYNASTY_H
|
||||
|
||||
/** \file
|
||||
* \brief This is the Dynasty protocol.
|
||||
*
|
||||
* \note These devices use a 38kHz carrier. The modulation scheme is a little more complicated
|
||||
* than on-off keying: the width of each mark or space represents one bit of data. This
|
||||
* results in a more time-efficient data packet.
|
||||
*
|
||||
* https://dynastytoys.co/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
TimedPulseTrain_T *DYNASTY_EncodePacket(TagPacket_T *packet);
|
||||
DecodedPacket_T *DYNASTY_MaybeDecodePacket(TimedPulseTrain_T *packet);
|
||||
color_t DYNASTY_GetTeamColor(uint8_t team_ID);
|
||||
|
||||
#endif // DYNASTY_H
|
212
Protocols/Laser_X.c
Executable file
212
Protocols/Laser_X.c
Executable file
|
@ -0,0 +1,212 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define LASER_X_PURPLE_TEAM 0b00
|
||||
#define LASER_X_RED_TEAM 0b01
|
||||
#define LASER_X_BLUE_TEAM 0b10
|
||||
|
||||
#define RED_TEAM_PACKET_DATA 0x4A
|
||||
#define BLUE_TEAM_PACKET_DATA 0x8A
|
||||
#define PURPLE_TEAM_PACKET_DATA 0xCA
|
||||
|
||||
#define LASER_X_HEADER_MARK_DURATION_IN_us 6500
|
||||
#define LASER_X_SPACE_DURATION_IN_us 500
|
||||
#define LASER_X_MARK_ZERO_DURATION_IN_us 500
|
||||
#define LASER_X_MARK_ONE_DURATION_IN_us 1500
|
||||
#define LASER_X_TOLERANCE_IN_us 450
|
||||
|
||||
#define MIN_MARK_ZERO_IN_us (LASER_X_MARK_ZERO_DURATION_IN_us - LASER_X_TOLERANCE_IN_us)
|
||||
#define MAX_MARK_ZERO_IN_us (LASER_X_MARK_ZERO_DURATION_IN_us + LASER_X_TOLERANCE_IN_us)
|
||||
#define MIN_MARK_ONE_IN_us (LASER_X_MARK_ONE_DURATION_IN_us - LASER_X_TOLERANCE_IN_us)
|
||||
#define MAX_MARK_ONE_IN_us (LASER_X_MARK_ONE_DURATION_IN_us + LASER_X_TOLERANCE_IN_us)
|
||||
|
||||
static const TimedBit_T LASER_X_HEADER_MARK = {.symbol = MARK, .duration = LASER_X_HEADER_MARK_DURATION_IN_us};
|
||||
static const TimedBit_T LASER_X_SPACE = {.symbol = SPACE, .duration = LASER_X_SPACE_DURATION_IN_us};
|
||||
static const TimedBit_T LASER_X_ONE = {.symbol = MARK, .duration = LASER_X_MARK_ONE_DURATION_IN_us};
|
||||
static const TimedBit_T LASER_X_ZERO = {.symbol = MARK, .duration = LASER_X_MARK_ZERO_DURATION_IN_us};
|
||||
static const TimedBit_T LASER_X_END = {.symbol = SPACE, .duration = LAST_PULSE};
|
||||
|
||||
static TimedPulseTrain_T Tag_Send_Buffer;
|
||||
static DecodedPacket_T Laser_X_Tag_Received_Buffers[3] =
|
||||
{
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE}};
|
||||
|
||||
static inline DecodedPacket_T *Get_Tag_Packet_Buffer(void)
|
||||
{
|
||||
if (Laser_X_Tag_Received_Buffers[0].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Laser_X_Tag_Received_Buffers[0];
|
||||
}
|
||||
else if (Laser_X_Tag_Received_Buffers[1].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Laser_X_Tag_Received_Buffers[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just use it.
|
||||
return &Laser_X_Tag_Received_Buffers[2];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void PackPulseTrain(TimedPulseTrain_T *pulsetrain, uint8_t data)
|
||||
{
|
||||
pulsetrain->bitstream[0] = LASER_X_HEADER_MARK;
|
||||
pulsetrain->bitstream[1] = LASER_X_SPACE;
|
||||
|
||||
for (uint8_t n = 0; n < 8; n++)
|
||||
{
|
||||
if ((data & 0x01) == 0x01)
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = LASER_X_ONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = LASER_X_ZERO;
|
||||
}
|
||||
pulsetrain->bitstream[2 + (n * 2) + 1] = LASER_X_SPACE;
|
||||
data = data >> 1;
|
||||
}
|
||||
|
||||
pulsetrain->bitstream[17] = LASER_X_END;
|
||||
pulsetrain->count = 17;
|
||||
}
|
||||
|
||||
TimedPulseTrain_T *LASER_X_EncodePacket(TagPacket_T *packet)
|
||||
{
|
||||
uint16_t packed_data = 0;
|
||||
|
||||
if (packet->team_ID == LASER_X_RED_TEAM)
|
||||
{
|
||||
packed_data = RED_TEAM_PACKET_DATA;
|
||||
}
|
||||
else if (packet->team_ID == LASER_X_BLUE_TEAM)
|
||||
{
|
||||
packed_data = BLUE_TEAM_PACKET_DATA;
|
||||
}
|
||||
else // LASER_X_PURPLE_TEAM
|
||||
{
|
||||
packed_data = PURPLE_TEAM_PACKET_DATA;
|
||||
}
|
||||
|
||||
PackPulseTrain(&Tag_Send_Buffer, packed_data);
|
||||
|
||||
return &Tag_Send_Buffer;
|
||||
}
|
||||
|
||||
DecodedPacket_T *LASER_X_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
||||
{
|
||||
// Some implementations (ESP32) do not report odd numbers of pulses.
|
||||
if ((packet->count != 17) && (packet->count != 18))
|
||||
{
|
||||
// Fail fast!
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert pulse durations to bits.
|
||||
// packet->bitstream[0] is the header mark--ignore it.
|
||||
// packet->bitstream[1] is the header space--ignore it also.
|
||||
|
||||
uint8_t data = 0;
|
||||
for (uint8_t n = 0; n < 8; n++)
|
||||
{
|
||||
// Even pulses are marks; odd pulses are spaces.
|
||||
if ((packet->bitstream[2 + (n * 2)].duration > MIN_MARK_ONE_IN_us) && (packet->bitstream[2 + (n * 2)].duration < MAX_MARK_ONE_IN_us))
|
||||
{
|
||||
// One
|
||||
data |= ((uint8_t)1 << n);
|
||||
}
|
||||
else if ((packet->bitstream[2 + (n * 2)].duration > MIN_MARK_ZERO_IN_us) && (packet->bitstream[2 + (n * 2)].duration < MAX_MARK_ZERO_IN_us))
|
||||
{
|
||||
// Zero--nothing to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
// This mark is neither a zero or a one; abort.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (data == RED_TEAM_PACKET_DATA)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = LASER_X_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = LASER_X_RED_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_RED;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
else if (data == BLUE_TEAM_PACKET_DATA)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = LASER_X_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = LASER_X_BLUE_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_BLUE;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
else if (data == PURPLE_TEAM_PACKET_DATA)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = LASER_X_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = LASER_X_PURPLE_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_PURPLE;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
color_t LASER_X_GetTeamColor(uint8_t team_ID)
|
||||
{
|
||||
color_t result = COLOR_PURPLE;
|
||||
|
||||
if (team_ID == LASER_X_RED_TEAM)
|
||||
{
|
||||
result = COLOR_RED;
|
||||
}
|
||||
else if (team_ID == LASER_X_BLUE_TEAM)
|
||||
{
|
||||
result = COLOR_BLUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
42
Protocols/Laser_X.h
Executable file
42
Protocols/Laser_X.h
Executable file
|
@ -0,0 +1,42 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LASER_X_H
|
||||
#define LASER_X_H
|
||||
|
||||
/** \file
|
||||
* \brief This is the Laser X protocol.
|
||||
*
|
||||
* \note These devices use 38kHz carrier for On-Off Keying.
|
||||
*
|
||||
* https://www.getlaserx.com/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
TimedPulseTrain_T *LASER_X_EncodePacket(TagPacket_T *packet);
|
||||
DecodedPacket_T *LASER_X_MaybeDecodePacket(TimedPulseTrain_T *packet);
|
||||
color_t LASER_X_GetTeamColor(uint8_t team_ID);
|
||||
|
||||
#endif // LASER_X_H
|
195
Protocols/Miles_Tag_II.c
Executable file
195
Protocols/Miles_Tag_II.c
Executable file
|
@ -0,0 +1,195 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define MILES_TAG_RED_TEAM 0b00
|
||||
#define MILES_TAG_BLUE_TEAM 0b01
|
||||
#define MILES_TAG_YELLOW_TEAM 0b10
|
||||
#define MILES_TAG_GREEN_TEAM 0b11
|
||||
|
||||
#define MILES_TAG_II_HEADER_PULSE_DURATION_IN_us 2400
|
||||
#define MILES_TAG_II_SPACE_DURATION_IN_us 600
|
||||
#define MILES_TAG_II_MARK_ZERO_DURATION_IN_us 600
|
||||
#define MILES_TAG_II_MARK_ONE_DURATION_IN_us 1200
|
||||
#define MILES_TAG_II_TOLERANCE_IN_us 300
|
||||
|
||||
#define MIN_SPACE_IN_us (MILES_TAG_II_SPACE_DURATION_IN_us - MILES_TAG_II_TOLERANCE_IN_us)
|
||||
#define MAX_SPACE_IN_us (MILES_TAG_II_SPACE_DURATION_IN_us + MILES_TAG_II_TOLERANCE_IN_us)
|
||||
#define MIN_MARK_ZERO_IN_us (MILES_TAG_II_MARK_ZERO_DURATION_IN_us - MILES_TAG_II_TOLERANCE_IN_us)
|
||||
#define MAX_MARK_ZERO_IN_us (MILES_TAG_II_MARK_ZERO_DURATION_IN_us + MILES_TAG_II_TOLERANCE_IN_us)
|
||||
#define MIN_MARK_ONE_IN_us (MILES_TAG_II_MARK_ONE_DURATION_IN_us - MILES_TAG_II_TOLERANCE_IN_us)
|
||||
#define MAX_MARK_ONE_IN_us (MILES_TAG_II_MARK_ONE_DURATION_IN_us + MILES_TAG_II_TOLERANCE_IN_us)
|
||||
|
||||
static const TimedBit_T MILES_TAG_HEADER = {.symbol = MARK, .duration = MILES_TAG_II_HEADER_PULSE_DURATION_IN_us};
|
||||
static const TimedBit_T MILES_TAG_SPACE = {.symbol = SPACE, .duration = MILES_TAG_II_SPACE_DURATION_IN_us};
|
||||
static const TimedBit_T MILES_TAG_ONE = {.symbol = MARK, .duration = MILES_TAG_II_MARK_ONE_DURATION_IN_us};
|
||||
static const TimedBit_T MILES_TAG_ZERO = {.symbol = MARK, .duration = MILES_TAG_II_MARK_ZERO_DURATION_IN_us};
|
||||
static const TimedBit_T MILES_TAG_END = {.symbol = SPACE, .duration = LAST_PULSE};
|
||||
|
||||
static TimedPulseTrain_T Tag_Send_Buffer;
|
||||
static DecodedPacket_T Miles_Tag_II_Tag_Received_Buffers[3] =
|
||||
{
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE}};
|
||||
|
||||
static inline DecodedPacket_T *Get_Tag_Packet_Buffer(void)
|
||||
{
|
||||
if (Miles_Tag_II_Tag_Received_Buffers[0].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Miles_Tag_II_Tag_Received_Buffers[0];
|
||||
}
|
||||
else if (Miles_Tag_II_Tag_Received_Buffers[1].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Miles_Tag_II_Tag_Received_Buffers[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just use it.
|
||||
return &Miles_Tag_II_Tag_Received_Buffers[2];
|
||||
}
|
||||
}
|
||||
|
||||
static const uint16_t Miles_Tag_II_Damage[] = {1, 2, 4, 5, 7, 10, 15, 17, 20, 25, 30, 35, 40, 50, 75, 100};
|
||||
static const color_t Miles_Tag_II_Team_Colors[] = {COLOR_RED, COLOR_BLUE, COLOR_YELLOW, COLOR_GREEN};
|
||||
|
||||
static inline uint8_t Encode_Damage(uint16_t damage)
|
||||
{
|
||||
uint8_t encoded_damage;
|
||||
|
||||
for (encoded_damage = 0; encoded_damage < 16; encoded_damage++)
|
||||
{
|
||||
if (damage <= Miles_Tag_II_Damage[encoded_damage])
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return encoded_damage;
|
||||
}
|
||||
|
||||
// Use the 14 least-significant bits.
|
||||
static inline void PackPulseTrain(TimedPulseTrain_T *pulsetrain, uint16_t data)
|
||||
{
|
||||
pulsetrain->bitstream[0] = MILES_TAG_HEADER;
|
||||
pulsetrain->bitstream[1] = MILES_TAG_SPACE;
|
||||
|
||||
for (uint8_t n = 0; n < 14; n++)
|
||||
{
|
||||
if ((data & 0x01) == 0x01)
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = MILES_TAG_ONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = MILES_TAG_ZERO;
|
||||
}
|
||||
pulsetrain->bitstream[2 + (n * 2) + 1] = MILES_TAG_SPACE;
|
||||
data = data >> 1;
|
||||
}
|
||||
|
||||
pulsetrain->bitstream[30] = MILES_TAG_END;
|
||||
}
|
||||
|
||||
TimedPulseTrain_T *MILES_TAG_II_EncodePacket(TagPacket_T *packet)
|
||||
{
|
||||
uint16_t packed_data = Encode_Damage(packet->damage);
|
||||
packed_data |= (packet->team_ID << 4);
|
||||
packed_data |= (packet->player_ID << 6);
|
||||
|
||||
PackPulseTrain(&Tag_Send_Buffer, packed_data);
|
||||
|
||||
return &Tag_Send_Buffer;
|
||||
}
|
||||
|
||||
DecodedPacket_T *MILES_TAG_II_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
||||
{
|
||||
// Some implementations (ESP32) do not report odd numbers of pulses.
|
||||
if ((packet->count != 29) && (packet->count != 30))
|
||||
{
|
||||
// Fail fast!
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert pulse durations to bits.
|
||||
// packet->bitstream[0] is the header--ignore it.
|
||||
// packet->bitstream[1] is the space after the header--ignore it also.
|
||||
uint16_t data = 0;
|
||||
for (uint8_t n = 0; n < 14; n++)
|
||||
{
|
||||
// Even pulses are marks; odd pulses are spaces.
|
||||
// We only check the marks against the tolerances, to save duration.
|
||||
if ((packet->bitstream[2 + (n * 2)].duration > MIN_MARK_ONE_IN_us) && (packet->bitstream[2 + (n * 2)].duration < MAX_MARK_ONE_IN_us))
|
||||
{
|
||||
// One
|
||||
data |= ((uint16_t)1 << n);
|
||||
}
|
||||
else if ((packet->bitstream[2 + (n * 2)].duration > MIN_MARK_ZERO_IN_us) && (packet->bitstream[2 + (n * 2)].duration < MAX_MARK_ZERO_IN_us))
|
||||
{
|
||||
// Zero--nothing to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
// This mark is neither a zero or a one; abort;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = MILES_TAG_II_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0x007F & (data >> 6);
|
||||
Tag_Rx_Buffer->Tag.team_ID = 0x0003 & (data >> 4);
|
||||
Tag_Rx_Buffer->Tag.damage = Miles_Tag_II_Damage[0x000F & data];
|
||||
Tag_Rx_Buffer->Tag.color = Miles_Tag_II_Team_Colors[Tag_Rx_Buffer->Tag.team_ID];
|
||||
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
}
|
||||
|
||||
color_t MILES_TAG_II_GetTeamColor(uint8_t team_ID)
|
||||
{
|
||||
color_t result = COLOR_RED;
|
||||
|
||||
if (team_ID == MILES_TAG_RED_TEAM)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
else if (team_ID == MILES_TAG_BLUE_TEAM)
|
||||
{
|
||||
result = COLOR_BLUE;
|
||||
}
|
||||
else if (team_ID == MILES_TAG_YELLOW_TEAM)
|
||||
{
|
||||
result = COLOR_YELLOW;
|
||||
}
|
||||
else if (team_ID == MILES_TAG_GREEN_TEAM)
|
||||
{
|
||||
result = COLOR_GREEN;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
33
Protocols/Miles_Tag_II.h
Executable file
33
Protocols/Miles_Tag_II.h
Executable file
|
@ -0,0 +1,33 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MILES_TAG_H
|
||||
#define MILES_TAG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
TimedPulseTrain_T *MILES_TAG_II_EncodePacket(TagPacket_T *packet);
|
||||
DecodedPacket_T *MILES_TAG_II_MaybeDecodePacket(TimedPulseTrain_T *packet);
|
||||
color_t MILES_TAG_II_GetTeamColor(uint8_t team_ID);
|
||||
|
||||
#endif // MILES_TAG_H
|
171
Protocols/NEC.c
Executable file
171
Protocols/NEC.c
Executable file
|
@ -0,0 +1,171 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define NEC_BLUE_TEAM 0x01
|
||||
#define NEC_RED_TEAM 0x02
|
||||
|
||||
#define NEC_HEADER_MARK_DURATION_IN_us 9000
|
||||
#define NEC_HEADER_SPACE_DURATION_IN_us 4500
|
||||
#define NEC_ZERO_MARK_DURATION_IN_us 562
|
||||
#define NEC_ZERO_SPACE_DURATION_IN_us 562
|
||||
#define NEC_ONE_MARK_DURATION_IN_us 562
|
||||
#define NEC_ONE_SPACE_DURATION_IN_us 1688
|
||||
#define NEC_TOLERANCE_IN_us 200
|
||||
|
||||
#define MIN_ZERO_MARK_IN_us (NEC_ZERO_MARK_DURATION_IN_us - NEC_TOLERANCE_IN_us)
|
||||
#define MAX_ZERO_MARK_IN_us (NEC_ZERO_MARK_DURATION_IN_us + NEC_TOLERANCE_IN_us)
|
||||
#define MIN_ZERO_SPACE_IN_us (NEC_ZERO_SPACE_DURATION_IN_us - NEC_TOLERANCE_IN_us)
|
||||
#define MAX_ZERO_SPACE_IN_us (NEC_ZERO_SPACE_DURATION_IN_us + NEC_TOLERANCE_IN_us)
|
||||
#define MIN_ONE_MARK_IN_us (NEC_ONE_MARK_DURATION_IN_us - NEC_TOLERANCE_IN_us)
|
||||
#define MAX_ONE_MARK_IN_us (NEC_ONE_MARK_DURATION_IN_us + NEC_TOLERANCE_IN_us)
|
||||
#define MIN_ONE_SPACE_IN_us (NEC_ONE_SPACE_DURATION_IN_us - NEC_TOLERANCE_IN_us)
|
||||
#define MAX_ONE_SPACE_IN_us (NEC_ONE_SPACE_DURATION_IN_us + NEC_TOLERANCE_IN_us)
|
||||
|
||||
static const TimedBit_T NEC_HEADER_MARK = {.symbol = MARK, .duration = NEC_HEADER_MARK_DURATION_IN_us};
|
||||
static const TimedBit_T NEC_HEADER_SPACE = {.symbol = MARK, .duration = NEC_HEADER_SPACE_DURATION_IN_us};
|
||||
static const TimedBit_T NEC_ZERO_SPACE = {.symbol = SPACE, .duration = NEC_ZERO_SPACE_DURATION_IN_us};
|
||||
static const TimedBit_T NEC_ONE_SPACE = {.symbol = SPACE, .duration = NEC_ONE_SPACE_DURATION_IN_us};
|
||||
static const TimedBit_T NEC_ZERO_MARK = {.symbol = MARK, .duration = NEC_ZERO_MARK_DURATION_IN_us};
|
||||
static const TimedBit_T NEC_ONE_MARK = {.symbol = MARK, .duration = NEC_ONE_MARK_DURATION_IN_us};
|
||||
static const TimedBit_T NEC_END = {.symbol = SPACE, .duration = LAST_PULSE};
|
||||
|
||||
static TimedPulseTrain_T Command_Send_Buffer;
|
||||
static DecodedPacket_T NEC_Command_Buffers[3] =
|
||||
{
|
||||
{.Command.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Command.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Command.type = DECODED_PACKET_TYPE_BUFFER_FREE}};
|
||||
|
||||
static inline DecodedPacket_T *Get_Command_Packet_Buffer(void)
|
||||
{
|
||||
if (NEC_Command_Buffers[0].Command.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &NEC_Command_Buffers[0];
|
||||
}
|
||||
else if (NEC_Command_Buffers[1].Command.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &NEC_Command_Buffers[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just use it.
|
||||
return &NEC_Command_Buffers[2];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void PackPulseTrain(TimedPulseTrain_T *pulsetrain, uint32_t data)
|
||||
{
|
||||
pulsetrain->bitstream[0] = NEC_HEADER_MARK;
|
||||
pulsetrain->bitstream[1] = NEC_HEADER_SPACE;
|
||||
|
||||
for (uint8_t n = 0; n < 16; n++)
|
||||
{
|
||||
if ((data & 0x80000000) == 0x80000000)
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = NEC_ONE_SPACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = NEC_ZERO_SPACE;
|
||||
}
|
||||
data = data << 1;
|
||||
if ((data & 0x80000000) == 0x80000000)
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = NEC_ONE_MARK;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = NEC_ZERO_MARK;
|
||||
}
|
||||
data = data << 1;
|
||||
}
|
||||
|
||||
pulsetrain->bitstream[(2 * 20) + 1] = NEC_END;
|
||||
}
|
||||
|
||||
TimedPulseTrain_T *NEC_EncodePacket(CommandPacket_T *packet)
|
||||
{
|
||||
uint32_t packed_data = 0x00000000;
|
||||
|
||||
PackPulseTrain(&Command_Send_Buffer, packed_data);
|
||||
|
||||
return &Command_Send_Buffer;
|
||||
}
|
||||
|
||||
DecodedPacket_T *NEC_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
||||
{
|
||||
// Sometimes the Roku remote will repeat keys, so only check for a minimum here.
|
||||
if (packet->count < 67)
|
||||
{
|
||||
// Fail fast!
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert pulse durations to bits.
|
||||
// packet->bitstream[0] is the header mark--ignore it.
|
||||
// packet->bitstream[1] is the header space--ignore it, too.
|
||||
|
||||
uint32_t data = 0;
|
||||
for (uint8_t n = 0; n < 32; n++)
|
||||
{
|
||||
uint8_t mark_index = (n * 2) + 2;
|
||||
uint8_t space_index = (n * 2) + 3;
|
||||
|
||||
if ((packet->bitstream[mark_index].duration > MIN_ONE_MARK_IN_us) && (packet->bitstream[mark_index].duration < MAX_ONE_MARK_IN_us) &&
|
||||
(packet->bitstream[space_index].duration > MIN_ONE_SPACE_IN_us) && (packet->bitstream[space_index].duration < MAX_ONE_SPACE_IN_us))
|
||||
{
|
||||
// One
|
||||
data |= ((uint32_t)1 << n);
|
||||
}
|
||||
else if ((packet->bitstream[mark_index].duration > MIN_ZERO_MARK_IN_us) && (packet->bitstream[mark_index].duration < MAX_ZERO_MARK_IN_us) &&
|
||||
(packet->bitstream[space_index].duration > MIN_ZERO_SPACE_IN_us) && (packet->bitstream[space_index].duration < MAX_ZERO_SPACE_IN_us))
|
||||
{
|
||||
// Zero--nothing to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
// This mark is neither a zero or a one; abort.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// PulseDurations[66] is the end-of-message mark. It can also be ignored.
|
||||
DecodedPacket_T *Command_Rx_Buffer = Get_Command_Packet_Buffer();
|
||||
Command_Rx_Buffer->Command.type = DECODED_PACKET_TYPE_COMMAND_RECEIVED;
|
||||
Command_Rx_Buffer->Command.protocol = NEC_PROTOCOL;
|
||||
Command_Rx_Buffer->Command.data = data;
|
||||
return Command_Rx_Buffer;
|
||||
}
|
||||
}
|
||||
|
||||
color_t NEC_GetTeamColor(uint8_t team_ID)
|
||||
{
|
||||
(void)team_ID;
|
||||
|
||||
return COLOR_BLACK;
|
||||
}
|
39
Protocols/NEC.h
Executable file
39
Protocols/NEC.h
Executable file
|
@ -0,0 +1,39 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NEC_H
|
||||
#define NEC_H
|
||||
|
||||
/** \file
|
||||
* \brief This is the NEC Infrared Transmission Protocol.
|
||||
*
|
||||
* See https://techdocs.altium.com/display/FPGA/NEC+Infrared+Transmission+Protocol for more information.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
TimedPulseTrain_T *NEC_EncodePacket(CommandPacket_T *packet);
|
||||
DecodedPacket_T *NEC_MaybeDecodePacket(TimedPulseTrain_T *packet);
|
||||
color_t NEC_GetTeamColor(uint8_t team_ID);
|
||||
|
||||
#endif // NEC_H
|
218
Protocols/Nerf_Laser_Ops_Pro.c
Executable file
218
Protocols/Nerf_Laser_Ops_Pro.c
Executable file
|
@ -0,0 +1,218 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define NERF_RED_TEAM 0b00
|
||||
#define NERF_BLUE_TEAM 0b01
|
||||
#define NERF_PURPLE_TEAM 0b10
|
||||
|
||||
#define RED_TEAM_PACKET_DATA 0x0010
|
||||
#define BLUE_TEAM_PACKET_DATA 0x0210
|
||||
#define PURPLE_TEAM_PACKET_DATA 0x0110
|
||||
|
||||
#define NERF_LASER_OPS_PRO_HEADER_MARK_DURATION_IN_us 3000
|
||||
#define NERF_LASER_OPS_PRO_HEADER_SPACE_DURATION_IN_us 6000
|
||||
#define NERF_LASER_OPS_PRO_SPACE_DURATION_IN_us 2000
|
||||
#define NERF_LASER_OPS_PRO_MARK_ZERO_DURATION_IN_us 800
|
||||
#define NERF_LASER_OPS_PRO_MARK_ONE_DURATION_IN_us 2000
|
||||
#define NERF_LASER_OPS_PRO_TOLERANCE_IN_us 400
|
||||
|
||||
#define MIN_SPACE_IN_us (NERF_LASER_OPS_PRO_SPACE_DURATION_IN_us - NERF_LASER_OPS_PRO_TOLERANCE_IN_us)
|
||||
#define MAX_SPACE_IN_us (NERF_LASER_OPS_PRO_SPACE_DURATION_IN_us + NERF_LASER_OPS_PRO_TOLERANCE_IN_us)
|
||||
#define MIN_MARK_ZERO_IN_us (NERF_LASER_OPS_PRO_MARK_ZERO_DURATION_IN_us - NERF_LASER_OPS_PRO_TOLERANCE_IN_us)
|
||||
#define MAX_MARK_ZERO_IN_us (NERF_LASER_OPS_PRO_MARK_ZERO_DURATION_IN_us + NERF_LASER_OPS_PRO_TOLERANCE_IN_us)
|
||||
#define MIN_MARK_ONE_IN_us (NERF_LASER_OPS_PRO_MARK_ONE_DURATION_IN_us - NERF_LASER_OPS_PRO_TOLERANCE_IN_us)
|
||||
#define MAX_MARK_ONE_IN_us (NERF_LASER_OPS_PRO_MARK_ONE_DURATION_IN_us + NERF_LASER_OPS_PRO_TOLERANCE_IN_us)
|
||||
|
||||
static const TimedBit_T NERF_LASER_OPS_PRO_HEADER_MARK = {.symbol = MARK, .duration = NERF_LASER_OPS_PRO_HEADER_MARK_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_LASER_OPS_PRO_HEADER_SPACE = {.symbol = SPACE, .duration = NERF_LASER_OPS_PRO_HEADER_SPACE_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_LASER_OPS_PRO_SPACE = {.symbol = SPACE, .duration = NERF_LASER_OPS_PRO_SPACE_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_LASER_OPS_PRO_ONE = {.symbol = MARK, .duration = NERF_LASER_OPS_PRO_MARK_ONE_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_LASER_OPS_PRO_ZERO = {.symbol = MARK, .duration = NERF_LASER_OPS_PRO_MARK_ZERO_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_LASER_OPS_PRO_END = {.symbol = SPACE, .duration = LAST_PULSE};
|
||||
|
||||
static TimedPulseTrain_T Tag_Send_Buffer;
|
||||
static DecodedPacket_T Nerf_Laser_Ops_Pro_Tag_Received_Buffers[3] =
|
||||
{
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE}};
|
||||
|
||||
static inline DecodedPacket_T *Get_Tag_Packet_Buffer(void)
|
||||
{
|
||||
if (Nerf_Laser_Ops_Pro_Tag_Received_Buffers[0].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Nerf_Laser_Ops_Pro_Tag_Received_Buffers[0];
|
||||
}
|
||||
else if (Nerf_Laser_Ops_Pro_Tag_Received_Buffers[1].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Nerf_Laser_Ops_Pro_Tag_Received_Buffers[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just use it.
|
||||
return &Nerf_Laser_Ops_Pro_Tag_Received_Buffers[2];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void PackPulseTrain(TimedPulseTrain_T *pulsetrain, uint16_t data)
|
||||
{
|
||||
pulsetrain->bitstream[0] = NERF_LASER_OPS_PRO_HEADER_MARK;
|
||||
pulsetrain->bitstream[1] = NERF_LASER_OPS_PRO_HEADER_SPACE;
|
||||
pulsetrain->bitstream[2] = NERF_LASER_OPS_PRO_HEADER_MARK;
|
||||
pulsetrain->bitstream[3] = NERF_LASER_OPS_PRO_SPACE;
|
||||
|
||||
for (uint8_t n = 0; n < 16; n++)
|
||||
{
|
||||
if ((data & 0x01) == 0x01)
|
||||
{
|
||||
pulsetrain->bitstream[4 + (n * 2)] = NERF_LASER_OPS_PRO_ONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[4 + (n * 2)] = NERF_LASER_OPS_PRO_ZERO;
|
||||
}
|
||||
pulsetrain->bitstream[4 + (n * 2) + 1] = NERF_LASER_OPS_PRO_SPACE;
|
||||
data = data >> 1;
|
||||
}
|
||||
|
||||
pulsetrain->bitstream[35] = NERF_LASER_OPS_PRO_END;
|
||||
}
|
||||
|
||||
TimedPulseTrain_T *NERF_LASER_OPS_PRO_EncodePacket(TagPacket_T *packet)
|
||||
{
|
||||
uint16_t packed_data = 0;
|
||||
|
||||
if (packet->team_ID == NERF_RED_TEAM)
|
||||
{
|
||||
packed_data = RED_TEAM_PACKET_DATA;
|
||||
}
|
||||
else if (packet->team_ID == NERF_BLUE_TEAM)
|
||||
{
|
||||
packed_data = BLUE_TEAM_PACKET_DATA;
|
||||
}
|
||||
else // NERF_PURPLE_TEAM
|
||||
{
|
||||
packed_data = PURPLE_TEAM_PACKET_DATA;
|
||||
}
|
||||
|
||||
PackPulseTrain(&Tag_Send_Buffer, packed_data);
|
||||
|
||||
return &Tag_Send_Buffer;
|
||||
}
|
||||
|
||||
DecodedPacket_T *NERF_LASER_OPS_PRO_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
||||
{
|
||||
// Some implementations (ESP32) do not report odd numbers of pulses.
|
||||
if ((packet->count != 35) && (packet->count != 36))
|
||||
{
|
||||
// Fail fast!
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert pulse durations to bits.
|
||||
// packet->bitstream[0] is the header mark--ignore it.
|
||||
// packet->bitstream[1] is the header space--ignore it also.
|
||||
// packet->bitstream[2] is the second header mark--ignore it as well.
|
||||
// packet->bitstream[3] is the second header space--also ignored.
|
||||
uint16_t data = 0;
|
||||
for (uint8_t n = 0; n < 16; n++)
|
||||
{
|
||||
// Even pulses are marks; odd pulses are spaces.
|
||||
if ((packet->bitstream[4 + (n * 2)].duration > MIN_MARK_ONE_IN_us) && (packet->bitstream[4 + (n * 2)].duration < MAX_MARK_ONE_IN_us))
|
||||
{
|
||||
// One
|
||||
data |= ((uint16_t)1 << n);
|
||||
}
|
||||
else if ((packet->bitstream[4 + (n * 2)].duration > MIN_MARK_ZERO_IN_us) && (packet->bitstream[4 + (n * 2)].duration < MAX_MARK_ZERO_IN_us))
|
||||
{
|
||||
// Zero--nothing to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
// This mark is neither a zero or a one; abort.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (data == RED_TEAM_PACKET_DATA)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = NERF_LASER_OPS_PRO_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = NERF_RED_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_RED;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
else if (data == BLUE_TEAM_PACKET_DATA)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = NERF_LASER_OPS_PRO_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = NERF_BLUE_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_BLUE;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
else if (data == PURPLE_TEAM_PACKET_DATA)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = NERF_LASER_OPS_PRO_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = NERF_PURPLE_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_PURPLE;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
color_t NERF_LASER_OPS_PRO_GetTeamColor(uint8_t team_ID)
|
||||
{
|
||||
color_t result = COLOR_PURPLE;
|
||||
|
||||
if (team_ID == NERF_RED_TEAM)
|
||||
{
|
||||
result = COLOR_RED;
|
||||
}
|
||||
else if (team_ID == NERF_BLUE_TEAM)
|
||||
{
|
||||
result = COLOR_BLUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
44
Protocols/Nerf_Laser_Ops_Pro.h
Executable file
44
Protocols/Nerf_Laser_Ops_Pro.h
Executable file
|
@ -0,0 +1,44 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NERF_LASER_OPS_PRO_H
|
||||
#define NERF_LASER_OPS_PRO_H
|
||||
|
||||
/** \file
|
||||
* \brief This is the Nerf Laser Ops Pro protocol.
|
||||
*
|
||||
* \note These devices use 38kHz carrier for On-Off Keying.
|
||||
*
|
||||
* https://nerf.hasbro.com/en-us/product/nerf-laser-ops-pro-alphapoint-2-pack:CA310BBE-E75A-4354-8D69-AC2B28C4E007
|
||||
*
|
||||
* https://forum.arduino.cc/index.php?topic=582222.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
TimedPulseTrain_T *NERF_LASER_OPS_PRO_EncodePacket(TagPacket_T *packet);
|
||||
DecodedPacket_T *NERF_LASER_OPS_PRO_MaybeDecodePacket(TimedPulseTrain_T *packet);
|
||||
color_t NERF_LASER_OPS_PRO_GetTeamColor(uint8_t team_ID);
|
||||
|
||||
#endif // NERF_LASER_OPS_PRO_H
|
295
Protocols/Nerf_Laser_Strike.c
Executable file
295
Protocols/Nerf_Laser_Strike.c
Executable file
|
@ -0,0 +1,295 @@
|
|||
|
||||
/*
|
||||
* 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 <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static const char *KLOG_TAG = "Nerf Laser Strike";
|
||||
|
||||
#define NERF_LASER_STRIKE_SOLO_TEAM 0b00
|
||||
#define NERF_LASER_STRIKE_ONE_TEAM 0b01
|
||||
#define NERF_LASER_STRIKE_TWO_TEAM 0b10
|
||||
|
||||
#define NERF_LASER_STRIKE_BLASTER 0b000
|
||||
#define NERF_LASER_STRIKE_ROCKET 0b001
|
||||
#define NERF_LASER_STRIKE_PLASMA 0b010
|
||||
#define NERF_LASER_STRIKE_BURST 0b011
|
||||
#define NERF_LASER_STRIKE_LASER 0b100
|
||||
|
||||
static const uint16_t Packet_Data[5][3] = {
|
||||
// Solo, Team 1, Team 2
|
||||
{0x4D33, 0x34CC, 0x2B55}, // Blaster
|
||||
{0x52B2, 0x354B, 0x2CD4}, // Rocket
|
||||
{0x532D, 0x4ACA, 0x2D53}, // Plasma
|
||||
{0x54AC, 0x4B35, 0x32D2}, // Burst
|
||||
{0x552B, 0x4CB4, 0x334D} // Laser
|
||||
};
|
||||
|
||||
static inline uint16_t Get_Packet_Data(uint8_t team, uint8_t weapon)
|
||||
{
|
||||
uint16_t result = 0;
|
||||
|
||||
if ((team <= NERF_LASER_STRIKE_TWO_TEAM) && (weapon <= NERF_LASER_STRIKE_LASER))
|
||||
{
|
||||
result = Packet_Data[weapon][team];
|
||||
}
|
||||
else
|
||||
{
|
||||
KLOG_ERROR(KLOG_TAG, "Requested team (%u) or weapon (%u) out of range!", team, weapon);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Given decoded packet data, this function searches the table to determine if the packet is a valid one.
|
||||
/*!
|
||||
* If it is valid, it will set team and weapon to their valid values, and return true.
|
||||
* If it does not find the packet data in the table, it will return false.
|
||||
*/
|
||||
static inline bool Is_Valid_Packet_Data(const uint16_t data, uint8_t *team, uint8_t *weapon)
|
||||
{
|
||||
bool valid = false;
|
||||
|
||||
for (uint_fast8_t weapon_index = 0; (weapon_index < 5) && (valid == false); weapon_index++)
|
||||
{
|
||||
for (uint_fast8_t team_index = 0; (team_index < 3) && (valid == false); team_index++)
|
||||
{
|
||||
if (Packet_Data[weapon_index][team_index] == data)
|
||||
{
|
||||
*team = team_index;
|
||||
*weapon = weapon_index;
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
#define NERF_LASER_STRIKE_HEADER_MARK_DURATION_IN_us 6000
|
||||
#define NERF_LASER_STRIKE_ZERO_DURATION_IN_us 500
|
||||
#define NERF_LASER_STRIKE_ONE_DURATION_IN_us 1500
|
||||
|
||||
#define NERF_LASER_STRIKE_TOLERANCE_IN_us 450
|
||||
|
||||
#define MIN_ZERO_IN_us (NERF_LASER_STRIKE_ZERO_DURATION_IN_us - NERF_LASER_STRIKE_TOLERANCE_IN_us)
|
||||
#define MAX_ZERO_IN_us (NERF_LASER_STRIKE_ZERO_DURATION_IN_us + NERF_LASER_STRIKE_TOLERANCE_IN_us)
|
||||
#define MIN_ONE_IN_us (NERF_LASER_STRIKE_ONE_DURATION_IN_us - NERF_LASER_STRIKE_TOLERANCE_IN_us)
|
||||
#define MAX_ONE_IN_us (NERF_LASER_STRIKE_ONE_DURATION_IN_us + NERF_LASER_STRIKE_TOLERANCE_IN_us)
|
||||
|
||||
static const TimedBit_T NERF_LASER_STRIKE_HEADER_MARK = {.symbol = MARK, .duration = NERF_LASER_STRIKE_HEADER_MARK_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_LASER_STRIKE_ZERO_SPACE = {.symbol = SPACE, .duration = NERF_LASER_STRIKE_ZERO_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_LASER_STRIKE_ONE_SPACE = {.symbol = SPACE, .duration = NERF_LASER_STRIKE_ONE_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_LASER_STRIKE_ZERO_MARK = {.symbol = MARK, .duration = NERF_LASER_STRIKE_ZERO_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_LASER_STRIKE_ONE_MARK = {.symbol = MARK, .duration = NERF_LASER_STRIKE_ONE_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_LASER_STRIKE_END = {.symbol = SPACE, .duration = LAST_PULSE};
|
||||
|
||||
static TimedPulseTrain_T Tag_Send_Buffer;
|
||||
static DecodedPacket_T Nerf_Laser_Strike_Tag_Received_Buffers[3] =
|
||||
{
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE}};
|
||||
|
||||
static inline DecodedPacket_T *Get_Tag_Packet_Buffer(void)
|
||||
{
|
||||
if (Nerf_Laser_Strike_Tag_Received_Buffers[0].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Nerf_Laser_Strike_Tag_Received_Buffers[0];
|
||||
}
|
||||
else if (Nerf_Laser_Strike_Tag_Received_Buffers[1].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Nerf_Laser_Strike_Tag_Received_Buffers[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just use it.
|
||||
return &Nerf_Laser_Strike_Tag_Received_Buffers[2];
|
||||
}
|
||||
}
|
||||
|
||||
// Packs data into a pulse train, most significant bit first.
|
||||
static inline void PackPulseTrain(TimedPulseTrain_T *pulsetrain, uint16_t data)
|
||||
{
|
||||
pulsetrain->bitstream[0] = NERF_LASER_STRIKE_HEADER_MARK;
|
||||
|
||||
for (uint8_t n = 0; n < 8; n++)
|
||||
{
|
||||
if ((data & 0x8000) == 0x8000)
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = NERF_LASER_STRIKE_ONE_SPACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = NERF_LASER_STRIKE_ZERO_SPACE;
|
||||
}
|
||||
data = data << 1;
|
||||
if ((data & 0x8000) == 0x8000)
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = NERF_LASER_STRIKE_ONE_MARK;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = NERF_LASER_STRIKE_ZERO_MARK;
|
||||
}
|
||||
data = data << 1;
|
||||
}
|
||||
|
||||
pulsetrain->bitstream[17] = NERF_LASER_STRIKE_END;
|
||||
pulsetrain->count = 17;
|
||||
}
|
||||
|
||||
TimedPulseTrain_T *NERF_LASER_STRIKE_EncodePacket(TagPacket_T *packet)
|
||||
{
|
||||
uint16_t packed_data = 0;
|
||||
|
||||
if (packet->team_ID == TEAM_RED)
|
||||
{
|
||||
packed_data = Get_Packet_Data(NERF_LASER_STRIKE_ONE_TEAM, NERF_LASER_STRIKE_BLASTER);
|
||||
}
|
||||
else if (packet->team_ID == TEAM_BLUE)
|
||||
{
|
||||
packed_data = Get_Packet_Data(NERF_LASER_STRIKE_TWO_TEAM, NERF_LASER_STRIKE_BLASTER);
|
||||
}
|
||||
else // NERF_LASER_STRIKE_SOLO_TEAM
|
||||
{
|
||||
packed_data = Get_Packet_Data(NERF_LASER_STRIKE_SOLO_TEAM, NERF_LASER_STRIKE_BLASTER);
|
||||
}
|
||||
|
||||
PackPulseTrain(&Tag_Send_Buffer, packed_data);
|
||||
|
||||
return &Tag_Send_Buffer;
|
||||
}
|
||||
|
||||
DecodedPacket_T *NERF_LASER_STRIKE_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
||||
{
|
||||
// Some implementations (ESP32) do not report odd numbers of pulses.
|
||||
// The PSoC sees all three copies of the tag at once.
|
||||
if ((packet->count != 17) &&
|
||||
(packet->count != 18) &&
|
||||
(packet->count != 53))
|
||||
{
|
||||
// Fail fast!
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert pulse durations to bits, assuming most significant bit is sent first.
|
||||
// packet->bitstream[0] is the header mark--ignore it.
|
||||
|
||||
uint16_t data = 0;
|
||||
for (uint16_t n = 16; n > 0; n--)
|
||||
{
|
||||
if ((packet->bitstream[n].duration > MIN_ONE_IN_us) && (packet->bitstream[n].duration < MAX_ONE_IN_us))
|
||||
{
|
||||
// One
|
||||
data |= ((uint16_t)1 << (16 - n));
|
||||
}
|
||||
else if ((packet->bitstream[n].duration > MIN_ZERO_IN_us) && (packet->bitstream[n].duration < MAX_ZERO_IN_us))
|
||||
{
|
||||
// Zero--nothing to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
// This pulse is neither a zero or a one; abort.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t team = 0;
|
||||
uint8_t weapon = 0;
|
||||
|
||||
if (Is_Valid_Packet_Data(data, &team, &weapon) == false)
|
||||
{
|
||||
KLOG_DEBUG(KLOG_TAG, "Decoded packet was not recognized: 0x%4X", data);
|
||||
}
|
||||
else
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
|
||||
if (team == NERF_LASER_STRIKE_ONE_TEAM)
|
||||
{
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = NERF_LASER_STRIKE_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = TEAM_RED;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_RED;
|
||||
}
|
||||
else if (team == NERF_LASER_STRIKE_TWO_TEAM)
|
||||
{
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = NERF_LASER_STRIKE_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = TEAM_BLUE;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_BLUE;
|
||||
}
|
||||
else // NERF_LASER_STRIKE_SOLO_TEAM
|
||||
{
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = NERF_LASER_STRIKE_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = TEAM_PURPLE;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_PURPLE;
|
||||
}
|
||||
|
||||
// Damages are 10x, normalized for MAX_HEALTH of 100.
|
||||
if (weapon == NERF_LASER_STRIKE_BLASTER)
|
||||
{
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
}
|
||||
else if (weapon == NERF_LASER_STRIKE_ROCKET)
|
||||
{
|
||||
Tag_Rx_Buffer->Tag.damage = 30;
|
||||
}
|
||||
else // NERF_LASER_STRIKE_PLASMA, NERF_LASER_STRIKE_BURST, and NERF_LASER_STRIKE_LASER
|
||||
{
|
||||
Tag_Rx_Buffer->Tag.damage = 20;
|
||||
}
|
||||
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
color_t NERF_LASER_STRIKE_GetTeamColor(uint8_t team_ID)
|
||||
{
|
||||
color_t result = COLOR_PURPLE;
|
||||
|
||||
if (team_ID == NERF_LASER_STRIKE_ONE_TEAM)
|
||||
{
|
||||
result = COLOR_RED;
|
||||
}
|
||||
else if (team_ID == NERF_LASER_STRIKE_TWO_TEAM)
|
||||
{
|
||||
result = COLOR_BLUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
40
Protocols/Nerf_Laser_Strike.h
Executable file
40
Protocols/Nerf_Laser_Strike.h
Executable file
|
@ -0,0 +1,40 @@
|
|||
|
||||
/*
|
||||
* 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 NERF_LASER_STRIKE_H
|
||||
#define NERF_LASER_STRIKE_H
|
||||
|
||||
/** \file
|
||||
* \brief This is the Nerf Laser Strike protocol.
|
||||
*
|
||||
* \note These devices use 38kHz carrier for On-Off Keying.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
TimedPulseTrain_T *NERF_LASER_STRIKE_EncodePacket(TagPacket_T *packet);
|
||||
DecodedPacket_T *NERF_LASER_STRIKE_MaybeDecodePacket(TimedPulseTrain_T *packet);
|
||||
color_t NERF_LASER_STRIKE_GetTeamColor(uint8_t team_ID);
|
||||
|
||||
#endif // NERF_LASER_STRIKE_H
|
221
Protocols/Nerf_Phoenix_LTX.c
Executable file
221
Protocols/Nerf_Phoenix_LTX.c
Executable file
|
@ -0,0 +1,221 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define NERF_RED_TEAM 0b00
|
||||
#define NERF_BLUE_TEAM 0b01
|
||||
#define NERF_PURPLE_TEAM 0b10
|
||||
|
||||
//! *Team One* is the Red Team.
|
||||
#define RED_TEAM_PACKET_DATA 0x0010
|
||||
//! *Team Two* is the Blue Team.
|
||||
#define BLUE_TEAM_PACKET_DATA 0x0008
|
||||
//! *Solo* is the Purple Team.
|
||||
#define PURPLE_TEAM_PACKET_DATA 0x0000
|
||||
|
||||
#define NERF_PHOENIX_LTX_HEADER_MARK_DURATION_IN_us 3000
|
||||
#define NERF_PHOENIX_LTX_HEADER_SPACE_DURATION_IN_us 6000
|
||||
#define NERF_PHOENIX_LTX_SPACE_DURATION_IN_us 2000
|
||||
#define NERF_PHOENIX_LTX_MARK_ZERO_DURATION_IN_us 900
|
||||
#define NERF_PHOENIX_LTX_MARK_ONE_DURATION_IN_us 2000
|
||||
#define NERF_PHOENIX_LTX_TOLERANCE_IN_us 350
|
||||
|
||||
#define MIN_SPACE_IN_us (NERF_PHOENIX_LTX_SPACE_DURATION_IN_us - NERF_PHOENIX_LTX_TOLERANCE_IN_us)
|
||||
#define MAX_SPACE_IN_us (NERF_PHOENIX_LTX_SPACE_DURATION_IN_us + NERF_PHOENIX_LTX_TOLERANCE_IN_us)
|
||||
#define MIN_MARK_ZERO_IN_us (NERF_PHOENIX_LTX_MARK_ZERO_DURATION_IN_us - NERF_PHOENIX_LTX_TOLERANCE_IN_us)
|
||||
#define MAX_MARK_ZERO_IN_us (NERF_PHOENIX_LTX_MARK_ZERO_DURATION_IN_us + NERF_PHOENIX_LTX_TOLERANCE_IN_us)
|
||||
#define MIN_MARK_ONE_IN_us (NERF_PHOENIX_LTX_MARK_ONE_DURATION_IN_us - NERF_PHOENIX_LTX_TOLERANCE_IN_us)
|
||||
#define MAX_MARK_ONE_IN_us (NERF_PHOENIX_LTX_MARK_ONE_DURATION_IN_us + NERF_PHOENIX_LTX_TOLERANCE_IN_us)
|
||||
|
||||
static const TimedBit_T NERF_PHOENIX_LTX_HEADER_MARK = {.symbol = MARK, .duration = NERF_PHOENIX_LTX_HEADER_MARK_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_PHOENIX_LTX_HEADER_SPACE = {.symbol = SPACE, .duration = NERF_PHOENIX_LTX_HEADER_SPACE_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_PHOENIX_LTX_SPACE = {.symbol = SPACE, .duration = NERF_PHOENIX_LTX_SPACE_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_PHOENIX_LTX_ONE = {.symbol = MARK, .duration = NERF_PHOENIX_LTX_MARK_ONE_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_PHOENIX_LTX_ZERO = {.symbol = MARK, .duration = NERF_PHOENIX_LTX_MARK_ZERO_DURATION_IN_us};
|
||||
static const TimedBit_T NERF_PHOENIX_LTX_END = {.symbol = SPACE, .duration = LAST_PULSE};
|
||||
|
||||
static TimedPulseTrain_T Tag_Send_Buffer;
|
||||
static DecodedPacket_T Nerf_Phoenix_LTX_Tag_Received_Buffers[3] =
|
||||
{
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE}};
|
||||
|
||||
static inline DecodedPacket_T *Get_Tag_Packet_Buffer(void)
|
||||
{
|
||||
if (Nerf_Phoenix_LTX_Tag_Received_Buffers[0].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Nerf_Phoenix_LTX_Tag_Received_Buffers[0];
|
||||
}
|
||||
else if (Nerf_Phoenix_LTX_Tag_Received_Buffers[1].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Nerf_Phoenix_LTX_Tag_Received_Buffers[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just use it.
|
||||
return &Nerf_Phoenix_LTX_Tag_Received_Buffers[2];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void PackPulseTrain(TimedPulseTrain_T *pulsetrain, uint16_t data)
|
||||
{
|
||||
pulsetrain->bitstream[0] = NERF_PHOENIX_LTX_HEADER_MARK;
|
||||
pulsetrain->bitstream[1] = NERF_PHOENIX_LTX_HEADER_SPACE;
|
||||
pulsetrain->bitstream[2] = NERF_PHOENIX_LTX_HEADER_MARK;
|
||||
pulsetrain->bitstream[3] = NERF_PHOENIX_LTX_SPACE;
|
||||
|
||||
for (uint8_t n = 0; n < 7; n++)
|
||||
{
|
||||
if ((data & 0x01) == 0x01)
|
||||
{
|
||||
pulsetrain->bitstream[4 + (n * 2)] = NERF_PHOENIX_LTX_ONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[4 + (n * 2)] = NERF_PHOENIX_LTX_ZERO;
|
||||
}
|
||||
pulsetrain->bitstream[4 + (n * 2) + 1] = NERF_PHOENIX_LTX_SPACE;
|
||||
data = data >> 1;
|
||||
}
|
||||
|
||||
pulsetrain->bitstream[17] = NERF_PHOENIX_LTX_END;
|
||||
}
|
||||
|
||||
TimedPulseTrain_T *NERF_PHOENIX_LTX_EncodePacket(TagPacket_T *packet)
|
||||
{
|
||||
uint16_t packed_data = 0;
|
||||
|
||||
if (packet->team_ID == NERF_RED_TEAM)
|
||||
{
|
||||
packed_data = RED_TEAM_PACKET_DATA;
|
||||
}
|
||||
else if (packet->team_ID == NERF_BLUE_TEAM)
|
||||
{
|
||||
packed_data = BLUE_TEAM_PACKET_DATA;
|
||||
}
|
||||
else // NERF_PURPLE_TEAM
|
||||
{
|
||||
packed_data = PURPLE_TEAM_PACKET_DATA;
|
||||
}
|
||||
|
||||
PackPulseTrain(&Tag_Send_Buffer, packed_data);
|
||||
|
||||
return &Tag_Send_Buffer;
|
||||
}
|
||||
|
||||
DecodedPacket_T *NERF_PHOENIX_LTX_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
||||
{
|
||||
// Some implementations (ESP32) do not report odd numbers of pulses.
|
||||
if ((packet->count != 17) && (packet->count != 18))
|
||||
{
|
||||
// Fail fast!
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert pulse durations to bits.
|
||||
// packet->bitstream[0] is the header mark--ignore it.
|
||||
// packet->bitstream[1] is the header space--ignore it also.
|
||||
// packet->bitstream[2] is the second header mark--ignore it as well.
|
||||
// packet->bitstream[3] is the second header space--also ignored..
|
||||
uint16_t data = 0;
|
||||
for (uint8_t n = 0; n < 7; n++)
|
||||
{
|
||||
// Even pulses are marks; odd pulses are spaces.
|
||||
if ((packet->bitstream[4 + (n * 2)].duration > MIN_MARK_ONE_IN_us) && (packet->bitstream[4 + (n * 2)].duration < MAX_MARK_ONE_IN_us))
|
||||
{
|
||||
// One
|
||||
data |= ((uint16_t)1 << n);
|
||||
}
|
||||
else if ((packet->bitstream[4 + (n * 2)].duration > MIN_MARK_ZERO_IN_us) && (packet->bitstream[4 + (n * 2)].duration < MAX_MARK_ZERO_IN_us))
|
||||
{
|
||||
// Zero--nothing to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
// This mark is neither a zero or a one; abort.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (data == RED_TEAM_PACKET_DATA)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = NERF_PHOENIX_LTX_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = NERF_RED_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_RED;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
else if (data == BLUE_TEAM_PACKET_DATA)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = NERF_PHOENIX_LTX_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = NERF_BLUE_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_BLUE;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
else if (data == PURPLE_TEAM_PACKET_DATA)
|
||||
{
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = NERF_PHOENIX_LTX_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = NERF_PURPLE_TEAM;
|
||||
Tag_Rx_Buffer->Tag.damage = 10;
|
||||
Tag_Rx_Buffer->Tag.color = COLOR_PURPLE;
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
color_t NERF_PHOENIX_LTX_GetTeamColor(uint8_t team_ID)
|
||||
{
|
||||
color_t result = COLOR_PURPLE;
|
||||
|
||||
if (team_ID == NERF_RED_TEAM)
|
||||
{
|
||||
result = COLOR_RED;
|
||||
}
|
||||
else if (team_ID == NERF_BLUE_TEAM)
|
||||
{
|
||||
result = COLOR_BLUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
42
Protocols/Nerf_Phoenix_LTX.h
Executable file
42
Protocols/Nerf_Phoenix_LTX.h
Executable file
|
@ -0,0 +1,42 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NERF_PHOENIX_LTX_H
|
||||
#define NERF_PHOENIX_LTX_H
|
||||
|
||||
/** \file
|
||||
* \brief This is the Nerf Phoenix LTX protocol.
|
||||
*
|
||||
* \note These devices use 38kHz carrier for On-Off Keying.
|
||||
*
|
||||
* https://nerf.hasbro.com/en-us/product/nerf-lazer-tag-phoenix-ltx-tagger-2-pack:F0C4410E-19B9-F369-D914-B940ADA55500
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
TimedPulseTrain_T *NERF_PHOENIX_LTX_EncodePacket(TagPacket_T *packet);
|
||||
DecodedPacket_T *NERF_PHOENIX_LTX_MaybeDecodePacket(TimedPulseTrain_T *packet);
|
||||
color_t NERF_PHOENIX_LTX_GetTeamColor(uint8_t team_ID);
|
||||
|
||||
#endif // NERF_PHOENIX_LTX_H
|
365
Protocols/Protocols.c
Executable file
365
Protocols/Protocols.c
Executable file
|
@ -0,0 +1,365 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
// #define DEBUG_PACKET_ENCODE
|
||||
#define DEBUG_PACKET_DECODE
|
||||
|
||||
static const char *KLOG_TAG = "Protocols";
|
||||
|
||||
static const char *PROTOCOL_NAME[] = {
|
||||
FOREACH_PROTOCOL(GENERATE_STRING)};
|
||||
|
||||
static inline const char *ProtocolNameAsString(Protocol_T p)
|
||||
{
|
||||
if (p < LAST_PROTOCOL)
|
||||
{
|
||||
return PROTOCOL_NAME[p];
|
||||
}
|
||||
|
||||
return "Invalid Protocol!";
|
||||
}
|
||||
|
||||
static inline const char *DecodedPacketTypeAsString(DecodedPacketType_T p)
|
||||
{
|
||||
if (p == DECODED_PACKET_TYPE_IGNORED)
|
||||
{
|
||||
return "Ignored";
|
||||
}
|
||||
if (p == DECODED_PACKET_TYPE_TAG_RECEIVED)
|
||||
{
|
||||
return "Tag Received";
|
||||
}
|
||||
if (p == DECODED_PACKET_TYPE_COMMAND_RECEIVED)
|
||||
{
|
||||
return "Command Received";
|
||||
}
|
||||
|
||||
return "Unexpected Packet Type";
|
||||
}
|
||||
|
||||
static inline void PrintPulseTrainToConsole(TimedPulseTrain_T *train)
|
||||
{
|
||||
for (uint_fast16_t i = 0; i < train->count; i += 2)
|
||||
{
|
||||
KLOG_DEBUG(KLOG_TAG, "%2d: (%d, %4d) (%d, %4d)", i + 1, train->bitstream[i].symbol, train->bitstream[i].duration, train->bitstream[i + 1].symbol, train->bitstream[i + 1].duration);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
|
||||
ModulationFrequency_T PROTOCOLS_GetModulationFrequency(Protocol_T protocol)
|
||||
{
|
||||
ModulationFrequency_T result = FREQUENCY_38kHz;
|
||||
|
||||
switch (protocol)
|
||||
{
|
||||
default:
|
||||
case UNKNOWN_PROTOCOL:
|
||||
break;
|
||||
|
||||
case TEST_PROTOCOL:
|
||||
result = FREQUENCY_38kHz;
|
||||
break;
|
||||
|
||||
case DUBUQUE_PROTOCOL:
|
||||
result = FREQUENCY_38kHz;
|
||||
break;
|
||||
|
||||
case MILES_TAG_II_PROTOCOL:
|
||||
result = FREQUENCY_56kHz;
|
||||
break;
|
||||
|
||||
case NERF_LASER_OPS_PRO_PROTOCOL:
|
||||
result = FREQUENCY_38kHz;
|
||||
break;
|
||||
|
||||
case NERF_PHOENIX_LTX_PROTOCOL:
|
||||
result = FREQUENCY_38kHz;
|
||||
break;
|
||||
|
||||
case DYNASTY_PROTOCOL:
|
||||
result = FREQUENCY_38kHz;
|
||||
break;
|
||||
|
||||
case SQUAD_HERO_PROTOCOL:
|
||||
result = FREQUENCY_38kHz;
|
||||
break;
|
||||
|
||||
case NEC_PROTOCOL:
|
||||
result = FREQUENCY_38kHz;
|
||||
break;
|
||||
|
||||
case LASER_X_PROTOCOL:
|
||||
result = FREQUENCY_38kHz;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
color_t PROTOCOLS_GetColor(Protocol_T protocol, uint8_t team_ID, uint8_t player_ID)
|
||||
{
|
||||
color_t result = COLOR_AQUA;
|
||||
|
||||
switch (protocol)
|
||||
{
|
||||
default:
|
||||
case UNKNOWN_PROTOCOL:
|
||||
break;
|
||||
|
||||
case TEST_PROTOCOL:
|
||||
result = COLOR_WHITE;
|
||||
break;
|
||||
|
||||
case DUBUQUE_PROTOCOL:
|
||||
result = DBQ_GetTeamColor(team_ID);
|
||||
break;
|
||||
|
||||
case MILES_TAG_II_PROTOCOL:
|
||||
result = MILES_TAG_II_GetTeamColor(team_ID);
|
||||
break;
|
||||
|
||||
case NERF_LASER_OPS_PRO_PROTOCOL:
|
||||
result = NERF_LASER_OPS_PRO_GetTeamColor(team_ID);
|
||||
break;
|
||||
|
||||
case NERF_LASER_STRIKE_PROTOCOL:
|
||||
result = NERF_LASER_STRIKE_GetTeamColor(team_ID);
|
||||
break;
|
||||
|
||||
case NERF_PHOENIX_LTX_PROTOCOL:
|
||||
result = NERF_PHOENIX_LTX_GetTeamColor(team_ID);
|
||||
break;
|
||||
|
||||
case DYNASTY_PROTOCOL:
|
||||
result = DYNASTY_GetTeamColor(team_ID);
|
||||
break;
|
||||
|
||||
case SQUAD_HERO_PROTOCOL:
|
||||
result = SQUAD_HERO_GetTeamColor(team_ID);
|
||||
break;
|
||||
|
||||
case LASER_X_PROTOCOL:
|
||||
result = LASER_X_GetTeamColor(team_ID);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TimedPulseTrain_T *PROTOCOLS_EncodePacket(TagPacket_T *packet)
|
||||
{
|
||||
TimedPulseTrain_T *result = NULL;
|
||||
|
||||
switch (packet->protocol)
|
||||
{
|
||||
default:
|
||||
case UNKNOWN_PROTOCOL:
|
||||
break;
|
||||
|
||||
case TEST_PROTOCOL:
|
||||
result = TEST_EncodePacket(packet);
|
||||
break;
|
||||
|
||||
case DUBUQUE_PROTOCOL:
|
||||
result = DBQ_EncodePacket(packet);
|
||||
break;
|
||||
|
||||
case MILES_TAG_II_PROTOCOL:
|
||||
result = MILES_TAG_II_EncodePacket(packet);
|
||||
break;
|
||||
|
||||
case NERF_LASER_OPS_PRO_PROTOCOL:
|
||||
result = NERF_LASER_OPS_PRO_EncodePacket(packet);
|
||||
break;
|
||||
|
||||
case NERF_LASER_STRIKE_PROTOCOL:
|
||||
result = NERF_LASER_STRIKE_EncodePacket(packet);
|
||||
break;
|
||||
|
||||
case NERF_PHOENIX_LTX_PROTOCOL:
|
||||
result = NERF_PHOENIX_LTX_EncodePacket(packet);
|
||||
break;
|
||||
|
||||
case DYNASTY_PROTOCOL:
|
||||
result = DYNASTY_EncodePacket(packet);
|
||||
break;
|
||||
|
||||
case SQUAD_HERO_PROTOCOL:
|
||||
result = SQUAD_HERO_EncodePacket(packet);
|
||||
break;
|
||||
|
||||
case NEC_PROTOCOL:
|
||||
result = NULL;
|
||||
break;
|
||||
|
||||
case LASER_X_PROTOCOL:
|
||||
result = LASER_X_EncodePacket(packet);
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PACKET_ENCODE
|
||||
KLOG_DEBUG(KLOG_TAG, "\nEncoded %s packet (%u):", ProtocolNameAsString(packet->protocol), result->count);
|
||||
PrintPulseTrainToConsole(result);
|
||||
#endif // DEBUG_PACKET_ENCODE
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Attempts to decode the pulse durations by comparing to the known protocols.
|
||||
DecodedPacket_T *PROTOCOLS_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
||||
{
|
||||
DecodedPacket_T *result = NULL;
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
result = LASER_X_MaybeDecodePacket(packet);
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
result = SQUAD_HERO_MaybeDecodePacket(packet);
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
result = NEC_MaybeDecodePacket(packet);
|
||||
|
||||
if (result != NULL)
|
||||
{
|
||||
// Many NEC remotes repeat packets when the button is held down.
|
||||
// Too avoid double-counting, endure 500ms of silence between packets.
|
||||
static TickType_t lastPacketTime = 0;
|
||||
const TickType_t minimumInterval = pdMS_TO_TICKS(500);
|
||||
|
||||
TickType_t currentTime = xTaskGetTickCount();
|
||||
TickType_t timeSinceLastPacket = currentTime - lastPacketTime;
|
||||
|
||||
if (timeSinceLastPacket < minimumInterval)
|
||||
{
|
||||
// This packet is possibly the result of a held button--ignore it.
|
||||
KLOG_WARN("NEC", "Received potentially redundant packet--ignoring.");
|
||||
result->Generic.type = DECODED_PACKET_TYPE_IGNORED;
|
||||
}
|
||||
|
||||
lastPacketTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
result = MILES_TAG_II_MaybeDecodePacket(packet);
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
result = DYNASTY_MaybeDecodePacket(packet);
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
result = NERF_LASER_OPS_PRO_MaybeDecodePacket(packet);
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
result = NERF_LASER_STRIKE_MaybeDecodePacket(packet);
|
||||
|
||||
if (result != NULL)
|
||||
{
|
||||
// The Nerf Laser Strike blasters send out three identical packets whenever the trigger is pulled,
|
||||
// over a time of about 200 milliseconds. Only one is necessary to hit. To avoid double-counting,
|
||||
// we ignore further packets for a short time after receiving a valid one.
|
||||
static TickType_t lastValidPacketTime = 0;
|
||||
const TickType_t minimumInterval = pdMS_TO_TICKS(200);
|
||||
|
||||
TickType_t currentTime = xTaskGetTickCount();
|
||||
TickType_t timeSinceLastValidPacket = currentTime - lastValidPacketTime;
|
||||
|
||||
if (timeSinceLastValidPacket < minimumInterval)
|
||||
{
|
||||
// This packet is possibly redundant with the previous valid packet--ignore it.
|
||||
KLOG_WARN("Nerf Laser Strike", "Received potentially redundant packet--ignoring.");
|
||||
result->Generic.type = DECODED_PACKET_TYPE_IGNORED;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastValidPacketTime = currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
result = NERF_PHOENIX_LTX_MaybeDecodePacket(packet);
|
||||
}
|
||||
|
||||
// Keep this close to the end of the list; it's purposely sensitive, and prone to false positives.
|
||||
if (result == NULL)
|
||||
{
|
||||
result = DBQ_MaybeDecodePacket(packet);
|
||||
}
|
||||
|
||||
// Keep this one last, so it doesn't slow down game play.
|
||||
if (result == NULL)
|
||||
{
|
||||
result = TEST_MaybeDecodePacket(packet);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PACKET_DECODE
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
esp_log_level_set(KLOG_TAG, ESP_LOG_DEBUG);
|
||||
#endif // ESP_PLATFORM
|
||||
|
||||
if (result != NULL)
|
||||
{
|
||||
KLOG_DEBUG(KLOG_TAG, "Successfully decoded packet as %s: %s", DecodedPacketTypeAsString(result->Generic.type), ProtocolNameAsString(result->Generic.protocol));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
if (result->Generic.type == DECODED_PACKET_TYPE_COMMAND_RECEIVED)
|
||||
{
|
||||
KLOG_DEBUG(KLOG_TAG, "Command data: %lu", result->Command.data);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
KLOG_DEBUG(KLOG_TAG, "Couldn't decode packet. Size was %d symbols:", packet->count);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
PrintPulseTrainToConsole(packet);
|
||||
}
|
||||
#endif // DEBUG_PACKET_DECODE
|
||||
|
||||
// Remember which receiver saw the packet.
|
||||
if (result != NULL)
|
||||
{
|
||||
result->Generic.receiver = packet->receiver;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
188
Protocols/Protocols.h
Executable file
188
Protocols/Protocols.h
Executable file
|
@ -0,0 +1,188 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PROTOCOLS_H
|
||||
#define PROTOCOLS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define FOREACH_PROTOCOL(PROTOCOL) \
|
||||
PROTOCOL(UNKNOWN_PROTOCOL) \
|
||||
PROTOCOL(TEST_PROTOCOL) \
|
||||
PROTOCOL(DUBUQUE_PROTOCOL) \
|
||||
PROTOCOL(MILES_TAG_II_PROTOCOL) \
|
||||
PROTOCOL(NERF_LASER_OPS_PRO_PROTOCOL) \
|
||||
PROTOCOL(NERF_LASER_STRIKE_PROTOCOL) \
|
||||
PROTOCOL(NERF_PHOENIX_LTX_PROTOCOL) \
|
||||
PROTOCOL(DYNASTY_PROTOCOL) \
|
||||
PROTOCOL(SQUAD_HERO_PROTOCOL) \
|
||||
PROTOCOL(NEC_PROTOCOL) \
|
||||
PROTOCOL(LASER_X_PROTOCOL) \
|
||||
PROTOCOL(LAST_PROTOCOL)
|
||||
|
||||
#define GENERATE_ENUM(ENUM) ENUM,
|
||||
#define GENERATE_STRING(STRING) #STRING,
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FOREACH_PROTOCOL(GENERATE_ENUM)
|
||||
} Protocol_T;
|
||||
|
||||
#define MAX_PULSES 125
|
||||
#define LAST_PULSE 0
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FREQUENCY_38kHz,
|
||||
FREQUENCY_56kHz
|
||||
} ModulationFrequency_T;
|
||||
|
||||
//! On-Off Keying (OOK) symbols
|
||||
typedef enum
|
||||
{
|
||||
SPACE = 0,
|
||||
MARK = 1
|
||||
} OOK_Symbol_T;
|
||||
|
||||
//! Represents a length of time, in microseconds.
|
||||
typedef uint32_t Duration_in_us_T;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TAG_SENSOR_UNKNOWN = 0,
|
||||
TAG_SENSOR_NONE,
|
||||
TAG_SENSOR_FORWARD,
|
||||
TAG_SENSOR_LEFT,
|
||||
TAG_SENSOR_RIGHT,
|
||||
TAG_SENSOR_REMOTE
|
||||
} TagSensorLocation_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
//! Use a value of #LAST_PULSE to mean the pulsetrain is over.
|
||||
Duration_in_us_T duration : 15;
|
||||
OOK_Symbol_T symbol : 1;
|
||||
} __attribute__((packed)) TimedBit_T;
|
||||
|
||||
//! Timing information for a single pulse, packed into a 32-bit integer.
|
||||
/*
|
||||
* \see #rmt_item32_t in the ESP-IDF.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
Duration_in_us_T duration0 : 15;
|
||||
OOK_Symbol_T symbol0 : 1;
|
||||
Duration_in_us_T duration1 : 15;
|
||||
OOK_Symbol_T symbol1 : 1;
|
||||
};
|
||||
uint32_t val;
|
||||
};
|
||||
} __attribute__((packed)) TimedPulse_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
TimedPulse_T pulsetrain[MAX_PULSES];
|
||||
TimedBit_T bitstream[2 * MAX_PULSES];
|
||||
};
|
||||
uint16_t count;
|
||||
TagSensorLocation_T receiver;
|
||||
} __attribute__((packed)) TimedPulseTrain_T;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DECODED_PACKET_TYPE_BUFFER_FREE,
|
||||
DECODED_PACKET_TYPE_UNKNOWN,
|
||||
DECODED_PACKET_TYPE_IGNORED,
|
||||
DECODED_PACKET_TYPE_TAG_RECEIVED,
|
||||
DECODED_PACKET_TYPE_COMMAND_RECEIVED
|
||||
} DecodedPacketType_T;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DecodedPacketType_T type;
|
||||
TagSensorLocation_T receiver;
|
||||
uint8_t protocol;
|
||||
} GenericDecodedPacket_T;
|
||||
|
||||
//! Contents of the decoded packet #DECODED_PACKET_TYPE_TAG_RECEIVED.
|
||||
typedef struct
|
||||
{
|
||||
DecodedPacketType_T type;
|
||||
TagSensorLocation_T receiver;
|
||||
uint8_t protocol;
|
||||
uint16_t team_ID;
|
||||
uint16_t player_ID;
|
||||
uint16_t damage;
|
||||
color_t color;
|
||||
} TagPacket_T;
|
||||
|
||||
//! Contents of the decoded packet #DECODED_PACKET_TYPE_COMMAND_RECEIVED.
|
||||
typedef struct
|
||||
{
|
||||
DecodedPacketType_T type;
|
||||
TagSensorLocation_T receiver;
|
||||
uint8_t protocol;
|
||||
uint16_t command;
|
||||
uint32_t data;
|
||||
} CommandPacket_T;
|
||||
|
||||
typedef union
|
||||
{
|
||||
GenericDecodedPacket_T Generic;
|
||||
TagPacket_T Tag;
|
||||
CommandPacket_T Command;
|
||||
} DecodedPacket_T;
|
||||
|
||||
static inline void FreeDecodedPacketBuffer(DecodedPacket_T *buffer)
|
||||
{
|
||||
if (buffer != NULL)
|
||||
{
|
||||
buffer->Generic.type = DECODED_PACKET_TYPE_BUFFER_FREE;
|
||||
}
|
||||
}
|
||||
|
||||
ModulationFrequency_T PROTOCOLS_GetModulationFrequency(Protocol_T protocol);
|
||||
color_t PROTOCOLS_GetColor(Protocol_T protocol, uint8_t team_ID, uint8_t player_ID);
|
||||
TimedPulseTrain_T *PROTOCOLS_EncodePacket(TagPacket_T *packet);
|
||||
DecodedPacket_T *PROTOCOLS_MaybeDecodePacket(TimedPulseTrain_T *packet);
|
||||
|
||||
// Include the supported protocols.
|
||||
#include "Test.h"
|
||||
#include "Dubuque.h"
|
||||
#include "Miles_Tag_II.h"
|
||||
#include "NEC.h"
|
||||
#include "Nerf_Laser_Ops_Pro.h"
|
||||
#include "Nerf_Laser_Strike.h"
|
||||
#include "Nerf_Phoenix_LTX.h"
|
||||
#include "Dynasty.h"
|
||||
#include "Squad_Hero.h"
|
||||
#include "Laser_X.h"
|
||||
|
||||
#endif // PROTOCOLS_H
|
244
Protocols/Squad_Hero.c
Executable file
244
Protocols/Squad_Hero.c
Executable file
|
@ -0,0 +1,244 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
#define SQUAD_HERO_BLUE_TEAM 0x01
|
||||
#define SQUAD_HERO_RED_TEAM 0x02
|
||||
#define SQUAD_HERO_GREEN_TEAM 0x03
|
||||
#define SQUAD_HERO_ORANGE_TEAM 0x04
|
||||
#define SQUAD_HERO_WHITE_TEAM 0x06
|
||||
|
||||
#define SQUAD_HERO_HEADER_MARK_DURATION_IN_us 1500
|
||||
#define SQUAD_HERO_ZERO_DURATION_IN_us 400
|
||||
#define SQUAD_HERO_ONE_DURATION_IN_us 800
|
||||
#define SQUAD_HERO_TOLERANCE_IN_us 175
|
||||
|
||||
#define MIN_ZERO_IN_us (SQUAD_HERO_ZERO_DURATION_IN_us - SQUAD_HERO_TOLERANCE_IN_us)
|
||||
#define MAX_ZERO_IN_us (SQUAD_HERO_ZERO_DURATION_IN_us + SQUAD_HERO_TOLERANCE_IN_us)
|
||||
#define MIN_ONE_IN_us (SQUAD_HERO_ONE_DURATION_IN_us - SQUAD_HERO_TOLERANCE_IN_us)
|
||||
#define MAX_ONE_IN_us (SQUAD_HERO_ONE_DURATION_IN_us + SQUAD_HERO_TOLERANCE_IN_us)
|
||||
|
||||
static const TimedBit_T SQUAD_HERO_HEADER_MARK = {.symbol = MARK, .duration = SQUAD_HERO_HEADER_MARK_DURATION_IN_us};
|
||||
static const TimedBit_T SQUAD_HERO_ZERO_SPACE = {.symbol = SPACE, .duration = SQUAD_HERO_ZERO_DURATION_IN_us};
|
||||
static const TimedBit_T SQUAD_HERO_ONE_SPACE = {.symbol = SPACE, .duration = SQUAD_HERO_ONE_DURATION_IN_us};
|
||||
static const TimedBit_T SQUAD_HERO_ZERO_MARK = {.symbol = MARK, .duration = SQUAD_HERO_ZERO_DURATION_IN_us};
|
||||
static const TimedBit_T SQUAD_HERO_ONE_MARK = {.symbol = MARK, .duration = SQUAD_HERO_ONE_DURATION_IN_us};
|
||||
static const TimedBit_T SQUAD_HERO_END = {.symbol = SPACE, .duration = LAST_PULSE};
|
||||
|
||||
#define SQUAD_HERO_FOOTER 0x9E
|
||||
|
||||
static TimedPulseTrain_T Tag_Send_Buffer;
|
||||
static DecodedPacket_T Squad_Hero_Tag_Received_Buffers[3] =
|
||||
{
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE}};
|
||||
|
||||
static inline DecodedPacket_T *Get_Tag_Packet_Buffer(void)
|
||||
{
|
||||
if (Squad_Hero_Tag_Received_Buffers[0].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Squad_Hero_Tag_Received_Buffers[0];
|
||||
}
|
||||
else if (Squad_Hero_Tag_Received_Buffers[1].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Squad_Hero_Tag_Received_Buffers[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just use it.
|
||||
return &Squad_Hero_Tag_Received_Buffers[2];
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t Calculate_Checksum(uint32_t data)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void PackPulseTrain(TimedPulseTrain_T *pulsetrain, uint32_t data)
|
||||
{
|
||||
pulsetrain->bitstream[0] = SQUAD_HERO_HEADER_MARK;
|
||||
|
||||
for (uint8_t n = 0; n < 16; n++)
|
||||
{
|
||||
if ((data & 0x80000000) == 0x80000000)
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = SQUAD_HERO_ONE_SPACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = SQUAD_HERO_ZERO_SPACE;
|
||||
}
|
||||
data = data << 1;
|
||||
if ((data & 0x80000000) == 0x80000000)
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = SQUAD_HERO_ONE_MARK;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = SQUAD_HERO_ZERO_MARK;
|
||||
}
|
||||
data = data << 1;
|
||||
}
|
||||
|
||||
// Append the footer.
|
||||
uint8_t footer = SQUAD_HERO_FOOTER;
|
||||
for (uint8_t n = 16; n < 20; n++)
|
||||
{
|
||||
if ((footer & 0x80) == 0x80)
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = SQUAD_HERO_ONE_SPACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[1 + (n * 2)] = SQUAD_HERO_ZERO_SPACE;
|
||||
}
|
||||
footer = footer << 1;
|
||||
if ((footer & 0x80) == 0x80)
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = SQUAD_HERO_ONE_MARK;
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsetrain->bitstream[2 + (n * 2)] = SQUAD_HERO_ZERO_MARK;
|
||||
}
|
||||
footer = footer << 1;
|
||||
}
|
||||
|
||||
pulsetrain->bitstream[(2 * 20) + 1] = SQUAD_HERO_END;
|
||||
}
|
||||
|
||||
TimedPulseTrain_T *SQUAD_HERO_EncodePacket(TagPacket_T *packet)
|
||||
{
|
||||
uint32_t packed_data = 0x10AA0000;
|
||||
packed_data |= (packet->team_ID << 8);
|
||||
if (packet->damage > 4)
|
||||
{
|
||||
packet->damage = 4;
|
||||
}
|
||||
packed_data |= packet->damage;
|
||||
|
||||
PackPulseTrain(&Tag_Send_Buffer, packed_data);
|
||||
|
||||
return &Tag_Send_Buffer;
|
||||
}
|
||||
|
||||
DecodedPacket_T *SQUAD_HERO_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
||||
{
|
||||
// Some implementations (ESP32) do not report odd numbers of pulses.
|
||||
if ((packet->count != 41) && (packet->count != 42))
|
||||
{
|
||||
// Fail fast!
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert pulse durations to bits.
|
||||
// packet->bitstream[0] is the header mark--ignore it.
|
||||
|
||||
uint32_t data = 0;
|
||||
for (uint8_t n = 0; n < 32; n++)
|
||||
{
|
||||
if ((packet->bitstream[n + 1].duration > MIN_ONE_IN_us) && (packet->bitstream[n + 1].duration < MAX_ONE_IN_us))
|
||||
{
|
||||
// One
|
||||
data |= ((uint32_t)1 << (31 - n));
|
||||
}
|
||||
else if ((packet->bitstream[n + 1].duration > MIN_ZERO_IN_us) && (packet->bitstream[n + 1].duration < MAX_ZERO_IN_us))
|
||||
{
|
||||
// Zero--nothing to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
// This mark is neither a zero or a one; abort.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t checksum = 0;
|
||||
for (uint8_t n = 0; n < 8; n++)
|
||||
{
|
||||
if ((packet->bitstream[n + 33].duration > MIN_ONE_IN_us) && (packet->bitstream[n + 33].duration < MAX_ONE_IN_us))
|
||||
{
|
||||
// One
|
||||
checksum |= ((uint8_t)1 << (7 - n));
|
||||
}
|
||||
else if ((packet->bitstream[n + 33].duration > MIN_ZERO_IN_us) && (packet->bitstream[n + 33].duration < MAX_ZERO_IN_us))
|
||||
{
|
||||
// Zero--nothing to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
// This mark is neither a zero or a one; abort.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
|
||||
Tag_Rx_Buffer->Tag.protocol = SQUAD_HERO_PROTOCOL;
|
||||
Tag_Rx_Buffer->Tag.player_ID = 0;
|
||||
Tag_Rx_Buffer->Tag.team_ID = (data & 0x0000FF00) >> 8;
|
||||
Tag_Rx_Buffer->Tag.damage = (data & 0x000000FF);
|
||||
Tag_Rx_Buffer->Tag.color = SQUAD_HERO_GetTeamColor(Tag_Rx_Buffer->Tag.team_ID);
|
||||
return Tag_Rx_Buffer;
|
||||
}
|
||||
}
|
||||
|
||||
color_t SQUAD_HERO_GetTeamColor(uint8_t team_ID)
|
||||
{
|
||||
color_t result = COLOR_WHITE;
|
||||
|
||||
if (team_ID == SQUAD_HERO_BLUE_TEAM)
|
||||
{
|
||||
result = COLOR_BLUE;
|
||||
}
|
||||
else if (team_ID == SQUAD_HERO_RED_TEAM)
|
||||
{
|
||||
result = COLOR_RED;
|
||||
}
|
||||
else if (team_ID == SQUAD_HERO_GREEN_TEAM)
|
||||
{
|
||||
result = COLOR_GREEN;
|
||||
}
|
||||
else if (team_ID == SQUAD_HERO_ORANGE_TEAM)
|
||||
{
|
||||
result = COLOR_ORANGE;
|
||||
}
|
||||
else if (team_ID == SQUAD_HERO_WHITE_TEAM)
|
||||
{
|
||||
result = COLOR_WHITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
37
Protocols/Squad_Hero.h
Executable file
37
Protocols/Squad_Hero.h
Executable file
|
@ -0,0 +1,37 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SQUAD_HERO_H
|
||||
#define SQUAD_HERO_H
|
||||
|
||||
/** \file
|
||||
* \brief This is the Squad Hero protocol.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
TimedPulseTrain_T *SQUAD_HERO_EncodePacket(TagPacket_T *packet);
|
||||
DecodedPacket_T *SQUAD_HERO_MaybeDecodePacket(TimedPulseTrain_T *packet);
|
||||
color_t SQUAD_HERO_GetTeamColor(uint8_t team_ID);
|
||||
|
||||
#endif // SQUAD_HERO_H
|
144
Protocols/Test.c
Executable file
144
Protocols/Test.c
Executable file
|
@ -0,0 +1,144 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SystemK.h"
|
||||
|
||||
static const char *KLOG_TAG = "Test Protocol";
|
||||
|
||||
#define TEST_PULSE_SUBTRAHEND_IN_us 300
|
||||
//! The shortest test packet is a two marks, the first of duration (3 * TEST_PULSE_SUBTRAHEND_IN_us), and the second of duration TEST_PULSE_SUBTRAHEND_IN_us.
|
||||
#define TEST_MINIMUM_LONGEST_PULSE_DURATION_IN_us (3 * TEST_PULSE_SUBTRAHEND_IN_us)
|
||||
#define TEST_TOLERANCE_IN_us ((TEST_PULSE_SUBTRAHEND_IN_us / 2) - 1)
|
||||
|
||||
static TimedPulseTrain_T Tag_Send_Buffer;
|
||||
static DecodedPacket_T Test_Tag_Received_Buffers[3] =
|
||||
{
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
||||
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE}};
|
||||
|
||||
static inline DecodedPacket_T *Get_Tag_Packet_Buffer(void)
|
||||
{
|
||||
if (Test_Tag_Received_Buffers[0].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Test_Tag_Received_Buffers[0];
|
||||
}
|
||||
else if (Test_Tag_Received_Buffers[1].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
||||
{
|
||||
return &Test_Tag_Received_Buffers[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just use it.
|
||||
return &Test_Tag_Received_Buffers[2];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void PackPulseTrain(TimedPulseTrain_T *pulsetrain, uint32_t longest_pulse_duration_in_us)
|
||||
{
|
||||
uint_fast8_t index = 0;
|
||||
uint32_t next_pulse_duration_in_us = longest_pulse_duration_in_us;
|
||||
TimedBit_T next_bit;
|
||||
|
||||
if (next_pulse_duration_in_us < TEST_MINIMUM_LONGEST_PULSE_DURATION_IN_us)
|
||||
{
|
||||
next_pulse_duration_in_us = TEST_MINIMUM_LONGEST_PULSE_DURATION_IN_us;
|
||||
}
|
||||
|
||||
while ((next_pulse_duration_in_us >= TEST_PULSE_SUBTRAHEND_IN_us) && (index < MAX_PULSES))
|
||||
{
|
||||
// Even indicies are marks.
|
||||
if ((index % 2) == 0)
|
||||
{
|
||||
next_bit.symbol = MARK;
|
||||
}
|
||||
// Odd indicies are spaces.
|
||||
else
|
||||
{
|
||||
next_bit.symbol = SPACE;
|
||||
}
|
||||
next_bit.duration = next_pulse_duration_in_us;
|
||||
pulsetrain->bitstream[index] = next_bit;
|
||||
|
||||
next_pulse_duration_in_us = next_pulse_duration_in_us - TEST_PULSE_SUBTRAHEND_IN_us;
|
||||
index++;
|
||||
}
|
||||
|
||||
// Mark the end of the train.
|
||||
next_bit.symbol = SPACE;
|
||||
next_bit.duration = LAST_PULSE;
|
||||
pulsetrain->bitstream[index] = next_bit;
|
||||
pulsetrain->count = index;
|
||||
}
|
||||
|
||||
TimedPulseTrain_T *TEST_EncodePacket(TagPacket_T *packet)
|
||||
{
|
||||
uint32_t packed_data = 2100;
|
||||
|
||||
PackPulseTrain(&Tag_Send_Buffer, packed_data);
|
||||
|
||||
return &Tag_Send_Buffer;
|
||||
}
|
||||
|
||||
DecodedPacket_T *TEST_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
||||
{
|
||||
// packet->bitstream[0] is the longest pulse--assume it was received correctly.
|
||||
uint32_t longest_pulse_duration_in_us = packet->bitstream[0].duration;
|
||||
|
||||
if ((longest_pulse_duration_in_us - TEST_TOLERANCE_IN_us) < TEST_MINIMUM_LONGEST_PULSE_DURATION_IN_us)
|
||||
{
|
||||
// The first pulse is too short!
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (uint_fast8_t index = 1; index < packet->count; index++)
|
||||
{
|
||||
uint32_t expected_pulse_duration_in_us = longest_pulse_duration_in_us - (index * TEST_PULSE_SUBTRAHEND_IN_us);
|
||||
|
||||
if (packet->bitstream[index].duration < (expected_pulse_duration_in_us - TEST_TOLERANCE_IN_us))
|
||||
{
|
||||
KLOG_WARN(KLOG_TAG, "Pulse %u is too short! Expected %lu; received %u.", index, expected_pulse_duration_in_us, packet->bitstream[index].duration);
|
||||
return NULL;
|
||||
}
|
||||
if (packet->bitstream[index].duration > (expected_pulse_duration_in_us + TEST_TOLERANCE_IN_us))
|
||||
{
|
||||
KLOG_WARN(KLOG_TAG, "Pulse %u is too long! Expected %lu; received %u.", index, expected_pulse_duration_in_us, packet->bitstream[index].duration);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DecodedPacket_T *Command_Rx_Buffer = Get_Tag_Packet_Buffer();
|
||||
Command_Rx_Buffer->Command.type = DECODED_PACKET_TYPE_COMMAND_RECEIVED;
|
||||
Command_Rx_Buffer->Command.protocol = TEST_PROTOCOL;
|
||||
Command_Rx_Buffer->Command.data = longest_pulse_duration_in_us;
|
||||
return Command_Rx_Buffer;
|
||||
}
|
||||
|
||||
color_t TEST_GetTeamColor(__attribute__((unused)) uint8_t team_ID)
|
||||
{
|
||||
color_t result = COLOR_WHITE;
|
||||
|
||||
return result;
|
||||
}
|
38
Protocols/Test.h
Executable file
38
Protocols/Test.h
Executable file
|
@ -0,0 +1,38 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TEST_H
|
||||
#define TEST_H
|
||||
|
||||
/** \file
|
||||
* \brief This is the test protocol.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
TimedPulseTrain_T *TEST_EncodePacket(TagPacket_T *packet);
|
||||
DecodedPacket_T *TEST_MaybeDecodePacket(TimedPulseTrain_T *packet);
|
||||
color_t TEST_GetTeamColor(uint8_t team_ID);
|
||||
|
||||
#endif // TEST_H
|
50
Results.h
Normal file
50
Results.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* This program source code file is part of SystemK, a library in the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief This file defines the possible results of calls to the KTag system (SystemK).
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RESULTS_H
|
||||
#define RESULTS_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SYSTEMK_RESULT_SUCCESS = 0,
|
||||
SYSTEMK_RESULT_UNSPECIFIED_FAILURE,
|
||||
SYSTEMK_RESULT_NOT_IMPLEMENTED,
|
||||
SYSTEMK_RESULT_NOT_ENOUGH_MEMORY,
|
||||
SYSTEMK_RESULT_WRONG_DATATYPE,
|
||||
SYSTEMK_RESULT_TOO_FEW_DATA,
|
||||
SYSTEMK_RESULT_TOO_MANY_DATA,
|
||||
SYSTEMK_RESULT_QUEUE_IS_FULL,
|
||||
SYSTEMK_RESULT_NOT_READY,
|
||||
SYSTEMK_RESULT_FAILED_TO_CREATE_RTOS_TASK,
|
||||
SYSTEMK_RESULT_FILE_NOT_FOUND,
|
||||
SYSTEMK_RESULT_KEY_NOT_FOUND,
|
||||
SYSTEMK_RESULT_READ_FAILED,
|
||||
SYSTEMK_RESULT_WRITE_FAILED,
|
||||
SYSTEMK_RESULT_OVERFLOW,
|
||||
SYSTEMK_RESULT_UNDERFLOW
|
||||
} SystemKResult_T;
|
||||
|
||||
#endif // RESULTS_H
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue