From 0a58ed8a795f431775bfc48d8c8c0d7b0c7ffd12 Mon Sep 17 00:00:00 2001 From: "DESKTOP-PUHFO60\\dsx2016" Date: Fri, 23 Oct 2026 16:24:19 +0800 Subject: [PATCH] Add optional cyd_35r (ESP32-3248S035) with 480x320 UI; default remains 2.8. --- firmware/export_from_build.ps1 | 57 +++++++++++++++++++++++++--------- include/pins.h | 38 +++++++++++++++-------- platformio.ini | 17 ++++++++++ src/ui.cpp | 36 ++++++++++----------- 4 files changed, 103 insertions(+), 45 deletions(-) diff --git a/firmware/export_from_build.ps1 b/firmware/export_from_build.ps1 index 08727fe..1d128a6 100644 --- a/firmware/export_from_build.ps1 +++ b/firmware/export_from_build.ps1 @@ -1,9 +1,13 @@ -# Rebuild (optional) and refresh firmware/cyd from PlatformIO build output. -# Usage: -# .\firmware\export_from_build.ps1 -# .\firmware\export_from_build.ps1 -SkipBuild +# Export firmware bins from PlatformIO build. +# Default: 2.8" (cyd -> firmware/cyd) +# 3.5": .\firmware\export_from_build.ps1 -Env cyd_35r +# (only after: pio run -e cyd_35r) -param([switch]$SkipBuild) +param( + [switch]$SkipBuild, + [ValidateSet("cyd", "cyd_35r")] + [string]$Env = "cyd" +) $ErrorActionPreference = "Stop" $root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path) @@ -12,17 +16,26 @@ Set-Location $root $env:Path = "C:\Users\dsx2016\AppData\Local\Programs\Python\Python312\Scripts;" + $env:Path if (-not $SkipBuild) { - pio run -e cyd + pio run -e $Env if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } -$src = Join-Path $root ".pio\build\cyd" -$dst = Join-Path $root "firmware\cyd" +$src = Join-Path $root (".pio\build\" + $Env) +if ($Env -eq "cyd_35r") { + $dstName = "cyd_35r" + $boardLine = "board: ESP32-3248S035 (ST7796)" + $boardIdHint = "esp32-3248s035" +} else { + $dstName = "cyd" + $boardLine = "board: ESP32-2432S028R (ILI9341)" + $boardIdHint = "esp32-2432s028" +} +$dst = Join-Path $root ("firmware\" + $dstName) New-Item -ItemType Directory -Force -Path $dst | Out-Null foreach ($f in @("bootloader.bin", "partitions.bin", "firmware.bin")) { $p = Join-Path $src $f - if (-not (Test-Path $p)) { Write-Error "Missing build artifact: $p" } + if (-not (Test-Path $p)) { Write-Error "Missing build artifact: $p — build with: pio run -e $Env" } Copy-Item $p $dst -Force } @@ -32,17 +45,15 @@ if (-not (Test-Path $bootApp)) { } Copy-Item $bootApp (Join-Path $dst "boot_app0.bin") -Force -# Keep flash scripts if already present $git = "" try { $git = (git -C $root rev-parse --short HEAD).Trim() } catch { $git = "unknown" } $date = Get-Date -Format "yyyy-MM-dd" $hashes = Get-ChildItem (Join-Path $dst "*.bin") | Get-FileHash -Algorithm SHA256 $lines = @( - "name: CYD Pomodoro (cyd)", + "name: CYD tftest ($dstName)", "git: $git", "date: $date", - "board: ESP32-2432S028R (ILI9341)", - "features: Pomodoro timer, SoftSPI touch, GPIO26 speaker, TF status", + $boardLine, "flash_size: 4MB", "offsets:", " 0x1000 bootloader.bin", @@ -55,4 +66,22 @@ foreach ($h in $hashes) { $lines += (" {0,-15} {1}" -f (Split-Path $h.Path -Leaf), $h.Hash) } $lines | Set-Content -Encoding utf8 (Join-Path $dst "VERSION.txt") -Write-Host "Exported -> $dst (git $git)" + +# Keep / refresh meta.json boardId for catalog filtering +$metaPath = Join-Path $dst "meta.json" +if (Test-Path $metaPath) { + try { + $meta = Get-Content $metaPath -Raw | ConvertFrom-Json + $meta.boardId = $boardIdHint + if ($Env -eq "cyd_35r" -and $meta.name -notmatch "3\.5") { + $meta.name = ($meta.name -replace "\s*$","") + " (3.5\")" + } + $meta | ConvertTo-Json -Depth 8 | Set-Content -Encoding utf8 $metaPath + } catch { + Write-Host "meta.json update skipped: $_" + } +} elseif ($Env -eq "cyd_35r") { + Write-Host "Note: add firmware/cyd_35r/meta.json with boardId=$boardIdHint for the web catalog" +} + +Write-Host "Exported -> $dst (env=$Env git=$git)" diff --git a/include/pins.h b/include/pins.h index 44adc4c..27e2294 100644 --- a/include/pins.h +++ b/include/pins.h @@ -1,16 +1,20 @@ #pragma once -// CYD ESP32-2432S028R +// Board pins — 2.8" default; 3.5" when CYD_BOARD_3248S035 is set (env:cyd_35r) +#if defined(CYD_BOARD_3248S035) +#define TFT_BL_PIN 27 +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 480 +#define UI_HOR_RES 480 +#define UI_VER_RES 320 +#else #define TFT_BL_PIN 21 - -// TF / microSD (VSPI;触摸用 SoftSPI,不抢总线) -#define SD_MISO 19 -#define SD_MOSI 23 -#define SD_SCK 18 -#define SD_CS 5 - -#define SPEAKER_PIN 26 +#define SCREEN_WIDTH 240 +#define SCREEN_HEIGHT 320 +#define UI_HOR_RES 320 +#define UI_VER_RES 240 +#endif #define CYD_TOUCH_IRQ 36 #define CYD_TOUCH_MOSI 32 @@ -18,7 +22,15 @@ #define CYD_TOUCH_CLK 25 #define CYD_TOUCH_CS 33 -#define SCREEN_WIDTH 240 -#define SCREEN_HEIGHT 320 -#define UI_HOR_RES 320 -#define UI_VER_RES 240 +/* Map 320x240 design coordinates onto the active UI resolution (identity on 2.8"). */ +#define UX(x) ((int)(((long)(x) * (UI_HOR_RES)) / 320)) +#define UY(y) ((int)(((long)(y) * (UI_VER_RES)) / 240)) +#define UW(w) UX(w) +#define UH(h) UY(h) + + +#define SD_MISO 19 +#define SD_MOSI 23 +#define SD_SCK 18 +#define SD_CS 5 +#define SPEAKER_PIN 26 diff --git a/platformio.ini b/platformio.ini index 8935b40..9f99a35 100644 --- a/platformio.ini +++ b/platformio.ini @@ -53,3 +53,20 @@ build_flags = -I${PROJECT_DIR}/include extra_scripts = pre:copy_lv_conf.py + +; Optional 3.5" CYD ESP32-3248S035 (ST7796 resistive). Default remains [env:cyd]. +; Build: pio run -e cyd_35r +[env:cyd_35r] +extends = env:cyd +build_flags = + ${env:cyd.build_flags} + -UILI9341_2_DRIVER + -DST7796_DRIVER=1 + -UTFT_WIDTH + -UTFT_HEIGHT + -DTFT_WIDTH=320 + -DTFT_HEIGHT=480 + -UTFT_BL + -DTFT_BL=27 + -DCYD_BOARD_3248S035=1 + diff --git a/src/ui.cpp b/src/ui.cpp index 085320a..eab6e4c 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -45,7 +45,7 @@ static lv_obj_t *panel_custom; static lv_obj_t *lbl_custom_val; static char g_timer_text[8] = "05:00"; -static char g_status_text[40] = "5m · READY"; +static char g_status_text[40] = "5m - READY"; static char g_sound_text[28] = "Alarm"; static char g_custom_buf[4] = ""; static char g_custom_disp[16] = "min: _"; @@ -98,7 +98,7 @@ static void refresh_timer_labels() { lv_obj_set_style_text_color(lbl_timer, lv_color_hex(color), 0); } - snprintf(g_status_text, sizeof(g_status_text), "%um · %s", + snprintf(g_status_text, sizeof(g_status_text), "%um - %s", (unsigned)pomo_duration_minutes(), pomo_state_name()); if (lbl_status) { lv_label_set_text(lbl_status, g_status_text); @@ -318,7 +318,7 @@ static lv_obj_t *make_small_btn(lv_obj_t *parent, const char *txt, int x, int y, static void build_custom_panel(lv_obj_t *scr) { panel_custom = lv_obj_create(scr); lv_obj_set_size(panel_custom, UI_HOR_RES, UI_VER_RES); - lv_obj_set_pos(panel_custom, 0, 0); + lv_obj_set_pos(panel_custom, UX(0), UY(0)); lv_obj_set_style_bg_color(panel_custom, lv_color_hex(COL_BG), 0); lv_obj_set_style_bg_opa(panel_custom, LV_OPA_COVER, 0); lv_obj_set_style_border_width(panel_custom, 0, 0); @@ -331,13 +331,13 @@ static void build_custom_panel(lv_obj_t *scr) { lv_label_set_text(title, "Custom minutes"); lv_obj_set_style_text_font(title, &lv_font_montserrat_16, 0); lv_obj_set_style_text_color(title, lv_color_hex(COL_ACCENT), 0); - lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 8); + lv_obj_align(title, LV_ALIGN_TOP_MID, UX(0), UY(8)); lbl_custom_val = lv_label_create(panel_custom); lv_label_set_text(lbl_custom_val, "min: _"); lv_obj_set_style_text_font(lbl_custom_val, &lv_font_montserrat_28, 0); lv_obj_set_style_text_color(lbl_custom_val, lv_color_hex(COL_INK), 0); - lv_obj_align(lbl_custom_val, LV_ALIGN_TOP_MID, 0, 36); + lv_obj_align(lbl_custom_val, LV_ALIGN_TOP_MID, UX(0), UY(36)); // Numpad 3x4 static const char *keys[12] = {"1", "2", "3", "4", "5", "6", @@ -405,13 +405,13 @@ void ui_create() { lv_label_set_text(lbl_title, "POMO"); lv_obj_set_style_text_font(lbl_title, &lv_font_montserrat_20, 0); lv_obj_set_style_text_color(lbl_title, lv_color_hex(COL_TOMATO), 0); - lv_obj_set_pos(lbl_title, 12, 4); + lv_obj_set_pos(lbl_title, UX(12), UY(4)); lbl_chip = lv_label_create(scr); lv_label_set_text(lbl_chip, "-"); lv_obj_set_style_text_font(lbl_chip, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(lbl_chip, lv_color_hex(COL_MUTED), 0); - lv_obj_align(lbl_chip, LV_ALIGN_TOP_RIGHT, -12, 8); + lv_obj_align(lbl_chip, LV_ALIGN_TOP_RIGHT, UX(-12), UY(8)); make_small_btn(scr, "-", 12, 32, 40, 44, on_minus_clicked, nullptr); @@ -419,16 +419,16 @@ void ui_create() { lv_label_set_text(lbl_timer, "05:00"); lv_obj_set_style_text_font(lbl_timer, &lv_font_montserrat_28, 0); lv_obj_set_style_text_color(lbl_timer, lv_color_hex(COL_ACCENT), 0); - lv_obj_align(lbl_timer, LV_ALIGN_TOP_MID, 0, 34); + lv_obj_align(lbl_timer, LV_ALIGN_TOP_MID, UX(0), UY(34)); make_small_btn(scr, "+", UI_HOR_RES - 52, 32, 40, 44, on_plus_clicked, nullptr); lbl_status = lv_label_create(scr); - lv_label_set_text(lbl_status, "5m · READY"); + lv_label_set_text(lbl_status, "5m - READY"); lv_obj_set_style_text_font(lbl_status, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_MUTED), 0); - lv_obj_align(lbl_status, LV_ALIGN_TOP_MID, 0, 74); + lv_obj_align(lbl_status, LV_ALIGN_TOP_MID, UX(0), UY(74)); // Presets: 1 3 5 10 + Set const int gap = 6; @@ -447,12 +447,12 @@ void ui_create() { lv_label_set_text(snd_cap, "SOUND"); lv_obj_set_style_text_font(snd_cap, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(snd_cap, lv_color_hex(COL_MUTED), 0); - lv_obj_set_pos(snd_cap, 12, 134); + lv_obj_set_pos(snd_cap, UX(12), UY(134)); make_small_btn(scr, "<", 68, 128, 32, 28, on_sound_prev, nullptr); lv_obj_t *btn_snd = lv_button_create(scr); - lv_obj_set_pos(btn_snd, 104, 128); + lv_obj_set_pos(btn_snd, UX(104), UY(128)); lv_obj_set_size(btn_snd, UI_HOR_RES - 104 - 48, 28); lv_obj_add_flag(btn_snd, LV_OBJ_FLAG_CLICKABLE); lv_obj_add_event_cb(btn_snd, on_sound_preview, LV_EVENT_CLICKED, nullptr); @@ -472,11 +472,11 @@ void ui_create() { lv_label_set_text(vol_cap, "VOL"); lv_obj_set_style_text_font(vol_cap, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(vol_cap, lv_color_hex(COL_MUTED), 0); - lv_obj_set_pos(vol_cap, 12, 164); + lv_obj_set_pos(vol_cap, UX(12), UY(164)); slider_vol = lv_slider_create(scr); lv_obj_set_size(slider_vol, UI_HOR_RES - 12 - 56 - 44, 14); - lv_obj_set_pos(slider_vol, 52, 164); + lv_obj_set_pos(slider_vol, UX(52), UY(164)); lv_slider_set_range(slider_vol, 0, 100); lv_slider_set_value(slider_vol, audio_get_volume(), LV_ANIM_OFF); lv_obj_set_style_bg_color(slider_vol, lv_color_hex(COL_LINE), LV_PART_MAIN); @@ -493,13 +493,13 @@ void ui_create() { lv_label_set_text(lbl_vol, "40%"); lv_obj_set_style_text_font(lbl_vol, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(lbl_vol, lv_color_hex(COL_INK), 0); - lv_obj_align(lbl_vol, LV_ALIGN_TOP_RIGHT, -12, 164); + lv_obj_align(lbl_vol, LV_ALIGN_TOP_RIGHT, UX(-12), UY(164)); lbl_audio = lv_label_create(scr); lv_label_set_text(lbl_audio, "Speaker OFF"); lv_obj_set_style_text_font(lbl_audio, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(lbl_audio, lv_color_hex(COL_MUTED), 0); - lv_obj_set_pos(lbl_audio, 12, 182); + lv_obj_set_pos(lbl_audio, UX(12), UY(182)); const int btn_y = 200; const int btn_h = 36; @@ -518,13 +518,13 @@ void ui_create() { lv_label_set_text(spk_cap, "SPK"); lv_obj_set_style_text_font(spk_cap, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(spk_cap, lv_color_hex(COL_MUTED), 0); - lv_obj_align(spk_cap, LV_ALIGN_LEFT_MID, 8, 0); + lv_obj_align(spk_cap, LV_ALIGN_LEFT_MID, UX(8), UY(0)); lbl_speaker = lv_label_create(btn_speaker); lv_label_set_text(lbl_speaker, "OFF"); lv_obj_set_style_text_font(lbl_speaker, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(lbl_speaker, lv_color_hex(COL_INK), 0); - lv_obj_align(lbl_speaker, LV_ALIGN_RIGHT_MID, -8, 0); + lv_obj_align(lbl_speaker, LV_ALIGN_RIGHT_MID, UX(-8), UY(0)); btn_start = lv_button_create(scr); lv_obj_set_pos(btn_start, 12 + btn_w + bgap, btn_y);