Move render to separate core
This commit is contained in:
+62
-67
@@ -25,12 +25,14 @@
|
|||||||
#define R_NEEDLE_TIP 160
|
#define R_NEEDLE_TIP 160
|
||||||
#define R_HUB 22
|
#define R_HUB 22
|
||||||
|
|
||||||
// -- Engine Load Position (Top Middle) --
|
// -- Engine Load Position --
|
||||||
#define LOAD_W 100
|
#define LOAD_W 100
|
||||||
#define LOAD_X (CX - (LOAD_W / 2))
|
#define LOAD_X (CX - (LOAD_W / 2))
|
||||||
#define LOAD_Y (CY - 70)
|
#define LOAD_Y (CY - 70)
|
||||||
|
|
||||||
// ── State & OBD Variables ─────────────────────────────────────────
|
// ── Shared State & Concurrency ────────────────────────────────────
|
||||||
|
SemaphoreHandle_t xDataMutex;
|
||||||
|
|
||||||
static float boost_target = 0.0f;
|
static float boost_target = 0.0f;
|
||||||
static float boost_current = -1.0f;
|
static float boost_current = -1.0f;
|
||||||
static float peak_boost = -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 iat_temp = 0.0f;
|
||||||
static float engine_load = 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 pbuf[16] = "0.00 bar";
|
||||||
static char clt_buf[24] = "COOLANT: 0 °C";
|
static char clt_buf[24] = "COOLANT: 0 °C";
|
||||||
static char iat_buf[24] = "INTAKE: 0 °C";
|
static char iat_buf[24] = "INTAKE: 0 °C";
|
||||||
|
|
||||||
// Animation State
|
|
||||||
static bool startup_anim = true;
|
static bool startup_anim = true;
|
||||||
static int anim_stage = 0;
|
static int anim_stage = 0;
|
||||||
static float anim_speed = 0.35f;
|
static float anim_speed = 0.35f;
|
||||||
|
|
||||||
// BLE Status
|
|
||||||
static int ble_status = 1;
|
static int ble_status = 1;
|
||||||
|
|
||||||
static lv_obj_t *face_obj;
|
static lv_obj_t *face_obj;
|
||||||
static lv_obj_t *needle_obj;
|
static lv_obj_t *needle_obj;
|
||||||
|
|
||||||
// OBD Hardware Objects
|
// OBD Hardware
|
||||||
NimBLEClient* pClient = nullptr;
|
NimBLEClient* pClient = nullptr;
|
||||||
ELM327 myELM327;
|
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 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); }
|
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) {
|
static void draw_gauge_face(lv_event_t *e) {
|
||||||
lv_draw_ctx_t *draw_ctx = lv_event_get_draw_ctx(e);
|
lv_draw_ctx_t *draw_ctx = lv_event_get_draw_ctx(e);
|
||||||
lv_point_t center = {CX, CY};
|
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_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);
|
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_t box_dsc;
|
||||||
lv_draw_rect_dsc_init(&box_dsc);
|
lv_draw_rect_dsc_init(&box_dsc);
|
||||||
box_dsc.bg_color = lv_color_make(0x1A, 0x1A, 0x1A);
|
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);
|
lv_draw_rect(draw_ctx, &hub_dsc, &hub_area);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── OBD Communication (Core 0 Task) ───────────────────────────────
|
||||||
void connect_to_vlink() {
|
void connect_to_vlink() {
|
||||||
NimBLEScan* pScan = NimBLEDevice::getScan();
|
NimBLEScan* pScan = NimBLEDevice::getScan();
|
||||||
pScan->setActiveScan(true);
|
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) {
|
void gauge_create(void) {
|
||||||
lv_obj_t *scr = lv_scr_act();
|
lv_obj_t *scr = lv_scr_act();
|
||||||
lv_obj_clean(scr);
|
lv_obj_clean(scr);
|
||||||
lv_obj_set_style_bg_color(scr, lv_color_black(), 0);
|
lv_obj_set_style_bg_color(scr, lv_color_black(), 0);
|
||||||
|
|
||||||
// Background Layer (Labels, Ticks)
|
|
||||||
face_obj = lv_obj_create(scr);
|
face_obj = lv_obj_create(scr);
|
||||||
lv_obj_set_size(face_obj, SCREEN_W, SCREEN_H);
|
lv_obj_set_size(face_obj, SCREEN_W, SCREEN_H);
|
||||||
lv_obj_set_style_bg_opa(face_obj, 0, 0);
|
lv_obj_set_style_bg_opa(face_obj, 0, 0);
|
||||||
lv_obj_set_style_border_width(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);
|
lv_obj_add_event_cb(face_obj, draw_gauge_face, LV_EVENT_DRAW_MAIN, NULL);
|
||||||
|
|
||||||
// Foreground Layer (Needle)
|
|
||||||
needle_obj = lv_obj_create(scr);
|
needle_obj = lv_obj_create(scr);
|
||||||
lv_obj_set_size(needle_obj, SCREEN_W, SCREEN_H);
|
lv_obj_set_size(needle_obj, SCREEN_W, SCREEN_H);
|
||||||
lv_obj_set_style_bg_opa(needle_obj, 0, 0);
|
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);
|
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() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
Serial1.begin(38400);
|
Serial1.begin(38400);
|
||||||
|
|
||||||
|
xDataMutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
TCA9554PWR_Init(0x00);
|
TCA9554PWR_Init(0x00);
|
||||||
Backlight_Init(); LCD_Init(); Lvgl_Init();
|
Backlight_Init(); LCD_Init(); Lvgl_Init();
|
||||||
NimBLEDevice::init("ESP32_Gauge");
|
NimBLEDevice::init("ESP32_Gauge");
|
||||||
gauge_create();
|
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() {
|
void loop() {
|
||||||
static uint32_t last_face_update = 0;
|
static uint32_t last_face_update = 0;
|
||||||
|
|
||||||
|
// 1. Logic & Smoothing (Core 1)
|
||||||
if (startup_anim) {
|
if (startup_anim) {
|
||||||
if (anim_stage == 0) {
|
if (anim_stage == 0) {
|
||||||
boost_current += anim_speed;
|
boost_current += anim_speed;
|
||||||
if (boost_current >= GAUGE_MAX) {
|
if (boost_current >= GAUGE_MAX) anim_stage = 1;
|
||||||
boost_current = GAUGE_MAX;
|
|
||||||
anim_stage = 1;
|
|
||||||
}
|
|
||||||
} else if (anim_stage == 1) {
|
} else if (anim_stage == 1) {
|
||||||
float diff = boost_current - 0.0f;
|
boost_current -= (boost_current * 0.07f);
|
||||||
float step = diff * 0.07f;
|
|
||||||
if (step < 0.01f) step = 0.01f;
|
|
||||||
boost_current -= step;
|
|
||||||
if (boost_current <= 0.01f) {
|
if (boost_current <= 0.01f) {
|
||||||
boost_current = 0.0f;
|
boost_current = 0.0f;
|
||||||
boost_target = 0.0f;
|
|
||||||
startup_anim = false;
|
startup_anim = false;
|
||||||
anim_stage = 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
} else {
|
} else {
|
||||||
boost_current += (boost_target - boost_current) * 0.15f;
|
// Apply smoothing to target provided by Core 0
|
||||||
if (millis() - peak_time > 10000) {
|
if (xSemaphoreTake(xDataMutex, 0)) {
|
||||||
peak_boost = boost_current;
|
boost_current += (boost_target - boost_current) * 0.15f;
|
||||||
peak_time = millis();
|
if (millis() - peak_time > 10000) peak_boost = boost_current;
|
||||||
|
xSemaphoreGive(xDataMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRITICAL OPTIMIZATION:
|
// 2. UI Refresh
|
||||||
// Always update the needle (fast)
|
|
||||||
lv_obj_invalidate(needle_obj);
|
lv_obj_invalidate(needle_obj);
|
||||||
|
|
||||||
// Update the face (labels/load) only every 100ms (10Hz) to save FPS
|
|
||||||
if (millis() - last_face_update > 100) {
|
if (millis() - last_face_update > 100) {
|
||||||
lv_obj_invalidate(face_obj);
|
lv_obj_invalidate(face_obj);
|
||||||
last_face_update = millis();
|
last_face_update = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
Lvgl_Loop();
|
Lvgl_Loop();
|
||||||
vTaskDelay(pdMS_TO_TICKS(16)); // Target ~60 FPS
|
delay(16); // Target 60fps
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user