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

14
main/CMakeLists.txt Normal file
View file

@ -0,0 +1,14 @@
idf_component_register(
SRCS
"HW_NeoPixels.c"
"main.c"
INCLUDE_DIRS
"."
)
# Create a SPIFFS image from the contents of the 'spiffs_image' directory
# that fits the partition named 'spiffs'. FLASH_IN_PROJECT indicates that
# the generated image should be flashed when the entire project is flashed to
# the target with 'idf.py -p PORT flash'.
#spiffs_create_partition_image(spiffs ../spiffs_image FLASH_IN_PROJECT)
spiffs_create_partition_image(spiffs ../spiffs_image)

230
main/HW_NeoPixels.c Normal file
View file

@ -0,0 +1,230 @@
#include <SystemK.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <string.h>
#include <muxed_led_strip.h>
#include <muxed_led_strip_interface.h>
#include <driver/gpio.h>
#include <driver/rmt_tx.h>
#include "HW_NeoPixels.h"
#define NEOPIXELS_STACK_SIZE (100 * 1024)
#define NEOPIXELS_TASK_PRIORITY (tskIDLE_PRIORITY + 1)
static StaticTask_t xTaskBuffer;
StackType_t *xStack;
static TaskHandle_t xTaskHandle;
static muxed_led_strip_handle_t NeoPixel_Out;
// GPIO assignments
#define NEOPIXEL_OUT_GPIO GPIO_NUM_48
#define BARREL_ENABLE_GPIO GPIO_NUM_18
#define RECEIVER_ENABLE_GPIO GPIO_NUM_45
#define DISPLAY_ENABLE_GPIO GPIO_NUM_10
#define EFFECTS_ENABLE_GPIO GPIO_NUM_17
// 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
static const char *TAG = "NeoPixels";
static inline void Enable_Channel(NeoPixelsChannel_T channel)
{
// The Enable lines are active low.
if (channel == NEOPIXEL_CHANNEL_ALL)
{
gpio_set_level(BARREL_ENABLE_GPIO, 0);
gpio_set_level(RECEIVER_ENABLE_GPIO, 0);
gpio_set_level(DISPLAY_ENABLE_GPIO, 0);
gpio_set_level(EFFECTS_ENABLE_GPIO, 0);
}
else if (channel < CONFIG_KTAG_N_NEOPIXEL_CHANNELS)
{
gpio_set_level(BARREL_ENABLE_GPIO, channel == NEOPIXEL_CHANNEL_BARREL ? 0 : 1);
gpio_set_level(RECEIVER_ENABLE_GPIO, channel == NEOPIXEL_CHANNEL_RECEIVER ? 0 : 1);
gpio_set_level(DISPLAY_ENABLE_GPIO, channel == NEOPIXEL_CHANNEL_DISPLAY ? 0 : 1);
gpio_set_level(EFFECTS_ENABLE_GPIO, channel == NEOPIXEL_CHANNEL_EFFECTS ? 0 : 1);
}
else
{
// Select none.
gpio_set_level(BARREL_ENABLE_GPIO, 1);
gpio_set_level(RECEIVER_ENABLE_GPIO, 1);
gpio_set_level(DISPLAY_ENABLE_GPIO, 1);
gpio_set_level(EFFECTS_ENABLE_GPIO, 1);
}
}
static bool IRAM_ATTR rmt_tx_done_callback(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *edata, void *user_data)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// Send a notification to the task that the current channel is complete.
xTaskNotifyIndexedFromISR(xTaskHandle,
0,
0,
eSetBits,
&xHigherPriorityTaskWoken);
return xHigherPriorityTaskWoken;
}
static rmt_tx_event_callbacks_t cbs = {
.on_trans_done = rmt_tx_done_callback,
};
static gpio_config_t barrel_enable_gpio_config = {
.pin_bit_mask = (1ULL << BARREL_ENABLE_GPIO),
.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 receiver_enable_gpio_config = {
.pin_bit_mask = (1ULL << RECEIVER_ENABLE_GPIO),
.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 display_enable_gpio_config = {
.pin_bit_mask = (1ULL << DISPLAY_ENABLE_GPIO),
.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 effects_enable_gpio_config = {
.pin_bit_mask = (1ULL << EFFECTS_ENABLE_GPIO),
.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 muxed_led_strip_config_t neopixel_out_config = {
.strip_gpio_num = NEOPIXEL_OUT_GPIO, // The GPIO that connected to the LED strip's data line
.channels = CONFIG_KTAG_N_NEOPIXEL_CHANNELS, // The number of multiplexed channels
.max_leds = CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL, // The number of LEDs in the strip
.led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
.led_model = LED_MODEL_WS2812, // LED strip model
.flags.invert_out = false, // whether to invert the output signal
};
// From <https://www.espressif.com/sites/default/files/documentation/esp32-s3_technical_reference_manual_en.pdf#rmt>:
//
// The RMT module has eight channels, numbered from zero to seven. Each channel is able to independently
// transmit or receive signals.
// • Channel 0 ~ 3 (TX channel) are dedicated to sending signals.
// • Channel 4 ~ 7 (RX channel) are dedicated to receiving signals.
// Each TX/RX channel is controlled by a dedicated set of registers with the same functionality. Channel 3 and
// channel 7 support DMA access, so the two channels also have a set of DMA-related control and status registers
static led_strip_rmt_config_t rmt_config_noDMA = {
.clk_src = RMT_CLK_SRC_XTAL, // different clock source can lead to different power consumption
.resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency
.flags.with_dma = false, // Only the last channel has the DMA capability
};
static led_strip_spi_config_t spi_config_withDMA __attribute__((unused)) = {
.clk_src = RMT_CLK_SRC_XTAL, // different clock source can lead to different power consumption
.flags.with_dma = true, // Using DMA can improve performance and help drive more LEDs
.spi_bus = SPI2_HOST, // SPI bus ID
};
void Initialize_SystemK_NeoPixels(SemaphoreHandle_t init_complete)
{
xStack = (uint8_t *)heap_caps_calloc(1, NEOPIXELS_STACK_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT | MALLOC_CAP_32BIT);
// Create the task without using any dynamic memory allocation.
xTaskHandle = xTaskCreateStaticPinnedToCore(
NeoPixels_Task, // Function that implements the task (this is part of SystemK).
"NeoPixels", // Text name for the task.
NEOPIXELS_STACK_SIZE, // Number of indexes in the xStack array.
NULL, // Parameter passed into the task.
NEOPIXELS_TASK_PRIORITY, // 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); // Specify the task's core affinity.
KLOG_INFO(TAG, "Initialization complete.");
xSemaphoreGive(init_complete);
}
SystemKResult_T HW_NeoPixels_Init(void)
{
if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS > 0)
{
// Initialize the NeoPixel Out and the Barrel Enable.
ESP_ERROR_CHECK(muxed_led_strip_new_rmt_device(&neopixel_out_config, &rmt_config_noDMA, &cbs, &NeoPixel_Out));
KLOG_INFO(TAG, "Initialized NeoPixel Out as GPIO[%d].", NEOPIXEL_OUT_GPIO);
ESP_ERROR_CHECK(gpio_config(&barrel_enable_gpio_config));
KLOG_INFO(TAG, "Initialized barrel NeoPixel enable as GPIO[%d].", BARREL_ENABLE_GPIO);
}
if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS > 1)
{
ESP_ERROR_CHECK(gpio_config(&receiver_enable_gpio_config));
KLOG_INFO(TAG, "Initialized receiver NeoPixel enable as GPIO[%d].", RECEIVER_ENABLE_GPIO);
}
if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS > 2)
{
ESP_ERROR_CHECK(gpio_config(&display_enable_gpio_config));
KLOG_INFO(TAG, "Initialized display NeoPixel enable as GPIO[%d].", DISPLAY_ENABLE_GPIO);
}
if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS > 3)
{
ESP_ERROR_CHECK(gpio_config(&effects_enable_gpio_config));
KLOG_INFO(TAG, "Initialized effects NeoPixel enable as GPIO[%d].", EFFECTS_ENABLE_GPIO);
}
return SYSTEMK_RESULT_SUCCESS;
}
SystemKResult_T HW_NeoPixels_Set_RGB(NeoPixelsChannel_T channel, uint8_t position, uint8_t red, uint8_t green, uint8_t blue)
{
if (channel < CONFIG_KTAG_N_NEOPIXEL_CHANNELS)
{
// TODO: Add code to account for RGB order in each channel.
ESP_ERROR_CHECK(muxed_led_strip_set_pixel(NeoPixel_Out, channel, position, red, green, blue));
}
return SYSTEMK_RESULT_SUCCESS;
}
SystemKResult_T HW_NeoPixels_Publish(void)
{
for (uint_fast8_t channel = 0; channel < CONFIG_KTAG_N_NEOPIXEL_CHANNELS; channel++)
{
uint32_t channel_complete;
Enable_Channel(channel);
ESP_ERROR_CHECK_WITHOUT_ABORT(muxed_led_strip_refresh(NeoPixel_Out, channel));
xTaskNotifyWaitIndexed(0, /* Wait for 0th Notification */
0x00, /* Don't clear any bits on entry. */
UINT32_MAX, /* Clear all bits on exit. */
&channel_complete, /* Receives the notification value. */
portMAX_DELAY); /* Block indefinitely. */
// KLOG_INFO(TAG, "Published %lu.", channel_complete);
}
Enable_Channel(NEOPIXEL_CHANNEL_NONE);
return SYSTEMK_RESULT_SUCCESS;
}
color_t HW_NeoPixels_Get_My_Color(void)
{
color_t result = COLOR_ORANGE;
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);
result = PROTOCOLS_GetColor(GetWeaponFromID(Weapon_ID).Protocol, Team_ID, Player_ID);
return result;
}

