Initial public release of the 2024A software.

This commit is contained in:
Joe Kearney 2025-01-25 14:04:42 -06:00
parent 7b9ad3edfd
commit 303e9e1dad
361 changed files with 60083 additions and 2 deletions

362
components/IR/IR.c Normal file
View file

@ -0,0 +1,362 @@
#include <SystemK.h>
#include <driver/gpio.h>
#include <driver/rmt_tx.h>
#include <driver/rmt_rx.h>
#define IR_RESOLUTION_HZ (1 * 1000 * 1000) // 1MHz resolution, 1 tick = 1us
#define IR_TX_PIN GPIO_NUM_6
#define IR_TX_RIGHT_ENABLE GPIO_NUM_7
#define IR_TX_LEFT_ENABLE GPIO_NUM_5
#define IR_RX_LEFT_PIN GPIO_NUM_2
#define IR_RX_FORWARD_PIN GPIO_NUM_15
#define IR_RX_RIGHT_PIN GPIO_NUM_42
static const char *TAG = "IR";
static rmt_channel_handle_t Tx_Channel = NULL;
static gpio_config_t tx_right_enable_gpio_config = {
.pin_bit_mask = (1ULL << IR_TX_RIGHT_ENABLE),
.mode = GPIO_MODE_OUTPUT, // Set mode to output
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE};
static gpio_config_t tx_left_enable_gpio_config = {
.pin_bit_mask = (1ULL << IR_TX_LEFT_ENABLE),
.mode = GPIO_MODE_OUTPUT, // Set mode to output
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE};
// Create simple encoder
rmt_copy_encoder_config_t Copy_Encoder_Config;
rmt_encoder_handle_t Tx_Encoder;
rmt_transmit_config_t Tx_Config = {
.loop_count = 0, // no transfer loop
};
rmt_receive_config_t Rx_Config = {
.signal_range_min_ns = 5 * 1000, // A pulse whose width is smaller than this threshold will be treated as glitch and ignored.
.signal_range_max_ns = 20 * 1000 * 1000, // RMT will stop receiving if one symbol level has kept longer than this value.
.flags.en_partial_rx = false};
static rmt_channel_handle_t Left_Rx_Channel = NULL;
static rmt_channel_handle_t Forward_Rx_Channel = NULL;
static rmt_channel_handle_t Right_Rx_Channel = NULL;
static QueueHandle_t Receive_Queue;
static TimedPulseTrain_T Left_Rxd_RMT_Data;
static TimedPulseTrain_T Forward_Rxd_RMT_Data;
static TimedPulseTrain_T Right_Rxd_RMT_Data;
#define IR_RX_STACK_SIZE 4096
static StaticTask_t xTaskBuffer;
static StackType_t xStack[IR_RX_STACK_SIZE];
static TaskHandle_t IR_Rx_Task_Handle;
static TagPacket_T Shot_Packet;
typedef struct
{
uint8_t count;
TagSensorLocation_T location;
} RxNotification_T;
static bool RMT_Rx_Left_Done_Callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
RxNotification_T notice;
notice.count = edata->num_symbols * 2;
notice.location = TAG_SENSOR_LEFT;
xQueueSendFromISR(Receive_Queue, &notice, &xHigherPriorityTaskWoken);
return xHigherPriorityTaskWoken;
}
static bool RMT_Rx_Forward_Done_Callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
RxNotification_T notice;
notice.count = edata->num_symbols * 2;
notice.location = TAG_SENSOR_FORWARD;
xQueueSendFromISR(Receive_Queue, &notice, &xHigherPriorityTaskWoken);
return xHigherPriorityTaskWoken;
}
static bool RMT_Rx_Right_Done_Callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
RxNotification_T notice;
notice.count = edata->num_symbols * 2;
notice.location = TAG_SENSOR_RIGHT;
xQueueSendFromISR(Receive_Queue, &notice, &xHigherPriorityTaskWoken);
return xHigherPriorityTaskWoken;
}
static const rmt_rx_event_callbacks_t left_cbs = {
.on_recv_done = RMT_Rx_Left_Done_Callback,
};
static const rmt_rx_event_callbacks_t forward_cbs = {
.on_recv_done = RMT_Rx_Forward_Done_Callback,
};
static const rmt_rx_event_callbacks_t right_cbs = {
.on_recv_done = RMT_Rx_Right_Done_Callback,
};
static void IR_Receive_Task(void *param)
{
KLOG_INFO(TAG, "IR Receive Task Started");
while (true)
{
RxNotification_T notice;
DecodedPacket_T *result = NULL;
if (xQueueReceive(Receive_Queue, &notice, portMAX_DELAY) == pdPASS)
{
if (notice.location == TAG_SENSOR_FORWARD)
{
KLOG_INFO(TAG, "TAG_SENSOR_FORWARD Rx'd");
Forward_Rxd_RMT_Data.count = notice.count;
result = PROTOCOLS_MaybeDecodePacket(&Forward_Rxd_RMT_Data);
}
else if (notice.location == TAG_SENSOR_LEFT)
{
KLOG_INFO(TAG, "TAG_SENSOR_LEFT Rx'd");
Left_Rxd_RMT_Data.count = notice.count;
result = PROTOCOLS_MaybeDecodePacket(&Left_Rxd_RMT_Data);
}
else if (notice.location == TAG_SENSOR_RIGHT)
{
KLOG_INFO(TAG, "TAG_SENSOR_RIGHT Rx'd");
Right_Rxd_RMT_Data.count = notice.count;
result = PROTOCOLS_MaybeDecodePacket(&Right_Rxd_RMT_Data);
}
if (result != NULL)
{
if (result->Generic.type == DECODED_PACKET_TYPE_TAG_RECEIVED)
{
KEvent_T tag_received_event = {.ID = KEVENT_TAG_RECEIVED, .Data = result};
Post_KEvent(&tag_received_event);
}
else if (result->Generic.type == DECODED_PACKET_TYPE_COMMAND_RECEIVED)
{
KEvent_T command_received_event = {.ID = KEVENT_COMMAND_RECEIVED, .Data = result};
Post_KEvent(&command_received_event);
}
}
else
{
KEvent_T near_miss_event = {.ID = KEVENT_NEAR_MISS, .Data = NULL};
Post_KEvent(&near_miss_event);
}
// Start receiving again.
// We need to reset all three channels for some reason--why?
{
ESP_ERROR_CHECK(rmt_disable(Forward_Rx_Channel));
ESP_ERROR_CHECK(rmt_enable(Forward_Rx_Channel));
ESP_ERROR_CHECK(rmt_receive(Forward_Rx_Channel, &Forward_Rxd_RMT_Data, sizeof(Forward_Rxd_RMT_Data.pulsetrain), &Rx_Config));
}
{
ESP_ERROR_CHECK(rmt_disable(Left_Rx_Channel));
ESP_ERROR_CHECK(rmt_enable(Left_Rx_Channel));
ESP_ERROR_CHECK(rmt_receive(Left_Rx_Channel, &Left_Rxd_RMT_Data, sizeof(Left_Rxd_RMT_Data.pulsetrain), &Rx_Config));
}
{
ESP_ERROR_CHECK(rmt_disable(Right_Rx_Channel));
ESP_ERROR_CHECK(rmt_enable(Right_Rx_Channel));
ESP_ERROR_CHECK(rmt_receive(Right_Rx_Channel, &Right_Rxd_RMT_Data, sizeof(Right_Rxd_RMT_Data.pulsetrain), &Rx_Config));
}
}
}
}
static void Initialize_Receive_Task(void)
{
IR_Rx_Task_Handle = xTaskCreateStaticPinnedToCore(
IR_Receive_Task, // Function that implements the task.
"IR Rx", // Text name for the task.
IR_RX_STACK_SIZE, // Stack size in words, not bytes.
0, // Parameter passed into the task.
tskIDLE_PRIORITY + 1, // Priority at which the task is created.
xStack, // Array to use as the task's stack.
&xTaskBuffer, // Variable to hold the task's data structure.
APP_CPU_NUM); // Core where the task should run.
if (IR_Rx_Task_Handle == NULL)
{
KLOG_ERROR(TAG, "Failed to create IR Receive task!");
}
}
void Initialize_IR(SemaphoreHandle_t init_complete)
{
KLOG_INFO(TAG, "Initializing IR...");
KLOG_INFO(TAG, "Creating RMT TX channel...");
rmt_tx_channel_config_t tx_channel_cfg = {
.gpio_num = IR_TX_PIN, // GPIO number used by RMT TX channel.
.clk_src = RMT_CLK_SRC_XTAL, // Clock source of RMT TX channel, channels in the same group must use the same clock source
.resolution_hz = IR_RESOLUTION_HZ, // Channel clock resolution, in Hz.
.mem_block_symbols = 48, // If DMA is not used, this field controls the size of the dedicated memory block owned by the channel.
// THIS IS WRONG IN THE DOCS! See https://github.com/espressif/esp-idf/issues/12084#issuecomment-1679881770.
.trans_queue_depth = 1, // Depth of the internal transaction queue.
.flags.invert_out = false, // Should the signal be inverted before sending it to the GPIO?
.flags.with_dma = true, // Should this channel use the DMA backend?
.flags.io_loop_back = false, // Should the signal output from the GPIO be fed to the input path as well?
.flags.io_od_mode = false, // Should the GPIO be configured in open-drain mode?
.intr_priority = 1 // RMT interrupt priority. If set to 0 , then the driver will use a interrupt with low or medium priority (priority level may be one of 1, 2, or 3).
};
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_channel_cfg, &Tx_Channel));
KLOG_INFO(TAG, "Modulate carrier to TX channel");
rmt_carrier_config_t carrier_cfg = {
.duty_cycle = 0.30, // 30% duty cycle
.frequency_hz = 38000, // 38KHz
};
ESP_ERROR_CHECK(rmt_apply_carrier(Tx_Channel, &carrier_cfg));
ESP_ERROR_CHECK(rmt_enable(Tx_Channel));
ESP_ERROR_CHECK(gpio_config(&tx_right_enable_gpio_config));
KLOG_INFO(TAG, "Initialized right IR Tx enable as GPIO[%d].", IR_TX_RIGHT_ENABLE);
ESP_ERROR_CHECK(gpio_config(&tx_left_enable_gpio_config));
KLOG_INFO(TAG, "Initialized left IR Tx enable as GPIO[%d].", IR_TX_LEFT_ENABLE);
ESP_ERROR_CHECK(rmt_new_copy_encoder(&Copy_Encoder_Config, &Tx_Encoder));
KLOG_INFO(TAG, "Creating RMT Rx channels...");
rmt_rx_channel_config_t rx_left_channel_cfg = {
.gpio_num = IR_RX_LEFT_PIN, // GPIO number used by RMT RX channel. Set to -1 if unused.
.clk_src = RMT_CLK_SRC_XTAL, // Clock source of RMT RX channel; channels in the same group must use the same clock source.
.resolution_hz = IR_RESOLUTION_HZ, // Channel clock resolution, in Hz.
.mem_block_symbols = 48, // Size of memory block, in number of `rmt_symbol_word_t`, must be an even.
// In the DMA mode, this field controls the DMA buffer size, it can be set to a large value (e.g. 1024);
// In the normal mode, this field controls the number of RMT memory block that will be used by the channel.
.flags.invert_in = true, // Should the incoming signal be inverted before processing?
.flags.with_dma = false, // Should this channel use the DMA backend?
.flags.io_loop_back = false, // Should the signal output from the GPIO be fed to the input path as well?
.intr_priority = 1 // RMT interrupt priority. If set to 0 , then the driver will use a interrupt with low or medium priority (priority level may be one of 1, 2, or 3).
};
ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_left_channel_cfg, &Left_Rx_Channel));
rmt_rx_channel_config_t rx_forward_channel_cfg = {
.gpio_num = IR_RX_FORWARD_PIN, // GPIO number used by RMT RX channel. Set to -1 if unused.
.clk_src = RMT_CLK_SRC_XTAL, // Clock source of RMT RX channel; channels in the same group must use the same clock source.
.resolution_hz = IR_RESOLUTION_HZ, // Channel clock resolution, in Hz.
.mem_block_symbols = 48, // Size of memory block, in number of `rmt_symbol_word_t`, must be an even.
// In the DMA mode, this field controls the DMA buffer size, it can be set to a large value (e.g. 1024);
// In the normal mode, this field controls the number of RMT memory block that will be used by the channel.
.flags.invert_in = true, // Should the incoming signal be inverted before processing?
.flags.with_dma = false, // Should this channel use the DMA backend?
.flags.io_loop_back = false, // Should the signal output from the GPIO be fed to the input path as well?
.intr_priority = 1 // RMT interrupt priority. If set to 0 , then the driver will use a interrupt with low or medium priority (priority level may be one of 1, 2, or 3).
};
ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_forward_channel_cfg, &Forward_Rx_Channel));
rmt_rx_channel_config_t rx_right_channel_cfg = {
.gpio_num = IR_RX_RIGHT_PIN, // GPIO number used by RMT RX channel. Set to -1 if unused.
.clk_src = RMT_CLK_SRC_XTAL, // Clock source of RMT RX channel; channels in the same group must use the same clock source.
.resolution_hz = IR_RESOLUTION_HZ, // Channel clock resolution, in Hz.
.mem_block_symbols = 48, // Size of memory block, in number of `rmt_symbol_word_t`, must be an even.
// In the DMA mode, this field controls the DMA buffer size, it can be set to a large value (e.g. 1024);
// In the normal mode, this field controls the number of RMT memory block that will be used by the channel.
.flags.invert_in = true, // Should the incoming signal be inverted before processing?
.flags.with_dma = false, // Should this channel use the DMA backend?
.flags.io_loop_back = false, // Should the signal output from the GPIO be fed to the input path as well?
.intr_priority = 1 // RMT interrupt priority. If set to 0 , then the driver will use a interrupt with low or medium priority (priority level may be one of 1, 2, or 3).
};
ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_right_channel_cfg, &Right_Rx_Channel));
ESP_LOGI(TAG, "register RX done callbacks");
Receive_Queue = xQueueCreate(1, sizeof(RxNotification_T));
assert(Receive_Queue);
ESP_ERROR_CHECK(rmt_rx_register_event_callbacks(Left_Rx_Channel, &left_cbs, 0));
ESP_ERROR_CHECK(rmt_rx_register_event_callbacks(Forward_Rx_Channel, &forward_cbs, 0));
ESP_ERROR_CHECK(rmt_rx_register_event_callbacks(Right_Rx_Channel, &right_cbs, 0));
Initialize_Receive_Task();
Left_Rxd_RMT_Data.receiver = TAG_SENSOR_LEFT;
Forward_Rxd_RMT_Data.receiver = TAG_SENSOR_FORWARD;
Right_Rxd_RMT_Data.receiver = TAG_SENSOR_RIGHT;
ESP_ERROR_CHECK(rmt_enable(Left_Rx_Channel));
ESP_ERROR_CHECK(rmt_receive(Left_Rx_Channel, &Left_Rxd_RMT_Data, sizeof(Left_Rxd_RMT_Data.pulsetrain), &Rx_Config));
ESP_ERROR_CHECK(rmt_enable(Forward_Rx_Channel));
ESP_ERROR_CHECK(rmt_receive(Forward_Rx_Channel, &Forward_Rxd_RMT_Data, sizeof(Forward_Rxd_RMT_Data.pulsetrain), &Rx_Config));
ESP_ERROR_CHECK(rmt_enable(Right_Rx_Channel));
ESP_ERROR_CHECK(rmt_receive(Right_Rx_Channel, &Right_Rxd_RMT_Data, sizeof(Right_Rxd_RMT_Data.pulsetrain), &Rx_Config));
xSemaphoreGive(init_complete);
}
static inline void PrintPulseTrainToConsole(TimedPulseTrain_T *train)
{
for (uint_fast16_t i = 0; i < train->count; i += 2)
{
KLOG_INFO(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));
}
}
SystemKResult_T Prepare_Tag(void)
{
TimedPulseTrain_T *Shot_Buffer;
uint8_t team_ID;
uint8_t player_ID;
uint8_t weapon_ID;
(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);
Weapon_t weapon = GetWeaponFromID(weapon_ID);
Shot_Packet.player_ID = player_ID;
Shot_Packet.team_ID = team_ID;
Shot_Packet.color = (uint32_t)PROTOCOLS_GetColor(weapon.Protocol, team_ID, player_ID);
Shot_Packet.protocol = weapon.Protocol;
Shot_Packet.damage = weapon.Damage_Per_Shot;
Shot_Buffer = PROTOCOLS_EncodePacket(&Shot_Packet);
KLOG_INFO(TAG, "Tag prepared (%u pulses):", Shot_Buffer->count);
PrintPulseTrainToConsole(Shot_Buffer);
gpio_set_level(IR_TX_RIGHT_ENABLE, 0);
gpio_set_level(IR_TX_LEFT_ENABLE, 0);
return SYSTEMK_RESULT_SUCCESS;
}
SystemKResult_T Send_Tag(void)
{
TimedPulseTrain_T *Shot_Buffer;
Shot_Buffer = PROTOCOLS_EncodePacket(&Shot_Packet);
ESP_ERROR_CHECK(rmt_transmit(Tx_Channel, Tx_Encoder, Shot_Buffer->pulsetrain, sizeof(rmt_symbol_word_t) * Shot_Buffer->count, &Tx_Config));
KEvent_T tag_sent_event = {.ID = KEVENT_TAG_SENT, .Data = (void *)0x00};
Post_KEvent(&tag_sent_event);
return SYSTEMK_RESULT_SUCCESS;
}