diff --git a/Turbo-Gauge.ino b/Turbo-Gauge.ino index 897a215..6d2d555 100644 --- a/Turbo-Gauge.ino +++ b/Turbo-Gauge.ino @@ -25,12 +25,14 @@ #define R_NEEDLE_TIP 160 #define R_HUB 22 -// -- Engine Load Position (Top Middle) -- +// -- Engine Load Position -- #define LOAD_W 100 #define LOAD_X (CX - (LOAD_W / 2)) #define LOAD_Y (CY - 70) -// ── State & OBD Variables ───────────────────────────────────────── +// ── Shared State & Concurrency ──────────────────────────────────── +SemaphoreHandle_t xDataMutex; + static float boost_target = 0.0f; static float boost_current = -1.0f; static float peak_boost = -1.0f; @@ -40,22 +42,19 @@ static float clt_temp = 0.0f; static float iat_temp = 0.0f; static float engine_load = 0.0f; -// Buffers for strings (moved out of draw calls for speed) static char pbuf[16] = "0.00 bar"; static char clt_buf[24] = "COOLANT: 0 °C"; static char iat_buf[24] = "INTAKE: 0 °C"; -// Animation State static bool startup_anim = true; static int anim_stage = 0; static float anim_speed = 0.35f; - -// BLE Status static int ble_status = 1; + static lv_obj_t *face_obj; static lv_obj_t *needle_obj; -// OBD Hardware Objects +// OBD Hardware NimBLEClient* pClient = nullptr; ELM327 myELM327; @@ -69,7 +68,7 @@ static float bar_to_rad(float bar) { static lv_coord_t px(float r, float rad) { return (lv_coord_t)(CX + cosf(rad) * r); } static lv_coord_t py(float r, float rad) { return (lv_coord_t)(CY + sinf(rad) * r); } -// ── Drawing Functions ───────────────────────────────────────────── +// ── Drawing Functions (Run on Core 1) ───────────────────────────── static void draw_gauge_face(lv_event_t *e) { lv_draw_ctx_t *draw_ctx = lv_event_get_draw_ctx(e); lv_point_t center = {CX, CY}; @@ -134,7 +133,7 @@ static void draw_gauge_face(lv_event_t *e) { lv_area_t load_txt_area = {LOAD_X, LOAD_Y + 8, LOAD_X + LOAD_W, LOAD_Y + 20}; lv_draw_label(draw_ctx, &load_lbl_dsc, &load_txt_area, "ENGINE LOAD", NULL); - // 5. FUTURISTIC DATA HUB + // 5. DATA HUB lv_draw_rect_dsc_t box_dsc; lv_draw_rect_dsc_init(&box_dsc); box_dsc.bg_color = lv_color_make(0x1A, 0x1A, 0x1A); @@ -197,6 +196,7 @@ static void draw_needle(lv_event_t *e) { lv_draw_rect(draw_ctx, &hub_dsc, &hub_area); } +// ── OBD Communication (Core 0 Task) ─────────────────────────────── void connect_to_vlink() { NimBLEScan* pScan = NimBLEDevice::getScan(); pScan->setActiveScan(true); @@ -216,19 +216,54 @@ void connect_to_vlink() { } } +void DriverTask(void *parameter) { + while (1) { + if (ble_status != 2) { + connect_to_vlink(); + vTaskDelay(pdMS_TO_TICKS(1000)); + } else { + // Poll hardware + float t_clt = myELM327.engineCoolantTemp(); + float t_iat = myELM327.intakeAirTemp(); + float t_load = myELM327.engineLoad(); + float map_kpa = myELM327.manifoldPressure(); + + // Safely write to shared variables + if (xSemaphoreTake(xDataMutex, pdMS_TO_TICKS(20))) { + if (myELM327.nb_rx_state == ELM_SUCCESS) { + clt_temp = t_clt; + iat_temp = t_iat; + engine_load = t_load; + boost_target = (map_kpa / 100.0f) - 1.01f; + + if (boost_target > peak_boost) { + peak_boost = boost_target; + peak_time = millis(); + } + + sprintf(pbuf, "%.2f bar", peak_boost < 0 ? 0.00 : peak_boost); + sprintf(clt_buf, "COOLANT: %.0f °C", clt_temp); + sprintf(iat_buf, "INTAKE: %.0f °C", iat_temp); + } + xSemaphoreGive(xDataMutex); + } + vTaskDelay(pdMS_TO_TICKS(20)); // High polling frequency + } + } +} + +// ── Initialization ──────────────────────────────────────────────── void gauge_create(void) { lv_obj_t *scr = lv_scr_act(); lv_obj_clean(scr); lv_obj_set_style_bg_color(scr, lv_color_black(), 0); - // Background Layer (Labels, Ticks) face_obj = lv_obj_create(scr); lv_obj_set_size(face_obj, SCREEN_W, SCREEN_H); lv_obj_set_style_bg_opa(face_obj, 0, 0); lv_obj_set_style_border_width(face_obj, 0, 0); lv_obj_add_event_cb(face_obj, draw_gauge_face, LV_EVENT_DRAW_MAIN, NULL); - // Foreground Layer (Needle) needle_obj = lv_obj_create(scr); lv_obj_set_size(needle_obj, SCREEN_W, SCREEN_H); lv_obj_set_style_bg_opa(needle_obj, 0, 0); @@ -236,93 +271,53 @@ void gauge_create(void) { lv_obj_add_event_cb(needle_obj, draw_needle, LV_EVENT_DRAW_MAIN, NULL); } -void DriverTask(void *parameter) { - while (1) { - if (!startup_anim) { - if (ble_status != 2) { - connect_to_vlink(); - vTaskDelay(pdMS_TO_TICKS(1000)); - } else { - float t_clt = myELM327.engineCoolantTemp(); - if (myELM327.nb_rx_state == ELM_SUCCESS) clt_temp = t_clt; - float t_iat = myELM327.intakeAirTemp(); - if (myELM327.nb_rx_state == ELM_SUCCESS) iat_temp = t_iat; - float t_load = myELM327.engineLoad(); - if (myELM327.nb_rx_state == ELM_SUCCESS) engine_load = t_load; - - float map_kpa = myELM327.manifoldPressure(); - if (myELM327.nb_rx_state == ELM_SUCCESS) { - boost_target = (map_kpa / 100.0f) - 1.01f; - } - if (boost_target > peak_boost) { - peak_boost = boost_target; - peak_time = millis(); - } - - // Update text buffers outside of drawing thread - sprintf(pbuf, "%.2f bar", peak_boost < 0 ? 0.00 : peak_boost); - sprintf(clt_buf, "COOLANT: %.0f °C", clt_temp); - sprintf(iat_buf, "INTAKE: %.0f °C", iat_temp); - - vTaskDelay(pdMS_TO_TICKS(50)); - } - } else { - vTaskDelay(pdMS_TO_TICKS(100)); - } - } -} - void setup() { Serial.begin(115200); Serial1.begin(38400); + + xDataMutex = xSemaphoreCreateMutex(); + TCA9554PWR_Init(0x00); Backlight_Init(); LCD_Init(); Lvgl_Init(); NimBLEDevice::init("ESP32_Gauge"); gauge_create(); - xTaskCreatePinnedToCore(DriverTask, "DriverTask", 8192, NULL, 2, NULL, 1); + + // Start Worker on Core 0 + xTaskCreatePinnedToCore(DriverTask, "DriverTask", 8192, NULL, 1, NULL, 0); } void loop() { static uint32_t last_face_update = 0; + // 1. Logic & Smoothing (Core 1) if (startup_anim) { if (anim_stage == 0) { boost_current += anim_speed; - if (boost_current >= GAUGE_MAX) { - boost_current = GAUGE_MAX; - anim_stage = 1; - } + if (boost_current >= GAUGE_MAX) anim_stage = 1; } else if (anim_stage == 1) { - float diff = boost_current - 0.0f; - float step = diff * 0.07f; - if (step < 0.01f) step = 0.01f; - boost_current -= step; + boost_current -= (boost_current * 0.07f); if (boost_current <= 0.01f) { boost_current = 0.0f; - boost_target = 0.0f; startup_anim = false; - anim_stage = 2; } } - vTaskDelay(pdMS_TO_TICKS(10)); } else { - boost_current += (boost_target - boost_current) * 0.15f; - if (millis() - peak_time > 10000) { - peak_boost = boost_current; - peak_time = millis(); + // Apply smoothing to target provided by Core 0 + if (xSemaphoreTake(xDataMutex, 0)) { + boost_current += (boost_target - boost_current) * 0.15f; + if (millis() - peak_time > 10000) peak_boost = boost_current; + xSemaphoreGive(xDataMutex); } } - // CRITICAL OPTIMIZATION: - // Always update the needle (fast) + // 2. UI Refresh lv_obj_invalidate(needle_obj); - // Update the face (labels/load) only every 100ms (10Hz) to save FPS if (millis() - last_face_update > 100) { lv_obj_invalidate(face_obj); last_face_update = millis(); } Lvgl_Loop(); - vTaskDelay(pdMS_TO_TICKS(16)); // Target ~60 FPS + delay(16); // Target 60fps } \ No newline at end of file