6
main/HW_NeoPixels.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef HW_NEOPIXELS_H
#define HW_NEOPIXELS_H
void Initialize_SystemK_NeoPixels(SemaphoreHandle_t init_complete);
#endif // HW_NEOPIXELS_H

11
main/Version.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef VERSION_H
#define VERSION_H
#define VERSION_MAJOR 00
#define VERSION_MINOR 38
#define STRINGIFY(number) #number
#define VERSION_STRING(major, minor) STRINGIFY(major) "." STRINGIFY(minor)
#define VERSION_AS_STR() "Version " VERSION_STRING(VERSION_MAJOR, VERSION_MINOR) " " __DATE__ " " __TIME__
#endif // VERSION_H

24
main/idf_component.yml Normal file
View file

@ -0,0 +1,24 @@
## IDF Component Manager Manifest File
dependencies:
chmorgan/esp-libhelix-mp3: "^1.0.3"
chmorgan/esp-audio-player: "^1.0.7"
espressif/button: "^3.3.1"
espressif/mdns: "^1.3.2"
espressif/usb_host_msc: "^1.1.2"
## Required IDF version (>=5.1 is required for the SPI backend of the led-strip component.)
## We tested with 5.4.0.
idf:
version: ">=5.4.0"
# # Put list of dependencies here
# # For components maintained by Espressif:
# component: "~1.0.0"
# # For 3rd party components:
# username/component: ">=1.0.0,<2.0.0"
# username2/component2:
# version: "~1.0.0"
# # For transient dependencies `public` flag can be set.
# # `public` flag doesn't have an effect dependencies of the `main` component.
# # All dependencies of `main` are public by default.
# public: true

