diff --git a/esp32s3/balance/main/config.h b/esp32s3/balance/main/config.h index 18d3e85..ecd1c67 100644 --- a/esp32s3/balance/main/config.h +++ b/esp32s3/balance/main/config.h @@ -4,11 +4,7 @@ * * Orin comms: CH343 USB-to-serial on UART0 (GPIO43/44) → /dev/ttyACM0 on Orin * VESC CAN: SN65HVD230 transceiver on GPIO15 (TX) / GPIO16 (RX) - * Display: GC9A01 on SPI2 — BL=GPIO40, RST=GPIO12 - * - * GPIO2 is NOT used for CAN — it is free. Earlier versions had an erroneous - * conflict between DISP_BL (GPIO2) and VESC_CAN_TX (GPIO2); corrected here - * to match motor-test-firmware verified hardware layout (commit 8e66430). + * Display: GC9A01 on SPI2 — BL=GPIO2, RST=GPIO14 (Waveshare schematic-verified) */ /* ── Orin serial (CH343 USB-to-UART, 1a86:55d3 on Orin side) ── */ @@ -37,8 +33,8 @@ #define DISP_CS_GPIO 9 #define DISP_SCK_GPIO 10 #define DISP_MOSI_GPIO 11 -#define DISP_RST_GPIO 12 -#define DISP_BL_GPIO 40 +#define DISP_RST_GPIO 14 +#define DISP_BL_GPIO 2 /* ── Safety / timing ── */ #define HB_TIMEOUT_MS 500u /* heartbeat watchdog: disarm if exceeded */ diff --git a/esp32s3/balance/main/ota_display.c b/esp32s3/balance/main/ota_display.c index 5599f89..3bf8d3f 100644 --- a/esp32s3/balance/main/ota_display.c +++ b/esp32s3/balance/main/ota_display.c @@ -11,7 +11,9 @@ */ #include "ota_display.h" +#include "gc9a01.h" #include "gitea_ota.h" +#include "vesc_can.h" #include "version.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" @@ -26,6 +28,40 @@ static const char *TAG = "ota_disp"; #define CY 120 #define RAD 110 +/* "SAULT" at scale=4: 5 chars × 24px wide = 120px, 28px tall */ +#define SAULT_SCALE 4 +#define SAULT_X 60 /* (240 - 120) / 2 */ +#define SAULT_Y 86 /* centre vertically */ + +/* Battery voltage line at scale=2: "XX.XV" ≤ 5 chars × 12px = 60px */ +#define VBAT_SCALE 2 +#define VBAT_Y 130 + +static bool s_hud_dirty = true; /* set true whenever OTA overlay was active */ + +/* ── Idle HUD: SAULT title + battery voltage ── */ +static void draw_hud_idle(void) +{ + if (s_hud_dirty) { + /* Clear full screen on first entry after OTA overlay */ + display_fill_rect(0, 0, 240, 240, COL_BG); + display_draw_string_s(SAULT_X, SAULT_Y, "SAULT", COL_WHITE, COL_BG, SAULT_SCALE); + s_hud_dirty = false; + } + /* Update battery voltage every tick */ + uint16_t vbat_mv = (uint16_t)((int32_t)g_vesc[0].voltage_x10 * 100); + char vbuf[16]; + if (vbat_mv == 0) { + snprintf(vbuf, sizeof(vbuf), "--.-V"); + } else { + snprintf(vbuf, sizeof(vbuf), "%2u.%uV", vbat_mv / 1000u, (vbat_mv % 1000u) / 100u); + } + int vx = CX - (int)(strlen(vbuf) * 6 * VBAT_SCALE / 2); + display_fill_rect(vx - 2, VBAT_Y - 2, (int)(strlen(vbuf) * 6 * VBAT_SCALE) + 4, + 7 * VBAT_SCALE + 4, COL_BG); + display_draw_string_s(vx, VBAT_Y, vbuf, COL_GREEN, COL_BG, VBAT_SCALE); +} + /* ── Availability badge: 8×8 dot at top-right of display ── */ static void draw_badge(bool balance_avail, bool io_avail) { @@ -64,7 +100,7 @@ void ota_display_update(void) case OTA_SELF_DOWNLOADING: case OTA_SELF_VERIFYING: case OTA_SELF_APPLYING: { - /* Balance self-update in progress */ + s_hud_dirty = true; char pct_str[16]; snprintf(pct_str, sizeof(pct_str), "%d%%", g_ota_self_progress); const char *phase = (self == OTA_SELF_VERIFYING) ? "Verifying..." : @@ -76,9 +112,11 @@ void ota_display_update(void) return; } case OTA_SELF_REBOOTING: + s_hud_dirty = true; draw_status("Update complete", "Rebooting...", COL_GREEN, COL_BG); return; case OTA_SELF_FAILED: + s_hud_dirty = true; draw_progress_arc(0, COL_RED); draw_status("Balance update", "FAILED RETRY?", COL_RED, COL_BG); return; @@ -88,10 +126,12 @@ void ota_display_update(void) switch (io_s) { case UART_OTA_S_DOWNLOADING: + s_hud_dirty = true; draw_progress_arc(g_uart_ota_progress, COL_YELLOW); draw_status("Downloading IO", "firmware...", COL_WHITE, COL_BG); return; case UART_OTA_S_SENDING: { + s_hud_dirty = true; char pct_str[16]; snprintf(pct_str, sizeof(pct_str), "%d%%", g_uart_ota_progress); draw_progress_arc(g_uart_ota_progress, COL_YELLOW); @@ -99,9 +139,11 @@ void ota_display_update(void) return; } case UART_OTA_S_DONE: + s_hud_dirty = true; draw_status("IO update done", "", COL_GREEN, COL_BG); return; case UART_OTA_S_FAILED: + s_hud_dirty = true; draw_progress_arc(0, COL_RED); draw_status("IO update", "FAILED RETRY?", COL_RED, COL_BG); return; @@ -109,13 +151,13 @@ void ota_display_update(void) break; } - /* Idle — show badge if update available */ + /* Idle — draw SAULT HUD + badge */ bool bal_avail = g_balance_update.available; bool io_avail = g_io_update.available; + draw_hud_idle(); draw_badge(bal_avail, io_avail); if (bal_avail || io_avail) { - /* Show available versions on display when idle */ char verline[48]; if (bal_avail) { snprintf(verline, sizeof(verline), "Bal v%s rdy", @@ -127,10 +169,6 @@ void ota_display_update(void) g_io_update.version); draw_status(verline, "", COL_ORANGE, COL_BG); } - } else { - /* Clear OTA overlay area */ - display_fill_rect(20, 90, 200, 60, COL_BG); - draw_badge(false, false); } }