#include #include #include #include #include "F1_api.h" #include "FreeRTOS.h" #include "task.h" #include "config.h" extern "C" { #include "DEV_Config.h" #include "EPD_4in2_V2.h" #include "GUI_Paint.h" #include "ImageData.h" } static UBYTE displayBuffer[(EPD_4IN2_V2_WIDTH / 8) * EPD_4IN2_V2_HEIGHT]; static constexpr UWORD COUNTER_WIDTH = 200; static constexpr UWORD COUNTER_HEIGHT = 24; static constexpr UWORD COUNTER_Y = 20; static UBYTE counterBuffer[(COUNTER_WIDTH / 8) * COUNTER_HEIGHT]; static int counter = 0; race_info next_race; void wifi_connect() { WiFi.setHostname(DEVICE_HOSTNAME); Serial.println("Connecting to WiFi..."); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); for (int attempt = 0; attempt < WIFI_MAXIMUM_RETRY; ++attempt) { const unsigned long start = millis(); while (WiFi.status() != WL_CONNECTED && millis() - start < 10000) { delay(250); } if (WiFi.status() == WL_CONNECTED) { Serial.print("Connected. IP address: "); Serial.println(WiFi.localIP()); return; } if (attempt + 1 < WIFI_MAXIMUM_RETRY) { Serial.println("WiFi connection failed; retrying..."); WiFi.disconnect(); delay(500); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); } } Serial.println("Failed to connect to WiFi."); } void sync_time() { if (WiFi.status() != WL_CONNECTED) { Serial.println("Skipping NTP synchronization because WiFi is disconnected."); return; } NTP.begin("pool.ntp.org", "time.nist.gov"); const bool synchronized = NTP.waitSet(10000); setenv("TZ", TIMEZONE, 1); tzset(); struct tm timeinfo; const time_t now = time(nullptr); if (synchronized && localtime_r(&now, &timeinfo) != nullptr) { Serial.print("Time synchronized: "); Serial.println(asctime(&timeinfo)); } else { Serial.println("Failed to synchronize time within 10 seconds."); } } bool readLocalTime(struct tm* result) { const time_t now = time(nullptr); return localtime_r(&now, result) != nullptr; } void display_loop(void *pvParameters) { while (true) { static unsigned long lastRefresh = 0; static char counterStr[32]; const unsigned long elapsed = millis() - lastRefresh; if (elapsed < 10000) { vTaskDelay(pdMS_TO_TICKS(10000 - elapsed)); continue; } lastRefresh = millis(); counter++; if (next_race.session_key == 0) { snprintf(counterStr, sizeof(counterStr), "Counter: %d", counter); Serial.print(""); Serial.println(counterStr); } else { snprintf(counterStr, sizeof(counterStr), "%s", next_race.circuit_short_name); } Paint_Clear(WHITE); Paint_DrawString_EN(10, 0, counterStr, &Font16, WHITE, BLACK); EPD_4IN2_V2_PartialDisplay( counterBuffer, 0, COUNTER_Y, COUNTER_WIDTH, COUNTER_Y + COUNTER_HEIGHT); vTaskDelay(pdMS_TO_TICKS(100)); // Add a small delay to prevent busy looping } } void setup() { Serial.begin(115200); if (DEV_Module_Init() != 0) { Serial.println("Failed to initialize display module."); return; } EPD_4IN2_V2_Init(); Serial.println("e-Paper initialization complete; clearing display..."); //EPD_4IN2_V2_Clear(); Paint_NewImage(displayBuffer, EPD_4IN2_V2_WIDTH, EPD_4IN2_V2_HEIGHT, ROTATE_0, WHITE); Paint_SelectImage(displayBuffer); Paint_Clear(WHITE); Paint_DrawString_EN(10, 0, "F1 Display Booting...", &Font16, WHITE, BLACK); Paint_DrawString_EN(10, COUNTER_Y, "Counter: 0", &Font16, WHITE, BLACK); EPD_4IN2_V2_Display(displayBuffer); DEV_Delay_ms(500); wifi_connect(); sync_time(); printf("Begin display loop.\n"); Paint_NewImage(counterBuffer, COUNTER_WIDTH, COUNTER_HEIGHT, ROTATE_0, WHITE); Paint_SelectImage(counterBuffer); xTaskCreate(display_loop, "display_loop", 1024, NULL, 5, NULL); xTaskCreate(get_next_race, "get_next_race", 2048, NULL, 5, NULL); }