140
main/main.c Normal file
View file

@ -0,0 +1,140 @@
// KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKKKKky+.`/ykKKKKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKds/. -+o:` ./sdNKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKNds+-` `-+hNKKKKNho:` `-+shNKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKNkhyo+:. `-/sdNKKKKKKKKKKKKKky+:` .-/oyhdNKKKKKKKKKK
// KKys++:-.````.-:+oykNKKKKKKKKKKKKKKKKKKKKKKNkhs+/-.````.-:/+syKK
// KK -/+osydkNNNKKKkkkkkkkNKKKKKKKKKKKkkkkkkkkNKKKKNNkdhyso/: KK
// KK sKKKKKKKKKKKKK```````/KKKKKKKKKd-```````:kKKKKKKKKKKKKKd `KK
// KK- oKKKKKKKKKKKKK :KKKKKKKKo` `oNKKKKKKKKKKKKKKh :KK
// KK/ +KKKKKKKKKKKKK :KKKKKKd- -dKKKKKKKKKKKKKKKKy /KK
// KK+ /KKKKKKKKKKKKK :KKKKKs` +NKKKKKKKKKKKKKKKKKs +KK
// KKo :KKKKKKKKKKKKK :KKKk: .hKKKKKKKKKKKKKKKKKKKo oKK
// KKy -KKKKKKKKKKKKK :KKy` +NKKKKKKKKKKKKKKKKKKKK/ yKK
// KKd `KKKKKKKKKKKKK :k/ .hKKKKKKKKKKKKKKKKKKKKKK: dKK
// KKN NKKKKKKKKKKKK .. /kKKKKKKKKKKKKKKKKKKKKKKK. NKK
// KKK. dKKKKKKKKKKKK .yKKKKKKKKKKKKKKKKKKKKKKKKN .KKK
// KKK+ oKKKKKKKKKKKK -kKKKKKKKKKKKKKKKKKKKKKKKKKh +KKK
// KKKd .KKKKKKKKKKKK `sNKKKKKKKKKKKKKKKKKKKKKKKK/ dKKK
// KKKK: hKKKKKKKKKKK :kKKKKKKKKKKKKKKKKKKKKKKk :KKKK
// KKKKh -KKKKKKKKKKK `` .yKKKKKKKKKKKKKKKKKKKKK+ hKKKK
// KKKKK/ yKKKKKKKKKK T :d: /kKKKKKKKKKKKKKKKKKKk`:KKKKK
// KKKKKk`.NKKKKKKKKK :KNo` .hKKKKKKKKKKKKKKKKK:`kKKKKK
// KKKKKKy /KKKKKKKKK A :KKKd- +NKKKKKKKKKKKKKKo yKKKKKK
// KKKKKKK+ oKKKKKKKK :KKKKN+` -hKKKKKKKKKKKKy`+KKKKKKK
// KKKKKKKN/ sKKKKKKK G :KKKKKKh. `oNKKKKKKKKKh`/KKKKKKKK
// KKKKKKKKN/`sKKKKKK :KKKKKKKN/ -dKKKKKKKh`/NKKKKKKKK
// KKKKKKKKKK+ +NKKKK :KKKKKKKKKy. `sNKKKKs`+KKKKKKKKKK
// KKKKKKKKKKKs`:kKKK-------+KKKKKKKKKKk/--------oKKN+`sKKKKKKKKKKK
// KKKKKKKKKKKKh..yKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKd--dKKKKKKKKKKKK
// KKKKKKKKKKKKKN+`/kKKKKKKKKKKKKKKKKKKKKKKKKKKKKNo`+NKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKh-`sNKKKKKKKKKKKKKKKKKKKKKKKKNy.-hKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKs..sNKKKKKKKKKKKKKKKKKKKKNy-.yKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKNs..okKKKKKKKKKKKKKKKKNs-.sNKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKy-`/hKKKKKKKKKKKKd+`-yKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKd/`.odKKKKKKks-`/dKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKNs: .+yy+-`:sNKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKKKKNy/..+yNKKKKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
#include <string.h>
#include <SystemK.h>
#include <SPIFFS.h>
#include <USB.h>
#include <I2S_Audio.h>
#include <Switches.h>
#include <BLE.h>
#include <IR.h>
#include <WiFi.h>
#include "nvs_flash.h"
#include "HW_NeoPixels.h"
#include "Version.h"
#include "Reprogramming.h"
static const char *TAG = "KTag 2024A";
static SemaphoreHandle_t init_complete_semaphore;
static const uint16_t INITIALIZATION_TIMEOUT_IN_ms = 10 * 1000;
void app_main(void)
{
KLOG_INFO(TAG, VERSION_AS_STR());
KLOG_INFO(TAG, "Initializing app...");
init_complete_semaphore = xSemaphoreCreateBinary();
// Initialize NVS — it is used by both the BLE and WiFi drivers.
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
Initialize_SPIFFS(init_complete_semaphore);
if (xSemaphoreTake(init_complete_semaphore, pdMS_TO_TICKS(INITIALIZATION_TIMEOUT_IN_ms)) != pdTRUE)
{
KLOG_ERROR(TAG, "Timeout initializing SPIFFS!");
}
Initialize_USB(init_complete_semaphore);
if (xSemaphoreTake(init_complete_semaphore, pdMS_TO_TICKS(INITIALIZATION_TIMEOUT_IN_ms)) != pdTRUE)
{
KLOG_ERROR(TAG, "Timeout initializing USB!");
}
if (OTA_File_Exists() == true)
{
KLOG_INFO(TAG, "Attempting OTA reprogramming from %s.", Get_OTA_Image_URL());
KLOG_WARN(TAG, "This does not work reliably...use USB.");
Initialize_WiFi();
}
else
{
Initialize_Audio();
Initialize_SystemK_NeoPixels(init_complete_semaphore);
if (xSemaphoreTake(init_complete_semaphore, pdMS_TO_TICKS(INITIALIZATION_TIMEOUT_IN_ms)) != pdTRUE)
{
KLOG_ERROR(TAG, "Timeout initializing NeoPixels!");
}
Initialize_BLE();
Initialize_IR(init_complete_semaphore);
if (xSemaphoreTake(init_complete_semaphore, pdMS_TO_TICKS(INITIALIZATION_TIMEOUT_IN_ms)) != pdTRUE)
{
KLOG_ERROR(TAG, "Timeout initializing IR!");
}
if (Initialize_SystemK() != SYSTEMK_RESULT_SUCCESS)
{
KLOG_ERROR(TAG, "Error initializing SystemK!");
}
// Initialize the switches after SystemK, so xQueueEvents will have already been created.
Initialize_Switches();
vSemaphoreDelete(init_complete_semaphore);
KLOG_INFO(TAG, "Initialization complete.");
}
while (true)
{
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
SystemKResult_T HW_Execute_Console_Command(const uint8_t *const command)
{
SystemKResult_T result = SYSTEMK_RESULT_NOT_IMPLEMENTED;
if (strncmp((const char *)command, "reprogram", 9) == 0)
{
Request_Reprogramming_From_USB();
result = SYSTEMK_RESULT_SUCCESS;
}
return result;
}