#include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_err.h" #include "esp_log.h" #include "esp_system.h" #include "panel_driver.h" static const char *TAG = "infinity_node"; typedef struct { const char *node_id; panel_output_config_t outputs[INFINITY_NODE_OUTPUT_COUNT]; } node_runtime_config_t; typedef struct { uint32_t heartbeat_count; uint32_t reconnect_count; } node_metrics_t; static node_runtime_config_t make_default_runtime_config(void); static esp_err_t validate_runtime_config(const node_runtime_config_t *config); static void network_rx_task(void *arg); static void render_task(void *arg); static void output_task(void *arg); static void telemetry_task(void *arg); void app_main(void) { static node_metrics_t metrics = {0}; node_runtime_config_t runtime_config = make_default_runtime_config(); esp_err_t validation = validate_runtime_config(&runtime_config); if (validation != ESP_OK) { ESP_LOGE(TAG, "startup halted until hardware mapping is validated: %s", esp_err_to_name(validation)); return; } xTaskCreate(network_rx_task, "network_rx", 4096, &metrics, 8, NULL); xTaskCreate(render_task, "render", 4096, &metrics, 7, NULL); xTaskCreate(output_task, "output", 4096, &metrics, 9, NULL); xTaskCreate(telemetry_task, "telemetry", 4096, &metrics, 5, NULL); } static node_runtime_config_t make_default_runtime_config(void) { node_runtime_config_t config = { .node_id = "unassigned-node", .outputs = { { .panel_slot = PANEL_SLOT_TOP, .physical_output_name = "UART 6", .driver_reference = "UART 6", .driver_kind = PANEL_DRIVER_KIND_UNVALIDATED, .led_count = INFINITY_LEDS_PER_OUTPUT, .reverse = false, .enabled = true, }, { .panel_slot = PANEL_SLOT_MIDDLE, .physical_output_name = "UART 5", .driver_reference = "UART 5", .driver_kind = PANEL_DRIVER_KIND_UNVALIDATED, .led_count = INFINITY_LEDS_PER_OUTPUT, .reverse = false, .enabled = true, }, { .panel_slot = PANEL_SLOT_BOTTOM, .physical_output_name = "UART 4", .driver_reference = "UART 4", .driver_kind = PANEL_DRIVER_KIND_UNVALIDATED, .led_count = INFINITY_LEDS_PER_OUTPUT, .reverse = false, .enabled = true, }, }, }; return config; } static esp_err_t validate_runtime_config(const node_runtime_config_t *config) { if (config == NULL) { return ESP_ERR_INVALID_ARG; } for (size_t index = 0; index < INFINITY_NODE_OUTPUT_COUNT; ++index) { esp_err_t status = panel_driver_validate_output(&config->outputs[index]); if (status != ESP_OK) { ESP_LOGE( TAG, "output %u (%s) failed validation", (unsigned int)index, config->outputs[index].physical_output_name ); return status; } } return ESP_OK; } static void network_rx_task(void *arg) { node_metrics_t *metrics = (node_metrics_t *)arg; for (;;) { metrics->heartbeat_count++; vTaskDelay(pdMS_TO_TICKS(50)); } } static void render_task(void *arg) { (void)arg; for (;;) { vTaskDelay(pdMS_TO_TICKS(16)); } } static void output_task(void *arg) { (void)arg; for (;;) { vTaskDelay(pdMS_TO_TICKS(16)); } } static void telemetry_task(void *arg) { node_metrics_t *metrics = (node_metrics_t *)arg; for (;;) { ESP_LOGI( TAG, "telemetry heartbeat_count=%u reconnect_count=%u free_heap=%u", metrics->heartbeat_count, metrics->reconnect_count, (unsigned int)esp_get_free_heap_size() ); vTaskDelay(pdMS_TO_TICKS(1000)); } }