commit f724796359dce2a4b6b77d23940c08bddf9bf32e Author: DESKTOP-PUHFO60\dsx2016 Date: Tue Oct 27 20:13:09 2026 +0800 Add CYD host PC info panel with serial SI protocol and LVGL bars. ESP32 shows CPU/GPU/RAM/disk from host/send_sysinfo.ps1 over USB; firmware packages for 2.8 and 3.5 included. Co-authored-by: Cursor diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..609b09c --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.pio/ +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch +*.pyc +__pycache__/ +.DS_Store +Thumbs.db +*.elf +# Build dir bins stay out; firmware/ prebuilt packages stay in +!firmware/**/*.bin diff --git a/README.md b/README.md new file mode 100644 index 0000000..853e3ba --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# CYD 本机 / 服务器信息面板 + +在 **2.8" / 3.5" CYD** 上显示当前电脑的 **CPU、GPU、内存、磁盘** 等主要信息。显示/触控骨架对齐 [tftest](../tftest) / [wifitest](../wifitest);板型差异按 [CYD_PROJECT_GUIDE.md](../CYD_PROJECT_GUIDE.md)。 + +| 板型 | env | 屏 / 触控 | 背光 | +|------|-----|-----------|------| +| **2.8" · 2432S028R** | `cyd`(默认) | ILI9341 + XPT2046 SoftSPI | GPIO**21** | +| **3.5" · 3248S035C** | `cyd_35r` | ST7796 + GT911 I2C | GPIO**27** | + +本工程**无外设接线**(只吃 USB 供电 + 串口)。 + +## 原理 + +ESP32 无法直接读 PC 硬件。本机跑 [`host/send_sysinfo.ps1`](host/send_sysinfo.ps1),经 **USB 串口** 按行推送: + +``` +SI|host=DESKTOP|cpu=i7-13700K|cpu%=12.3|gpu=RTX 4070|gpu%=5|ram=18.2/31.8|disk=512/931 +``` + +屏上 LVGL 面板解析后画进度条;超过约 5 秒无新包显示 **stale**。 + +GPU 占用:优先 `nvidia-smi`,否则尝试 Windows GPU Engine 计数器;都失败则显示 `n/a`(名称仍可读)。 + +## 刷写 + +| 板型 | 目录 | +|------|------| +| 2.8" | [`firmware/cyd/`](firmware/cyd/) | +| 3.5" | [`firmware/cyd_35r/`](firmware/cyd_35r/) | + +```powershell +cd firmware\cyd # 或 firmware\cyd_35r +.\flash.ps1 +# 或 +.\flash.ps1 -Port COM3 +``` + +源码烧录:`pio run -e cyd -t upload`(3.5:`-e cyd_35r`) +重新打包:`.\firmware\export_from_build.ps1`(3.5 加 `-Env cyd_35r`) +串口监视:`pio device monitor -b 460800` + +## 推送本机信息 + +板子刷好并插上 USB 后,在 **PC** 上: + +```powershell +cd host +.\send_sysinfo.ps1 +# 或 +.\send_sysinfo.ps1 -Port COM5 -IntervalMs 1000 +``` + +注意:串口同时只能被一个程序占用;跑推送脚本时不要开 `pio device monitor`。 + +## 文件 + +``` +include/pins.h, touch.h, host_info.h, ui.h, lv_conf.h +src/main.cpp, touch.cpp, host_info.cpp, ui.cpp +host/send_sysinfo.ps1 +firmware/ +``` diff --git a/copy_lv_conf.py b/copy_lv_conf.py new file mode 100644 index 0000000..ca4f24c --- /dev/null +++ b/copy_lv_conf.py @@ -0,0 +1,13 @@ +Import("env") # noqa: F821 +from pathlib import Path + +src = Path(env["PROJECT_DIR"]) / "include" / "lv_conf.h" # noqa: F821 +dst_dir = Path(env["PROJECT_LIBDEPS_DIR"]) / env["PIOENV"] # noqa: F821 +dst_dir.mkdir(parents=True, exist_ok=True) +dst = dst_dir / "lv_conf.h" +if src.exists(): + dst.write_text(src.read_text(encoding="utf-8"), encoding="utf-8") + lvgl = dst_dir / "lvgl" / "lv_conf.h" + if (dst_dir / "lvgl").exists(): + lvgl.write_text(src.read_text(encoding="utf-8"), encoding="utf-8") + print(f"copy_lv_conf: synced -> {dst}") diff --git a/firmware/README.md b/firmware/README.md new file mode 100644 index 0000000..da917ce --- /dev/null +++ b/firmware/README.md @@ -0,0 +1,53 @@ +# 预编译固件(可随时刷写) + +| 目录 | 板子 | +|------|------| +| [`cyd/`](cyd/) | **2.8"** CYD ESP32-2432S028R(ILI9341) | +| [`cyd_35r/`](cyd_35r/) | **3.5"** CYD ESP32-3248S035C(ST7796 + GT911) | + +功能:**本机/服务器信息面板**(USB 串口收 `SI|...`,屏上显示 CPU/GPU/RAM/DISK) +版本信息见各目录下 `VERSION.txt`。 + +也可在 [`../../cydweb`](../../cydweb)(CYD Flasher)里通过浏览器串口刷写本目录固件。 + +## 直接刷写(无需重新编译) + +1. USB 连接板子 +2. 执行: + +```powershell +cd firmware\cyd # 3.5": firmware\cyd_35r +.\flash.ps1 +# 或指定端口 +.\flash.ps1 -Port COM3 +``` + +也可运行 `flash.bat`。 + +依赖其一即可: + +- 已安装 PlatformIO(`pio`,自带 esptool) +- 或 `pip install esptool` + +## 手动 esptool + +```bash +esptool.py --chip esp32 --port COM3 --baud 921600 write_flash -z ^ + --flash_mode dio --flash_freq 40m --flash_size 4MB ^ + 0x1000 bootloader.bin ^ + 0x8000 partitions.bin ^ + 0xe000 boot_app0.bin ^ + 0x10000 firmware.bin +``` + +## 从源码重新打包进本目录 + +在仓库根目录: + +```powershell +.\firmware\export_from_build.ps1 # 2.8 -> firmware/cyd +.\firmware\export_from_build.ps1 -Env cyd_35r # 3.5 -> firmware/cyd_35r +.\firmware\export_from_build.ps1 -SkipBuild +``` + +刷完后在 PC 上运行 `..\..\host\send_sysinfo.ps1` 推送本机信息。 diff --git a/firmware/cyd/VERSION.txt b/firmware/cyd/VERSION.txt new file mode 100644 index 0000000..776ed2f --- /dev/null +++ b/firmware/cyd/VERSION.txt @@ -0,0 +1,15 @@ +name: CYD serverinfotest (cyd) +git: unknown +date: 2026-10-27 +board: ESP32-2432S028R (ILI9341) +flash_size: 4MB +offsets: + 0x1000 bootloader.bin + 0x8000 partitions.bin + 0xe000 boot_app0.bin + 0x10000 firmware.bin +sha256: + bootloader.bin 3D234A7471F67B013686DABD4DEE7C1FA915C9928463616A94BC9297ACF1ABF8 + boot_app0.bin F94C5D786A7A8FAB06AC5D10E33BF37711A6697636DC037559EA19CC410A17F0 + firmware.bin 85838702F4062A3679CAE17D0365B1E14DDBB4C3BA235A0A6500FDDB84A56B6A + partitions.bin 148B959CBFF1C38AA8E1D5C0BA9D612C54997B945E56A63F41223EEF650653A1 diff --git a/firmware/cyd/boot_app0.bin b/firmware/cyd/boot_app0.bin new file mode 100644 index 0000000..13562ca Binary files /dev/null and b/firmware/cyd/boot_app0.bin differ diff --git a/firmware/cyd/bootloader.bin b/firmware/cyd/bootloader.bin new file mode 100644 index 0000000..d592359 Binary files /dev/null and b/firmware/cyd/bootloader.bin differ diff --git a/firmware/cyd/firmware.bin b/firmware/cyd/firmware.bin new file mode 100644 index 0000000..f51e269 Binary files /dev/null and b/firmware/cyd/firmware.bin differ diff --git a/firmware/cyd/flash.bat b/firmware/cyd/flash.bat new file mode 100644 index 0000000..bf983b3 --- /dev/null +++ b/firmware/cyd/flash.bat @@ -0,0 +1,5 @@ +@echo off +setlocal +cd /d "%~dp0" +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0flash.ps1" %* +exit /b %ERRORLEVEL% diff --git a/firmware/cyd/flash.ps1 b/firmware/cyd/flash.ps1 new file mode 100644 index 0000000..c7555a3 --- /dev/null +++ b/firmware/cyd/flash.ps1 @@ -0,0 +1,98 @@ +# Flash archived CYD Server Info panel firmware (no rebuild needed) +# Usage: +# .\flash.ps1 +# .\flash.ps1 -Port COM5 +# .\flash.ps1 -Port COM5 -Baud 460800 + +param( + [string]$Port = "", + [int]$Baud = 921600 +) + +$ErrorActionPreference = "Stop" +$here = Split-Path -Parent $MyInvocation.MyCommand.Path +Set-Location $here + +function Find-Esptool { + $pio = Get-Command pio -ErrorAction SilentlyContinue + if (-not $pio) { + $pioPath = Join-Path $env:USERPROFILE "AppData\Local\Programs\Python\Python312\Scripts\pio.exe" + if (Test-Path $pioPath) { + $env:Path = "$(Split-Path $pioPath);$env:Path" + $pio = Get-Command pio -ErrorAction SilentlyContinue + } + } + if ($pio) { + $pkg = Join-Path $env:USERPROFILE ".platformio\packages\tool-esptoolpy\esptool.py" + if (Test-Path $pkg) { return @{ Kind = "py"; Path = $pkg } } + } + $py = Get-Command python -ErrorAction SilentlyContinue + if ($py) { + try { + & python -c "import esptool" 2>$null + if ($LASTEXITCODE -eq 0) { return @{ Kind = "mod"; Path = "esptool" } } + } catch {} + } + $esp = Get-Command esptool.py -ErrorAction SilentlyContinue + if ($esp) { return @{ Kind = "py"; Path = $esp.Source } } + return $null +} + +if (-not $Port) { + $ports = @() + try { + Add-Type -AssemblyName System.IO.Ports -ErrorAction SilentlyContinue | Out-Null + $ports = [System.IO.Ports.SerialPort]::GetPortNames() | Sort-Object + } catch {} + if (-not $ports -or $ports.Count -eq 0) { + $ports = Get-CimInstance Win32_SerialPort -ErrorAction SilentlyContinue | + Select-Object -ExpandProperty DeviceID | Sort-Object + } + if (-not $ports -or $ports.Count -eq 0) { + Write-Error "No serial ports found. Plug in the CYD USB cable, then retry with -Port COMx" + } + if ($ports.Count -eq 1) { + $Port = $ports[0] + } else { + Write-Host "Available ports: $($ports -join ', ')" + $Port = $ports[-1] + Write-Host "Using last port: $Port (override with -Port)" + } +} + +$tool = Find-Esptool +if (-not $tool) { + Write-Error "esptool not found. Install PlatformIO (`pio`) or: pip install esptool" +} + +$needed = @("bootloader.bin", "partitions.bin", "boot_app0.bin", "firmware.bin") +foreach ($f in $needed) { + if (-not (Test-Path (Join-Path $here $f))) { + Write-Error "Missing $f — run ..\export_from_build.ps1 from repo root first" + } +} + +$argsCommon = @( + "--chip", "esp32", + "--port", $Port, + "--baud", "$Baud", + "--before", "default_reset", + "--after", "hard_reset", + "write_flash", "-z", + "--flash_mode", "dio", + "--flash_freq", "40m", + "--flash_size", "4MB", + "0x1000", "bootloader.bin", + "0x8000", "partitions.bin", + "0xe000", "boot_app0.bin", + "0x10000", "firmware.bin" +) + +Write-Host "Flashing $here -> $Port @ $Baud" +if ($tool.Kind -eq "mod") { + & python -m esptool @argsCommon +} else { + & python $tool.Path @argsCommon +} +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +Write-Host "Done. Open serial monitor at 460800 if needed." diff --git a/firmware/cyd/meta.json b/firmware/cyd/meta.json new file mode 100644 index 0000000..af648c7 --- /dev/null +++ b/firmware/cyd/meta.json @@ -0,0 +1,13 @@ +{ + "boardId": "esp32-2432s028", + "name": "CYD Server Info", + "summary": "LVGL panel for host CPU / GPU / RAM / disk via USB serial", + "description": "Receives SI|... lines from host/send_sysinfo.ps1 and shows live bars for CPU, GPU, memory and system drive. No external GPIO peripherals.", + "tags": [ + "monitor", + "host-info", + "cpu", + "gpu", + "serial" + ] +} diff --git a/firmware/cyd/partitions.bin b/firmware/cyd/partitions.bin new file mode 100644 index 0000000..2108af9 Binary files /dev/null and b/firmware/cyd/partitions.bin differ diff --git a/firmware/cyd_35r/VERSION.txt b/firmware/cyd_35r/VERSION.txt new file mode 100644 index 0000000..8c2e0d8 --- /dev/null +++ b/firmware/cyd_35r/VERSION.txt @@ -0,0 +1,15 @@ +name: CYD serverinfotest (cyd_35r) +git: unknown +date: 2026-10-27 +board: ESP32-3248S035 (ST7796) +flash_size: 4MB +offsets: + 0x1000 bootloader.bin + 0x8000 partitions.bin + 0xe000 boot_app0.bin + 0x10000 firmware.bin +sha256: + bootloader.bin 3D234A7471F67B013686DABD4DEE7C1FA915C9928463616A94BC9297ACF1ABF8 + boot_app0.bin F94C5D786A7A8FAB06AC5D10E33BF37711A6697636DC037559EA19CC410A17F0 + firmware.bin 93C0D8E2A7C31848960C2DBA2894DE9D18BF21D3D94B47DDD5406CF18A1FD1B3 + partitions.bin 148B959CBFF1C38AA8E1D5C0BA9D612C54997B945E56A63F41223EEF650653A1 diff --git a/firmware/cyd_35r/boot_app0.bin b/firmware/cyd_35r/boot_app0.bin new file mode 100644 index 0000000..13562ca Binary files /dev/null and b/firmware/cyd_35r/boot_app0.bin differ diff --git a/firmware/cyd_35r/bootloader.bin b/firmware/cyd_35r/bootloader.bin new file mode 100644 index 0000000..d592359 Binary files /dev/null and b/firmware/cyd_35r/bootloader.bin differ diff --git a/firmware/cyd_35r/firmware.bin b/firmware/cyd_35r/firmware.bin new file mode 100644 index 0000000..4afded3 Binary files /dev/null and b/firmware/cyd_35r/firmware.bin differ diff --git a/firmware/cyd_35r/flash.bat b/firmware/cyd_35r/flash.bat new file mode 100644 index 0000000..bf983b3 --- /dev/null +++ b/firmware/cyd_35r/flash.bat @@ -0,0 +1,5 @@ +@echo off +setlocal +cd /d "%~dp0" +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0flash.ps1" %* +exit /b %ERRORLEVEL% diff --git a/firmware/cyd_35r/flash.ps1 b/firmware/cyd_35r/flash.ps1 new file mode 100644 index 0000000..c7555a3 --- /dev/null +++ b/firmware/cyd_35r/flash.ps1 @@ -0,0 +1,98 @@ +# Flash archived CYD Server Info panel firmware (no rebuild needed) +# Usage: +# .\flash.ps1 +# .\flash.ps1 -Port COM5 +# .\flash.ps1 -Port COM5 -Baud 460800 + +param( + [string]$Port = "", + [int]$Baud = 921600 +) + +$ErrorActionPreference = "Stop" +$here = Split-Path -Parent $MyInvocation.MyCommand.Path +Set-Location $here + +function Find-Esptool { + $pio = Get-Command pio -ErrorAction SilentlyContinue + if (-not $pio) { + $pioPath = Join-Path $env:USERPROFILE "AppData\Local\Programs\Python\Python312\Scripts\pio.exe" + if (Test-Path $pioPath) { + $env:Path = "$(Split-Path $pioPath);$env:Path" + $pio = Get-Command pio -ErrorAction SilentlyContinue + } + } + if ($pio) { + $pkg = Join-Path $env:USERPROFILE ".platformio\packages\tool-esptoolpy\esptool.py" + if (Test-Path $pkg) { return @{ Kind = "py"; Path = $pkg } } + } + $py = Get-Command python -ErrorAction SilentlyContinue + if ($py) { + try { + & python -c "import esptool" 2>$null + if ($LASTEXITCODE -eq 0) { return @{ Kind = "mod"; Path = "esptool" } } + } catch {} + } + $esp = Get-Command esptool.py -ErrorAction SilentlyContinue + if ($esp) { return @{ Kind = "py"; Path = $esp.Source } } + return $null +} + +if (-not $Port) { + $ports = @() + try { + Add-Type -AssemblyName System.IO.Ports -ErrorAction SilentlyContinue | Out-Null + $ports = [System.IO.Ports.SerialPort]::GetPortNames() | Sort-Object + } catch {} + if (-not $ports -or $ports.Count -eq 0) { + $ports = Get-CimInstance Win32_SerialPort -ErrorAction SilentlyContinue | + Select-Object -ExpandProperty DeviceID | Sort-Object + } + if (-not $ports -or $ports.Count -eq 0) { + Write-Error "No serial ports found. Plug in the CYD USB cable, then retry with -Port COMx" + } + if ($ports.Count -eq 1) { + $Port = $ports[0] + } else { + Write-Host "Available ports: $($ports -join ', ')" + $Port = $ports[-1] + Write-Host "Using last port: $Port (override with -Port)" + } +} + +$tool = Find-Esptool +if (-not $tool) { + Write-Error "esptool not found. Install PlatformIO (`pio`) or: pip install esptool" +} + +$needed = @("bootloader.bin", "partitions.bin", "boot_app0.bin", "firmware.bin") +foreach ($f in $needed) { + if (-not (Test-Path (Join-Path $here $f))) { + Write-Error "Missing $f — run ..\export_from_build.ps1 from repo root first" + } +} + +$argsCommon = @( + "--chip", "esp32", + "--port", $Port, + "--baud", "$Baud", + "--before", "default_reset", + "--after", "hard_reset", + "write_flash", "-z", + "--flash_mode", "dio", + "--flash_freq", "40m", + "--flash_size", "4MB", + "0x1000", "bootloader.bin", + "0x8000", "partitions.bin", + "0xe000", "boot_app0.bin", + "0x10000", "firmware.bin" +) + +Write-Host "Flashing $here -> $Port @ $Baud" +if ($tool.Kind -eq "mod") { + & python -m esptool @argsCommon +} else { + & python $tool.Path @argsCommon +} +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +Write-Host "Done. Open serial monitor at 460800 if needed." diff --git a/firmware/cyd_35r/meta.json b/firmware/cyd_35r/meta.json new file mode 100644 index 0000000..f870bbc --- /dev/null +++ b/firmware/cyd_35r/meta.json @@ -0,0 +1,13 @@ +{ + "boardId": "esp32-3248s035", + "name": "CYD Server Info", + "summary": "LVGL panel for host CPU / GPU / RAM / disk via USB serial", + "description": "Receives SI|... lines from host/send_sysinfo.ps1 and shows live bars for CPU, GPU, memory and system drive. No external GPIO peripherals. 3.5C: ST7796 + GT911, BL=27.", + "tags": [ + "monitor", + "host-info", + "cpu", + "gpu", + "serial" + ] +} diff --git a/firmware/cyd_35r/partitions.bin b/firmware/cyd_35r/partitions.bin new file mode 100644 index 0000000..2108af9 Binary files /dev/null and b/firmware/cyd_35r/partitions.bin differ diff --git a/firmware/export_from_build.ps1 b/firmware/export_from_build.ps1 new file mode 100644 index 0000000..5a8c2d1 --- /dev/null +++ b/firmware/export_from_build.ps1 @@ -0,0 +1,71 @@ +# 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, + [ValidateSet("cyd", "cyd_35r")] + [string]$Env = "cyd" +) + +$ErrorActionPreference = "Stop" +$root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path) +Set-Location $root + +$env:Path = "C:\Users\dsx2016\AppData\Local\Programs\Python\Python312\Scripts;" + $env:Path + +if (-not $SkipBuild) { + pio run -e $Env + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +} + +$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 — build with: pio run -e $Env" } + Copy-Item $p $dst -Force +} + +$bootApp = Join-Path $env:USERPROFILE ".platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin" +if (-not (Test-Path $bootApp)) { + Write-Error "boot_app0.bin not found under PlatformIO packages" +} +Copy-Item $bootApp (Join-Path $dst "boot_app0.bin") -Force + +$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 serverinfotest ($dstName)", + "git: $git", + "date: $date", + $boardLine, + "flash_size: 4MB", + "offsets:", + " 0x1000 bootloader.bin", + " 0x8000 partitions.bin", + " 0xe000 boot_app0.bin", + " 0x10000 firmware.bin", + "sha256:" +) +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 (env=$Env git=$git)" diff --git a/host/send_sysinfo.ps1 b/host/send_sysinfo.ps1 new file mode 100644 index 0000000..126085b --- /dev/null +++ b/host/send_sysinfo.ps1 @@ -0,0 +1,162 @@ +# Push this PC's CPU / GPU / RAM / disk stats to CYD over USB serial. +# Usage: +# .\send_sysinfo.ps1 +# .\send_sysinfo.ps1 -Port COM5 +# .\send_sysinfo.ps1 -Port COM5 -IntervalMs 1000 + +param( + [string]$Port = "", + [int]$IntervalMs = 1000, + [int]$Baud = 460800 +) + +$ErrorActionPreference = "Stop" + +function Find-CydPort { + $ports = @() + try { + Add-Type -AssemblyName System.IO.Ports -ErrorAction SilentlyContinue | Out-Null + $ports = [System.IO.Ports.SerialPort]::GetPortNames() | Sort-Object + } catch {} + if (-not $ports -or $ports.Count -eq 0) { + $ports = Get-CimInstance Win32_SerialPort -ErrorAction SilentlyContinue | + Select-Object -ExpandProperty DeviceID | Sort-Object + } + if (-not $ports -or $ports.Count -eq 0) { + throw "No serial ports found. Plug in the CYD USB cable." + } + if ($ports.Count -eq 1) { return $ports[0] } + Write-Host "Available ports: $($ports -join ', ')" + $pick = $ports[-1] + Write-Host "Using last port: $pick (override with -Port)" + return $pick +} + +function Sanitize([string]$s, [int]$max = 40) { + if (-not $s) { return "" } + $t = ($s -replace '[|=\r\n]', ' ').Trim() + if ($t.Length -gt $max) { $t = $t.Substring(0, $max).Trim() } + return $t +} + +function Get-CpuName { + $p = Get-CimInstance Win32_Processor -ErrorAction SilentlyContinue | Select-Object -First 1 + if (-not $p) { return "CPU" } + $n = [string]$p.Name + # Shorten common Intel/AMD noise + $n = $n -replace '\(R\)|\(TM\)|CPU @.*|with Radeon.*', '' + $n = ($n -replace '\s+', ' ').Trim() + return Sanitize $n 44 +} + +function Get-CpuPct { + try { + $c = Get-CimInstance Win32_Processor -ErrorAction Stop + $vals = @($c | ForEach-Object { [double]$_.LoadPercentage }) + if ($vals.Count -eq 0) { return 0.0 } + return [math]::Round(($vals | Measure-Object -Average).Average, 1) + } catch { + return 0.0 + } +} + +function Get-GpuName { + $gpus = Get-CimInstance Win32_VideoController -ErrorAction SilentlyContinue | + Where-Object { $_.Name -and $_.Name -notmatch 'Microsoft Basic|Remote Desktop' } + if (-not $gpus) { return "GPU" } + # Prefer NVIDIA / AMD discrete if present + $pick = $gpus | Where-Object { $_.Name -match 'NVIDIA|GeForce|RTX|GTX|Radeon|AMD' } | + Select-Object -First 1 + if (-not $pick) { $pick = $gpus | Select-Object -First 1 } + $n = [string]$pick.Name + $n = $n -replace 'NVIDIA GeForce ', 'RTX/GTX ' + $n = $n -replace 'AMD ', '' + return Sanitize $n 44 +} + +function Get-GpuPct { + # 1) nvidia-smi + $smi = Get-Command nvidia-smi -ErrorAction SilentlyContinue + if ($smi) { + try { + $out = & nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>$null + if ($LASTEXITCODE -eq 0 -and $out) { + $first = ([string]($out | Select-Object -First 1)).Trim() + if ($first -match '^\d+') { return [double]$first } + } + } catch {} + } + # 2) Windows GPU Engine counter (may need a few samples) + try { + $counters = Get-Counter '\GPU Engine(*)\Utilization Percentage' -ErrorAction Stop + $samples = $counters.CounterSamples | + Where-Object { $_.CookedValue -ge 0 -and $_.InstanceName -match 'engtype_3D|engtype_Graphics' } + if ($samples) { + $sum = ($samples | Measure-Object -Property CookedValue -Sum).Sum + if ($sum -gt 100) { $sum = 100 } + return [math]::Round($sum, 1) + } + } catch {} + return -1.0 +} + +function Get-RamGb { + $os = Get-CimInstance Win32_OperatingSystem -ErrorAction Stop + $total = [math]::Round($os.TotalVisibleMemorySize / 1MB, 1) + $free = [math]::Round($os.FreePhysicalMemory / 1MB, 1) + $used = [math]::Round($total - $free, 1) + return @{ Used = $used; Total = $total } +} + +function Get-DiskGb { + $d = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='C:'" -ErrorAction SilentlyContinue + if (-not $d) { + $d = Get-CimInstance Win32_LogicalDisk -ErrorAction SilentlyContinue | + Where-Object { $_.DriveType -eq 3 -and $_.Size -gt 0 } | + Select-Object -First 1 + } + if (-not $d -or -not $d.Size) { + return @{ Used = 0.0; Total = 0.0 } + } + $total = [math]::Round($d.Size / 1GB, 1) + $free = [math]::Round($d.FreeSpace / 1GB, 1) + $used = [math]::Round($total - $free, 1) + return @{ Used = $used; Total = $total } +} + +function Build-Line { + $hostName = Sanitize $env:COMPUTERNAME 36 + $cpu = Get-CpuName + $cpuPct = Get-CpuPct + $gpu = Get-GpuName + $gpuPct = Get-GpuPct + $ram = Get-RamGb + $disk = Get-DiskGb + $gpuField = if ($gpuPct -lt 0) { "gpu%=-1" } else { "gpu%=$gpuPct" } + return "SI|host=$hostName|cpu=$cpu|cpu%=$cpuPct|gpu=$gpu|$gpuField|ram=$($ram.Used)/$($ram.Total)|disk=$($disk.Used)/$($disk.Total)" +} + +if (-not $Port) { $Port = Find-CydPort } + +Add-Type -AssemblyName System.IO.Ports +$sp = [System.IO.Ports.SerialPort]::new($Port, $Baud) +$sp.ReadTimeout = 200 +$sp.WriteTimeout = 2000 +$sp.NewLine = "`n" +$sp.DtrEnable = $true +$sp.RtsEnable = $true + +try { + $sp.Open() + Write-Host "Sending host info -> $Port @ $Baud every ${IntervalMs}ms (Ctrl+C to stop)" + Start-Sleep -Milliseconds 400 + while ($true) { + $line = Build-Line + $sp.WriteLine($line) + Write-Host $line + Start-Sleep -Milliseconds $IntervalMs + } +} finally { + if ($sp.IsOpen) { $sp.Close() } + $sp.Dispose() +} diff --git a/include/host_info.h b/include/host_info.h new file mode 100644 index 0000000..a5e57cd --- /dev/null +++ b/include/host_info.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +/** Max chars for hostname / CPU / GPU name shown on UI. */ +static constexpr int HOST_NAME_MAX = 40; +static constexpr int HOST_CPU_MAX = 48; +static constexpr int HOST_GPU_MAX = 48; + +struct HostInfo { + char host[HOST_NAME_MAX]; + char cpu[HOST_CPU_MAX]; + char gpu[HOST_GPU_MAX]; + float cpu_pct; // 0..100 + float gpu_pct; // 0..100, -1 if unknown + float ram_used_gb; + float ram_total_gb; + float disk_used_gb; + float disk_total_gb; + bool valid; + uint32_t updated_ms; +}; + +void host_info_begin(); +/** Poll USB Serial for SI|... lines; return true if a packet was applied. */ +bool host_info_poll(); +const HostInfo &host_info_get(); +/** True if last packet is fresher than stale_ms. */ +bool host_info_is_live(uint32_t stale_ms = 5000); diff --git a/include/lv_conf.h b/include/lv_conf.h new file mode 100644 index 0000000..94de0de --- /dev/null +++ b/include/lv_conf.h @@ -0,0 +1,1127 @@ +/** + * @file lv_conf.h + * Configuration file for v9.2.2 + */ + +/* + * Copy this file as `lv_conf.h` + * 1. simply next to the `lvgl` folder + * 2. or any other places and + * - define `LV_CONF_INCLUDE_SIMPLE` + * - add the path as include path + */ + +/* clang-format off */ +#if 1 /*Set it to "1" to enable content*/ + +#ifndef LV_CONF_H +#define LV_CONF_H + +/*If you need to include anything here, do it inside the `__ASSEMBLY__` guard */ +#if 0 && defined(__ASSEMBLY__) +#include "my_include.h" +#endif + +/*==================== + COLOR SETTINGS + *====================*/ + +/*Color depth: 1 (I1), 8 (L8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888)*/ +#define LV_COLOR_DEPTH 16 + +/*========================= + STDLIB WRAPPER SETTINGS + *=========================*/ + +/* Possible values + * - LV_STDLIB_BUILTIN: LVGL's built in implementation + * - LV_STDLIB_CLIB: Standard C functions, like malloc, strlen, etc + * - LV_STDLIB_MICROPYTHON: MicroPython implementation + * - LV_STDLIB_RTTHREAD: RT-Thread implementation + * - LV_STDLIB_CUSTOM: Implement the functions externally + */ +#define LV_USE_STDLIB_MALLOC LV_STDLIB_BUILTIN +#define LV_USE_STDLIB_STRING LV_STDLIB_BUILTIN +#define LV_USE_STDLIB_SPRINTF LV_STDLIB_BUILTIN + +#define LV_STDINT_INCLUDE +#define LV_STDDEF_INCLUDE +#define LV_STDBOOL_INCLUDE +#define LV_INTTYPES_INCLUDE +#define LV_LIMITS_INCLUDE +#define LV_STDARG_INCLUDE + +#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN + /*Size of the memory available for `lv_malloc()` in bytes (>= 2kB)*/ + #define LV_MEM_SIZE (64 * 1024U) /*[bytes]*/ + + /*Size of the memory expand for `lv_malloc()` in bytes*/ + #define LV_MEM_POOL_EXPAND_SIZE 0 + + /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ + #define LV_MEM_ADR 0 /*0: unused*/ + /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/ + #if LV_MEM_ADR == 0 + #undef LV_MEM_POOL_INCLUDE + #undef LV_MEM_POOL_ALLOC + #endif +#endif /*LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN*/ + +/*==================== + HAL SETTINGS + *====================*/ + +/*Default display refresh, input device read and animation step period.*/ +#define LV_DEF_REFR_PERIOD 33 /*[ms]*/ + +/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings. + *(Not so important, you can adjust it to modify default sizes and spaces)*/ +#define LV_DPI_DEF 130 /*[px/inch]*/ + +/*================= + * OPERATING SYSTEM + *=================*/ +/*Select an operating system to use. Possible options: + * - LV_OS_NONE + * - LV_OS_PTHREAD + * - LV_OS_FREERTOS + * - LV_OS_CMSIS_RTOS2 + * - LV_OS_RTTHREAD + * - LV_OS_WINDOWS + * - LV_OS_MQX + * - LV_OS_CUSTOM */ +#define LV_USE_OS LV_OS_NONE + +#if LV_USE_OS == LV_OS_CUSTOM + #define LV_OS_CUSTOM_INCLUDE +#endif +#if LV_USE_OS == LV_OS_FREERTOS + /* + * Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM + * than unblocking a task using an intermediary object such as a binary semaphore. + * RTOS task notifications can only be used when there is only one task that can be the recipient of the event. + */ + #define LV_USE_FREERTOS_TASK_NOTIFY 1 +#endif + +/*======================== + * RENDERING CONFIGURATION + *========================*/ + +/*Align the stride of all layers and images to this bytes*/ +#define LV_DRAW_BUF_STRIDE_ALIGN 1 + +/*Align the start address of draw_buf addresses to this bytes*/ +#define LV_DRAW_BUF_ALIGN 4 + +/*Using matrix for transformations. + *Requirements: + `LV_USE_MATRIX = 1`. + The rendering engine needs to support 3x3 matrix transformations.*/ +#define LV_DRAW_TRANSFORM_USE_MATRIX 0 + +/* If a widget has `style_opa < 255` (not `bg_opa`, `text_opa` etc) or not NORMAL blend mode + * it is buffered into a "simple" layer before rendering. The widget can be buffered in smaller chunks. + * "Transformed layers" (if `transform_angle/zoom` are set) use larger buffers + * and can't be drawn in chunks. */ + +/*The target buffer size for simple layer chunks.*/ +#define LV_DRAW_LAYER_SIMPLE_BUF_SIZE (24 * 1024) /*[bytes]*/ + +/* The stack size of the drawing thread. + * NOTE: If FreeType or ThorVG is enabled, it is recommended to set it to 32KB or more. + */ +#define LV_DRAW_THREAD_STACK_SIZE (8 * 1024) /*[bytes]*/ + +#define LV_USE_DRAW_SW 1 +#if LV_USE_DRAW_SW == 1 + + /* + * Selectively disable color format support in order to reduce code size. + * NOTE: some features use certain color formats internally, e.g. + * - gradients use RGB888 + * - bitmaps with transparency may use ARGB8888 + */ + + #define LV_DRAW_SW_SUPPORT_RGB565 1 + #define LV_DRAW_SW_SUPPORT_RGB565A8 1 + #define LV_DRAW_SW_SUPPORT_RGB888 1 + #define LV_DRAW_SW_SUPPORT_XRGB8888 1 + #define LV_DRAW_SW_SUPPORT_ARGB8888 1 + #define LV_DRAW_SW_SUPPORT_L8 1 + #define LV_DRAW_SW_SUPPORT_AL88 1 + #define LV_DRAW_SW_SUPPORT_A8 1 + #define LV_DRAW_SW_SUPPORT_I1 1 + + /* Set the number of draw unit. + * > 1 requires an operating system enabled in `LV_USE_OS` + * > 1 means multiple threads will render the screen in parallel */ + #define LV_DRAW_SW_DRAW_UNIT_CNT 1 + + /* Use Arm-2D to accelerate the sw render */ + #define LV_USE_DRAW_ARM2D_SYNC 0 + + /* Enable native helium assembly to be compiled */ + #define LV_USE_NATIVE_HELIUM_ASM 0 + + /* 0: use a simple renderer capable of drawing only simple rectangles with gradient, images, texts, and straight lines only + * 1: use a complex renderer capable of drawing rounded corners, shadow, skew lines, and arcs too */ + #define LV_DRAW_SW_COMPLEX 1 + + #if LV_DRAW_SW_COMPLEX == 1 + /*Allow buffering some shadow calculation. + *LV_DRAW_SW_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius` + *Caching has LV_DRAW_SW_SHADOW_CACHE_SIZE^2 RAM cost*/ + #define LV_DRAW_SW_SHADOW_CACHE_SIZE 0 + + /* Set number of maximally cached circle data. + * The circumference of 1/4 circle are saved for anti-aliasing + * radius * 4 bytes are used per circle (the most often used radiuses are saved) + * 0: to disable caching */ + #define LV_DRAW_SW_CIRCLE_CACHE_SIZE 4 + #endif + + #define LV_USE_DRAW_SW_ASM LV_DRAW_SW_ASM_NONE + + #if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_CUSTOM + #define LV_DRAW_SW_ASM_CUSTOM_INCLUDE "" + #endif + + /* Enable drawing complex gradients in software: linear at an angle, radial or conical */ + #define LV_USE_DRAW_SW_COMPLEX_GRADIENTS 0 +#endif + +/* Use NXP's VG-Lite GPU on iMX RTxxx platforms. */ +#define LV_USE_DRAW_VGLITE 0 + +#if LV_USE_DRAW_VGLITE + /* Enable blit quality degradation workaround recommended for screen's dimension > 352 pixels. */ + #define LV_USE_VGLITE_BLIT_SPLIT 0 + + #if LV_USE_OS + /* Use additional draw thread for VG-Lite processing.*/ + #define LV_USE_VGLITE_DRAW_THREAD 1 + + #if LV_USE_VGLITE_DRAW_THREAD + /* Enable VGLite draw async. Queue multiple tasks and flash them once to the GPU. */ + #define LV_USE_VGLITE_DRAW_ASYNC 1 + #endif + #endif + + /* Enable VGLite asserts. */ + #define LV_USE_VGLITE_ASSERT 0 +#endif + +/* Use NXP's PXP on iMX RTxxx platforms. */ +#define LV_USE_PXP 0 + +#if LV_USE_PXP + /* Use PXP for drawing.*/ + #define LV_USE_DRAW_PXP 1 + + /* Use PXP to rotate display.*/ + #define LV_USE_ROTATE_PXP 0 + + #if LV_USE_DRAW_PXP && LV_USE_OS + /* Use additional draw thread for PXP processing.*/ + #define LV_USE_PXP_DRAW_THREAD 1 + #endif + + /* Enable PXP asserts. */ + #define LV_USE_PXP_ASSERT 0 +#endif + +/* Use Renesas Dave2D on RA platforms. */ +#define LV_USE_DRAW_DAVE2D 0 + +/* Draw using cached SDL textures*/ +#define LV_USE_DRAW_SDL 0 + +/* Use VG-Lite GPU. */ +#define LV_USE_DRAW_VG_LITE 0 + +#if LV_USE_DRAW_VG_LITE + /* Enable VG-Lite custom external 'gpu_init()' function */ + #define LV_VG_LITE_USE_GPU_INIT 0 + + /* Enable VG-Lite assert. */ + #define LV_VG_LITE_USE_ASSERT 0 + + /* VG-Lite flush commit trigger threshold. GPU will try to batch these many draw tasks. */ + #define LV_VG_LITE_FLUSH_MAX_COUNT 8 + + /* Enable border to simulate shadow + * NOTE: which usually improves performance, + * but does not guarantee the same rendering quality as the software. */ + #define LV_VG_LITE_USE_BOX_SHADOW 0 + + /* VG-Lite gradient maximum cache number. + * NOTE: The memory usage of a single gradient image is 4K bytes. + */ + #define LV_VG_LITE_GRAD_CACHE_CNT 32 + + /* VG-Lite stroke maximum cache number. + */ + #define LV_VG_LITE_STROKE_CACHE_CNT 32 + +#endif + +/*======================= + * FEATURE CONFIGURATION + *=======================*/ + +/*------------- + * Logging + *-----------*/ + +/*Enable the log module*/ +#define LV_USE_LOG 0 +#if LV_USE_LOG + + /*How important log should be added: + *LV_LOG_LEVEL_TRACE A lot of logs to give detailed information + *LV_LOG_LEVEL_INFO Log important events + *LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem + *LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail + *LV_LOG_LEVEL_USER Only logs added by the user + *LV_LOG_LEVEL_NONE Do not log anything*/ + #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN + + /*1: Print the log with 'printf'; + *0: User need to register a callback with `lv_log_register_print_cb()`*/ + #define LV_LOG_PRINTF 0 + + /*Set callback to print the logs. + *E.g `my_print`. The prototype should be `void my_print(lv_log_level_t level, const char * buf)` + *Can be overwritten by `lv_log_register_print_cb`*/ + //#define LV_LOG_PRINT_CB + + /*1: Enable print timestamp; + *0: Disable print timestamp*/ + #define LV_LOG_USE_TIMESTAMP 1 + + /*1: Print file and line number of the log; + *0: Do not print file and line number of the log*/ + #define LV_LOG_USE_FILE_LINE 1 + + + /*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/ + #define LV_LOG_TRACE_MEM 1 + #define LV_LOG_TRACE_TIMER 1 + #define LV_LOG_TRACE_INDEV 1 + #define LV_LOG_TRACE_DISP_REFR 1 + #define LV_LOG_TRACE_EVENT 1 + #define LV_LOG_TRACE_OBJ_CREATE 1 + #define LV_LOG_TRACE_LAYOUT 1 + #define LV_LOG_TRACE_ANIM 1 + #define LV_LOG_TRACE_CACHE 1 + +#endif /*LV_USE_LOG*/ + +/*------------- + * Asserts + *-----------*/ + +/*Enable asserts if an operation is failed or an invalid data is found. + *If LV_USE_LOG is enabled an error message will be printed on failure*/ +#define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/ +#define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/ +#define LV_USE_ASSERT_STYLE 0 /*Check if the styles are properly initialized. (Very fast, recommended)*/ +#define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/ +#define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/ + +/*Add a custom handler when assert happens e.g. to restart the MCU*/ +#define LV_ASSERT_HANDLER_INCLUDE +#define LV_ASSERT_HANDLER while(1); /*Halt by default*/ + +/*------------- + * Debug + *-----------*/ + +/*1: Draw random colored rectangles over the redrawn areas*/ +#define LV_USE_REFR_DEBUG 0 + +/*1: Draw a red overlay for ARGB layers and a green overlay for RGB layers*/ +#define LV_USE_LAYER_DEBUG 0 + +/*1: Draw overlays with different colors for each draw_unit's tasks. + *Also add the index number of the draw unit on white background. + *For layers add the index number of the draw unit on black background.*/ +#define LV_USE_PARALLEL_DRAW_DEBUG 0 + +/*------------- + * Others + *-----------*/ + +#define LV_ENABLE_GLOBAL_CUSTOM 0 +#if LV_ENABLE_GLOBAL_CUSTOM + /*Header to include for the custom 'lv_global' function"*/ + #define LV_GLOBAL_CUSTOM_INCLUDE +#endif + +/*Default cache size in bytes. + *Used by image decoders such as `lv_lodepng` to keep the decoded image in the memory. + *If size is not set to 0, the decoder will fail to decode when the cache is full. + *If size is 0, the cache function is not enabled and the decoded mem will be released immediately after use.*/ +#define LV_CACHE_DEF_SIZE 0 + +/*Default number of image header cache entries. The cache is used to store the headers of images + *The main logic is like `LV_CACHE_DEF_SIZE` but for image headers.*/ +#define LV_IMAGE_HEADER_CACHE_DEF_CNT 0 + +/*Number of stops allowed per gradient. Increase this to allow more stops. + *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ +#define LV_GRADIENT_MAX_STOPS 2 + +/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently. + * 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */ +#define LV_COLOR_MIX_ROUND_OFS 0 + +/* Add 2 x 32 bit variables to each lv_obj_t to speed up getting style properties */ +#define LV_OBJ_STYLE_CACHE 0 + +/* Add `id` field to `lv_obj_t` */ +#define LV_USE_OBJ_ID 0 + +/* Automatically assign an ID when obj is created */ +#define LV_OBJ_ID_AUTO_ASSIGN LV_USE_OBJ_ID + +/*Use the builtin obj ID handler functions: +* - lv_obj_assign_id: Called when a widget is created. Use a separate counter for each widget class as an ID. +* - lv_obj_id_compare: Compare the ID to decide if it matches with a requested value. +* - lv_obj_stringify_id: Return e.g. "button3" +* - lv_obj_free_id: Does nothing, as there is no memory allocation for the ID. +* When disabled these functions needs to be implemented by the user.*/ +#define LV_USE_OBJ_ID_BUILTIN 1 + +/*Use obj property set/get API*/ +#define LV_USE_OBJ_PROPERTY 0 + +/*Enable property name support*/ +#define LV_USE_OBJ_PROPERTY_NAME 1 + +/* VG-Lite Simulator */ +/*Requires: LV_USE_THORVG_INTERNAL or LV_USE_THORVG_EXTERNAL */ +#define LV_USE_VG_LITE_THORVG 0 + +#if LV_USE_VG_LITE_THORVG + + /*Enable LVGL's blend mode support*/ + #define LV_VG_LITE_THORVG_LVGL_BLEND_SUPPORT 0 + + /*Enable YUV color format support*/ + #define LV_VG_LITE_THORVG_YUV_SUPPORT 0 + + /*Enable Linear gradient extension support*/ + #define LV_VG_LITE_THORVG_LINEAR_GRADIENT_EXT_SUPPORT 0 + + /*Enable 16 pixels alignment*/ + #define LV_VG_LITE_THORVG_16PIXELS_ALIGN 1 + + /*Buffer address alignment*/ + #define LV_VG_LITE_THORVG_BUF_ADDR_ALIGN 64 + + /*Enable multi-thread render*/ + #define LV_VG_LITE_THORVG_THREAD_RENDER 0 + +#endif + +/*===================== + * COMPILER SETTINGS + *====================*/ + +/*For big endian systems set to 1*/ +#define LV_BIG_ENDIAN_SYSTEM 0 + +/*Define a custom attribute to `lv_tick_inc` function*/ +#define LV_ATTRIBUTE_TICK_INC + +/*Define a custom attribute to `lv_timer_handler` function*/ +#define LV_ATTRIBUTE_TIMER_HANDLER + +/*Define a custom attribute to `lv_display_flush_ready` function*/ +#define LV_ATTRIBUTE_FLUSH_READY + +/*Required alignment size for buffers*/ +#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1 + +/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default). + * E.g. __attribute__((aligned(4)))*/ +#define LV_ATTRIBUTE_MEM_ALIGN + +/*Attribute to mark large constant arrays for example font's bitmaps*/ +#define LV_ATTRIBUTE_LARGE_CONST + +/*Compiler prefix for a big array declaration in RAM*/ +#define LV_ATTRIBUTE_LARGE_RAM_ARRAY + +/*Place performance critical functions into a faster memory (e.g RAM)*/ +#define LV_ATTRIBUTE_FAST_MEM + +/*Export integer constant to binding. This macro is used with constants in the form of LV_ that + *should also appear on LVGL binding API such as MicroPython.*/ +#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/ + +/*Prefix all global extern data with this*/ +#define LV_ATTRIBUTE_EXTERN_DATA + +/* Use `float` as `lv_value_precise_t` */ +#define LV_USE_FLOAT 0 + +/*Enable matrix support + *Requires `LV_USE_FLOAT = 1`*/ +#define LV_USE_MATRIX 0 + +/*Include `lvgl_private.h` in `lvgl.h` to access internal data and functions by default*/ +#define LV_USE_PRIVATE_API 0 + +/*================== + * FONT USAGE + *===================*/ + +/*Montserrat fonts with ASCII range and some symbols using bpp = 4 + *https://fonts.google.com/specimen/Montserrat*/ +#define LV_FONT_MONTSERRAT_8 0 +#define LV_FONT_MONTSERRAT_10 0 +#define LV_FONT_MONTSERRAT_12 0 +#define LV_FONT_MONTSERRAT_14 1 +#define LV_FONT_MONTSERRAT_16 1 +#define LV_FONT_MONTSERRAT_18 1 +#define LV_FONT_MONTSERRAT_20 1 +#define LV_FONT_MONTSERRAT_22 0 +#define LV_FONT_MONTSERRAT_24 0 +#define LV_FONT_MONTSERRAT_26 0 +#define LV_FONT_MONTSERRAT_28 0 +#define LV_FONT_MONTSERRAT_30 0 +#define LV_FONT_MONTSERRAT_32 0 +#define LV_FONT_MONTSERRAT_34 0 +#define LV_FONT_MONTSERRAT_36 0 +#define LV_FONT_MONTSERRAT_38 0 +#define LV_FONT_MONTSERRAT_40 0 +#define LV_FONT_MONTSERRAT_42 0 +#define LV_FONT_MONTSERRAT_44 0 +#define LV_FONT_MONTSERRAT_46 0 +#define LV_FONT_MONTSERRAT_48 0 + +/*Demonstrate special features*/ +#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/ +#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Persian letters and all their forms*/ +#define LV_FONT_SIMSUN_14_CJK 0 /*1000 most common CJK radicals*/ +#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/ + +/*Pixel perfect monospace fonts*/ +#define LV_FONT_UNSCII_8 0 +#define LV_FONT_UNSCII_16 0 + +/*Optionally declare custom fonts here. + *You can use these fonts as default font too and they will be available globally. + *E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/ +#define LV_FONT_CUSTOM_DECLARE + +/*Always set a default font*/ +#define LV_FONT_DEFAULT &lv_font_montserrat_14 + +/*Enable handling large font and/or fonts with a lot of characters. + *The limit depends on the font size, font face and bpp. + *Compiler error will be triggered if a font needs it.*/ +#define LV_FONT_FMT_TXT_LARGE 0 + +/*Enables/disables support for compressed fonts.*/ +#define LV_USE_FONT_COMPRESSED 0 + +/*Enable drawing placeholders when glyph dsc is not found*/ +#define LV_USE_FONT_PLACEHOLDER 1 + +/*================= + * TEXT SETTINGS + *=================*/ + +/** + * Select a character encoding for strings. + * Your IDE or editor should have the same character encoding + * - LV_TXT_ENC_UTF8 + * - LV_TXT_ENC_ASCII + */ +#define LV_TXT_ENC LV_TXT_ENC_UTF8 + +/*Can break (wrap) texts on these chars*/ +#define LV_TXT_BREAK_CHARS " ,.;:-_)]}" + +/*If a word is at least this long, will break wherever "prettiest" + *To disable, set to a value <= 0*/ +#define LV_TXT_LINE_BREAK_LONG_LEN 0 + +/*Minimum number of characters in a long word to put on a line before a break. + *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ +#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 + +/*Minimum number of characters in a long word to put on a line after a break. + *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ +#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 + +/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts. + *The direction will be processed according to the Unicode Bidirectional Algorithm: + *https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/ +#define LV_USE_BIDI 0 +#if LV_USE_BIDI + /*Set the default direction. Supported values: + *`LV_BASE_DIR_LTR` Left-to-Right + *`LV_BASE_DIR_RTL` Right-to-Left + *`LV_BASE_DIR_AUTO` detect texts base direction*/ + #define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO +#endif + +/*Enable Arabic/Persian processing + *In these languages characters should be replaced with another form based on their position in the text*/ +#define LV_USE_ARABIC_PERSIAN_CHARS 0 + +/*================== + * WIDGETS + *================*/ + +/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/ + +#define LV_WIDGETS_HAS_DEFAULT_VALUE 1 + +#define LV_USE_ANIMIMG 1 + +#define LV_USE_ARC 1 + +#define LV_USE_BAR 1 + +#define LV_USE_BUTTON 1 + +#define LV_USE_BUTTONMATRIX 1 + +#define LV_USE_CALENDAR 1 +#if LV_USE_CALENDAR + #define LV_CALENDAR_WEEK_STARTS_MONDAY 0 + #if LV_CALENDAR_WEEK_STARTS_MONDAY + #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"} + #else + #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"} + #endif + + #define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} + #define LV_USE_CALENDAR_HEADER_ARROW 1 + #define LV_USE_CALENDAR_HEADER_DROPDOWN 1 + #define LV_USE_CALENDAR_CHINESE 0 +#endif /*LV_USE_CALENDAR*/ + +#define LV_USE_CANVAS 1 + +#define LV_USE_CHART 1 + +#define LV_USE_CHECKBOX 1 + +#define LV_USE_DROPDOWN 1 /*Requires: lv_label*/ + +#define LV_USE_IMAGE 1 /*Requires: lv_label*/ + +#define LV_USE_IMAGEBUTTON 1 + +#define LV_USE_KEYBOARD 1 + +#define LV_USE_LABEL 1 +#if LV_USE_LABEL + #define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/ + #define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/ + #define LV_LABEL_WAIT_CHAR_COUNT 3 /*The count of wait chart*/ +#endif + +#define LV_USE_LED 1 + +#define LV_USE_LINE 1 + +#define LV_USE_LIST 1 + +#define LV_USE_LOTTIE 0 /*Requires: lv_canvas, thorvg */ + +#define LV_USE_MENU 1 + +#define LV_USE_MSGBOX 1 + +#define LV_USE_ROLLER 1 /*Requires: lv_label*/ + +#define LV_USE_SCALE 1 + +#define LV_USE_SLIDER 1 /*Requires: lv_bar*/ + +#define LV_USE_SPAN 1 +#if LV_USE_SPAN + /*A line text can contain maximum num of span descriptor */ + #define LV_SPAN_SNIPPET_STACK_SIZE 64 +#endif + +#define LV_USE_SPINBOX 1 + +#define LV_USE_SPINNER 1 + +#define LV_USE_SWITCH 1 + +#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/ +#if LV_USE_TEXTAREA != 0 + #define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/ +#endif + +#define LV_USE_TABLE 1 + +#define LV_USE_TABVIEW 1 + +#define LV_USE_TILEVIEW 1 + +#define LV_USE_WIN 1 + +/*================== + * THEMES + *==================*/ + +/*A simple, impressive and very complete theme*/ +#define LV_USE_THEME_DEFAULT 1 +#if LV_USE_THEME_DEFAULT + + /*0: Light mode; 1: Dark mode*/ + #define LV_THEME_DEFAULT_DARK 0 + + /*1: Enable grow on press*/ + #define LV_THEME_DEFAULT_GROW 1 + + /*Default transition time in [ms]*/ + #define LV_THEME_DEFAULT_TRANSITION_TIME 80 +#endif /*LV_USE_THEME_DEFAULT*/ + +/*A very simple theme that is a good starting point for a custom theme*/ +#define LV_USE_THEME_SIMPLE 1 + +/*A theme designed for monochrome displays*/ +#define LV_USE_THEME_MONO 1 + +/*================== + * LAYOUTS + *==================*/ + +/*A layout similar to Flexbox in CSS.*/ +#define LV_USE_FLEX 1 + +/*A layout similar to Grid in CSS.*/ +#define LV_USE_GRID 1 + +/*==================== + * 3RD PARTS LIBRARIES + *====================*/ + +/*File system interfaces for common APIs */ + +/*Setting a default driver letter allows skipping the driver prefix in filepaths*/ +#define LV_FS_DEFAULT_DRIVE_LETTER '\0' + +/*API for fopen, fread, etc*/ +#define LV_USE_FS_STDIO 0 +#if LV_USE_FS_STDIO + #define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for open, read, etc*/ +#define LV_USE_FS_POSIX 0 +#if LV_USE_FS_POSIX + #define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for CreateFile, ReadFile, etc*/ +#define LV_USE_FS_WIN32 0 +#if LV_USE_FS_WIN32 + #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/ +#define LV_USE_FS_FATFS 0 +#if LV_USE_FS_FATFS + #define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for memory-mapped file access. */ +#define LV_USE_FS_MEMFS 0 +#if LV_USE_FS_MEMFS + #define LV_FS_MEMFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#endif + +/*API for LittleFs. */ +#define LV_USE_FS_LITTLEFS 0 +#if LV_USE_FS_LITTLEFS + #define LV_FS_LITTLEFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#endif + +/*API for Arduino LittleFs. */ +#define LV_USE_FS_ARDUINO_ESP_LITTLEFS 0 +#if LV_USE_FS_ARDUINO_ESP_LITTLEFS + #define LV_FS_ARDUINO_ESP_LITTLEFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#endif + +/*API for Arduino Sd. */ +#define LV_USE_FS_ARDUINO_SD 0 +#if LV_USE_FS_ARDUINO_SD + #define LV_FS_ARDUINO_SD_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#endif + +/*LODEPNG decoder library*/ +#define LV_USE_LODEPNG 0 + +/*PNG decoder(libpng) library*/ +#define LV_USE_LIBPNG 0 + +/*BMP decoder library*/ +#define LV_USE_BMP 0 + +/* JPG + split JPG decoder library. + * Split JPG is a custom format optimized for embedded systems. */ +#define LV_USE_TJPGD 0 + +/* libjpeg-turbo decoder library. + * Supports complete JPEG specifications and high-performance JPEG decoding. */ +#define LV_USE_LIBJPEG_TURBO 0 + +/*GIF decoder library*/ +#define LV_USE_GIF 0 +#if LV_USE_GIF + /*GIF decoder accelerate*/ + #define LV_GIF_CACHE_DECODE_DATA 0 +#endif + + +/*Decode bin images to RAM*/ +#define LV_BIN_DECODER_RAM_LOAD 0 + +/*RLE decompress library*/ +#define LV_USE_RLE 0 + +/*QR code library*/ +#define LV_USE_QRCODE 0 + +/*Barcode code library*/ +#define LV_USE_BARCODE 0 + +/*FreeType library*/ +#define LV_USE_FREETYPE 0 +#if LV_USE_FREETYPE + /*Let FreeType to use LVGL memory and file porting*/ + #define LV_FREETYPE_USE_LVGL_PORT 0 + + /*Cache count of the glyphs in FreeType. It means the number of glyphs that can be cached. + *The higher the value, the more memory will be used.*/ + #define LV_FREETYPE_CACHE_FT_GLYPH_CNT 256 +#endif + +/* Built-in TTF decoder */ +#define LV_USE_TINY_TTF 0 +#if LV_USE_TINY_TTF + /* Enable loading TTF data from files */ + #define LV_TINY_TTF_FILE_SUPPORT 0 + #define LV_TINY_TTF_CACHE_GLYPH_CNT 256 +#endif + +/*Rlottie library*/ +#define LV_USE_RLOTTIE 0 + +/*Enable Vector Graphic APIs + *Requires `LV_USE_MATRIX = 1`*/ +#define LV_USE_VECTOR_GRAPHIC 0 + +/* Enable ThorVG (vector graphics library) from the src/libs folder */ +#define LV_USE_THORVG_INTERNAL 0 + +/* Enable ThorVG by assuming that its installed and linked to the project */ +#define LV_USE_THORVG_EXTERNAL 0 + +/*Use lvgl built-in LZ4 lib*/ +#define LV_USE_LZ4_INTERNAL 0 + +/*Use external LZ4 library*/ +#define LV_USE_LZ4_EXTERNAL 0 + +/*FFmpeg library for image decoding and playing videos + *Supports all major image formats so do not enable other image decoder with it*/ +#define LV_USE_FFMPEG 0 +#if LV_USE_FFMPEG + /*Dump input information to stderr*/ + #define LV_FFMPEG_DUMP_FORMAT 0 +#endif + +/*================== + * OTHERS + *==================*/ + +/*1: Enable API to take snapshot for object*/ +#define LV_USE_SNAPSHOT 0 + +/*1: Enable system monitor component*/ +#define LV_USE_SYSMON 0 +#if LV_USE_SYSMON + /*Get the idle percentage. E.g. uint32_t my_get_idle(void);*/ + #define LV_SYSMON_GET_IDLE lv_timer_get_idle + + /*1: Show CPU usage and FPS count + * Requires `LV_USE_SYSMON = 1`*/ + #define LV_USE_PERF_MONITOR 0 + #if LV_USE_PERF_MONITOR + #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT + + /*0: Displays performance data on the screen, 1: Prints performance data using log.*/ + #define LV_USE_PERF_MONITOR_LOG_MODE 0 + #endif + + /*1: Show the used memory and the memory fragmentation + * Requires `LV_USE_STDLIB_MALLOC = LV_STDLIB_BUILTIN` + * Requires `LV_USE_SYSMON = 1`*/ + #define LV_USE_MEM_MONITOR 0 + #if LV_USE_MEM_MONITOR + #define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT + #endif + +#endif /*LV_USE_SYSMON*/ + +/*1: Enable the runtime performance profiler*/ +#define LV_USE_PROFILER 0 +#if LV_USE_PROFILER + /*1: Enable the built-in profiler*/ + #define LV_USE_PROFILER_BUILTIN 1 + #if LV_USE_PROFILER_BUILTIN + /*Default profiler trace buffer size*/ + #define LV_PROFILER_BUILTIN_BUF_SIZE (16 * 1024) /*[bytes]*/ + #endif + + /*Header to include for the profiler*/ + #define LV_PROFILER_INCLUDE "lvgl/src/misc/lv_profiler_builtin.h" + + /*Profiler start point function*/ + #define LV_PROFILER_BEGIN LV_PROFILER_BUILTIN_BEGIN + + /*Profiler end point function*/ + #define LV_PROFILER_END LV_PROFILER_BUILTIN_END + + /*Profiler start point function with custom tag*/ + #define LV_PROFILER_BEGIN_TAG LV_PROFILER_BUILTIN_BEGIN_TAG + + /*Profiler end point function with custom tag*/ + #define LV_PROFILER_END_TAG LV_PROFILER_BUILTIN_END_TAG +#endif + +/*1: Enable Monkey test*/ +#define LV_USE_MONKEY 0 + +/*1: Enable grid navigation*/ +#define LV_USE_GRIDNAV 0 + +/*1: Enable lv_obj fragment*/ +#define LV_USE_FRAGMENT 0 + +/*1: Support using images as font in label or span widgets */ +#define LV_USE_IMGFONT 0 + +/*1: Enable an observer pattern implementation*/ +#define LV_USE_OBSERVER 1 + +/*1: Enable Pinyin input method*/ +/*Requires: lv_keyboard*/ +#define LV_USE_IME_PINYIN 0 +#if LV_USE_IME_PINYIN + /*1: Use default thesaurus*/ + /*If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesaurus*/ + #define LV_IME_PINYIN_USE_DEFAULT_DICT 1 + /*Set the maximum number of candidate panels that can be displayed*/ + /*This needs to be adjusted according to the size of the screen*/ + #define LV_IME_PINYIN_CAND_TEXT_NUM 6 + + /*Use 9 key input(k9)*/ + #define LV_IME_PINYIN_USE_K9_MODE 1 + #if LV_IME_PINYIN_USE_K9_MODE == 1 + #define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3 + #endif /*LV_IME_PINYIN_USE_K9_MODE*/ +#endif + +/*1: Enable file explorer*/ +/*Requires: lv_table*/ +#define LV_USE_FILE_EXPLORER 0 +#if LV_USE_FILE_EXPLORER + /*Maximum length of path*/ + #define LV_FILE_EXPLORER_PATH_MAX_LEN (128) + /*Quick access bar, 1:use, 0:not use*/ + /*Requires: lv_list*/ + #define LV_FILE_EXPLORER_QUICK_ACCESS 1 +#endif + +/*================== + * DEVICES + *==================*/ + +/*Use SDL to open window on PC and handle mouse and keyboard*/ +#define LV_USE_SDL 0 +#if LV_USE_SDL + #define LV_SDL_INCLUDE_PATH + #define LV_SDL_RENDER_MODE LV_DISPLAY_RENDER_MODE_DIRECT /*LV_DISPLAY_RENDER_MODE_DIRECT is recommended for best performance*/ + #define LV_SDL_BUF_COUNT 1 /*1 or 2*/ + #define LV_SDL_ACCELERATED 1 /*1: Use hardware acceleration*/ + #define LV_SDL_FULLSCREEN 0 /*1: Make the window full screen by default*/ + #define LV_SDL_DIRECT_EXIT 1 /*1: Exit the application when all SDL windows are closed*/ + #define LV_SDL_MOUSEWHEEL_MODE LV_SDL_MOUSEWHEEL_MODE_ENCODER /*LV_SDL_MOUSEWHEEL_MODE_ENCODER/CROWN*/ +#endif + +/*Use X11 to open window on Linux desktop and handle mouse and keyboard*/ +#define LV_USE_X11 0 +#if LV_USE_X11 + #define LV_X11_DIRECT_EXIT 1 /*Exit the application when all X11 windows have been closed*/ + #define LV_X11_DOUBLE_BUFFER 1 /*Use double buffers for rendering*/ + /*select only 1 of the following render modes (LV_X11_RENDER_MODE_PARTIAL preferred!)*/ + #define LV_X11_RENDER_MODE_PARTIAL 1 /*Partial render mode (preferred)*/ + #define LV_X11_RENDER_MODE_DIRECT 0 /*direct render mode*/ + #define LV_X11_RENDER_MODE_FULL 0 /*Full render mode*/ +#endif + +/*Use Wayland to open a window and handle input on Linux or BSD desktops */ +#define LV_USE_WAYLAND 0 +#if LV_USE_WAYLAND + #define LV_WAYLAND_WINDOW_DECORATIONS 0 /*Draw client side window decorations only necessary on Mutter/GNOME*/ + #define LV_WAYLAND_WL_SHELL 0 /*Use the legacy wl_shell protocol instead of the default XDG shell*/ +#endif + +/*Driver for /dev/fb*/ +#define LV_USE_LINUX_FBDEV 0 +#if LV_USE_LINUX_FBDEV + #define LV_LINUX_FBDEV_BSD 0 + #define LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL + #define LV_LINUX_FBDEV_BUFFER_COUNT 0 + #define LV_LINUX_FBDEV_BUFFER_SIZE 60 +#endif + +/*Use Nuttx to open window and handle touchscreen*/ +#define LV_USE_NUTTX 0 + +#if LV_USE_NUTTX + #define LV_USE_NUTTX_LIBUV 0 + + /*Use Nuttx custom init API to open window and handle touchscreen*/ + #define LV_USE_NUTTX_CUSTOM_INIT 0 + + /*Driver for /dev/lcd*/ + #define LV_USE_NUTTX_LCD 0 + #if LV_USE_NUTTX_LCD + #define LV_NUTTX_LCD_BUFFER_COUNT 0 + #define LV_NUTTX_LCD_BUFFER_SIZE 60 + #endif + + /*Driver for /dev/input*/ + #define LV_USE_NUTTX_TOUCHSCREEN 0 + +#endif + +/*Driver for /dev/dri/card*/ +#define LV_USE_LINUX_DRM 0 + +/*Interface for TFT_eSPI*/ +#define LV_USE_TFT_ESPI 1 + +/*Driver for evdev input devices*/ +#define LV_USE_EVDEV 0 + +/*Driver for libinput input devices*/ +#define LV_USE_LIBINPUT 0 + +#if LV_USE_LIBINPUT + #define LV_LIBINPUT_BSD 0 + + /*Full keyboard support*/ + #define LV_LIBINPUT_XKB 0 + #if LV_LIBINPUT_XKB + /*"setxkbmap -query" can help find the right values for your keyboard*/ + #define LV_LIBINPUT_XKB_KEY_MAP { .rules = NULL, .model = "pc101", .layout = "us", .variant = NULL, .options = NULL } + #endif +#endif + +/*Drivers for LCD devices connected via SPI/parallel port*/ +#define LV_USE_ST7735 0 +#define LV_USE_ST7789 0 +#define LV_USE_ST7796 0 +#define LV_USE_ILI9341 0 + +#define LV_USE_GENERIC_MIPI (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341) + +/*Driver for Renesas GLCD*/ +#define LV_USE_RENESAS_GLCDC 0 + +/* LVGL Windows backend */ +#define LV_USE_WINDOWS 0 + +/* Use OpenGL to open window on PC and handle mouse and keyboard */ +#define LV_USE_OPENGLES 0 +#if LV_USE_OPENGLES + #define LV_USE_OPENGLES_DEBUG 1 /* Enable or disable debug for opengles */ +#endif + +/* QNX Screen display and input drivers */ +#define LV_USE_QNX 0 +#if LV_USE_QNX + #define LV_QNX_BUF_COUNT 1 /*1 or 2*/ +#endif + +/*================== +* EXAMPLES +*==================*/ + +/*Enable the examples to be built with the library*/ +#define LV_BUILD_EXAMPLES 1 + +/*=================== + * DEMO USAGE + ====================*/ + +/*Show some widget. It might be required to increase `LV_MEM_SIZE` */ +#define LV_USE_DEMO_WIDGETS 0 + +/*Demonstrate the usage of encoder and keyboard*/ +#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 + +/*Benchmark your system*/ +#define LV_USE_DEMO_BENCHMARK 0 + +/*Render test for each primitives. Requires at least 480x272 display*/ +#define LV_USE_DEMO_RENDER 0 + +/*Stress test for LVGL*/ +#define LV_USE_DEMO_STRESS 0 + +/*Music player demo*/ +#define LV_USE_DEMO_MUSIC 0 +#if LV_USE_DEMO_MUSIC + #define LV_DEMO_MUSIC_SQUARE 0 + #define LV_DEMO_MUSIC_LANDSCAPE 0 + #define LV_DEMO_MUSIC_ROUND 0 + #define LV_DEMO_MUSIC_LARGE 0 + #define LV_DEMO_MUSIC_AUTO_PLAY 0 +#endif + +/*Flex layout demo*/ +#define LV_USE_DEMO_FLEX_LAYOUT 0 + +/*Smart-phone like multi-language demo*/ +#define LV_USE_DEMO_MULTILANG 0 + +/*Widget transformation demo*/ +#define LV_USE_DEMO_TRANSFORM 0 + +/*Demonstrate scroll settings*/ +#define LV_USE_DEMO_SCROLL 0 + +/*Vector graphic demo*/ +#define LV_USE_DEMO_VECTOR_GRAPHIC 0 + +/*--END OF LV_CONF_H--*/ + +#endif /*LV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/include/pins.h b/include/pins.h new file mode 100644 index 0000000..b304879 --- /dev/null +++ b/include/pins.h @@ -0,0 +1,42 @@ +#pragma once + +// 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 +/* 3.5C capacitive GT911 (ESP32-3248S035C). GPIO33 is I2C SDA, not TOUCH_CS. + * Backlight is GPIO27; INT is GPIO21 — they are NOT the same pin. */ +#define CYD_GT911_SDA 33 +#define CYD_GT911_SCL 32 +#define CYD_GT911_INT 21 +#define CYD_GT911_RST 25 +#else +#define TFT_BL_PIN 21 +#define SCREEN_WIDTH 240 +#define SCREEN_HEIGHT 320 +#define UI_HOR_RES 320 +#define UI_VER_RES 240 +/* 2.8" CYD: dedicated SoftSPI touch bus. */ +#define CYD_TOUCH_IRQ 36 +#define CYD_TOUCH_MOSI 32 +#define CYD_TOUCH_MISO 39 +#define CYD_TOUCH_CLK 25 +#define CYD_TOUCH_CS 33 +#endif + +/* 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) + +/* Partial frame buffer: 3.5 needs smaller ratio to fit DRAM. */ +#if defined(CYD_BOARD_3248S035) +#define DRAW_BUF_DIV 20 +#else +#define DRAW_BUF_DIV 10 +#endif diff --git a/include/touch.h b/include/touch.h new file mode 100644 index 0000000..20bcfb0 --- /dev/null +++ b/include/touch.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +void touch_init(); +void touch_read_cb(lv_indev_t *indev, lv_indev_data_t *data); diff --git a/include/ui.h b/include/ui.h new file mode 100644 index 0000000..d9de7c9 --- /dev/null +++ b/include/ui.h @@ -0,0 +1,4 @@ +#pragma once + +void ui_create(); +void ui_tick(); diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..9411e48 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,109 @@ +; CYD — host PC / server info panel (USB serial feed + LVGL) +[env:cyd] +platform = espressif32 +board = esp32dev +framework = arduino +monitor_speed = 460800 +upload_speed = 460800 +board_build.partitions = default.csv + +lib_deps = + bodmer/TFT_eSPI@^2.5.43 + lvgl/lvgl@9.2.2 + +build_flags = + -DCORE_DEBUG_LEVEL=0 + -DUSER_SETUP_LOADED=1 + -DILI9341_2_DRIVER=1 + -DTFT_WIDTH=240 + -DTFT_HEIGHT=320 + -DTFT_MISO=12 + -DTFT_MOSI=13 + -DTFT_SCLK=14 + -DTFT_CS=15 + -DTFT_DC=2 + -DTFT_RST=-1 + -DTFT_BL=21 + -DTFT_BACKLIGHT_ON=HIGH + -DTOUCH_CS=-1 + -DLOAD_GLCD=1 + -DLOAD_FONT2=1 + -DLOAD_FONT4=1 + -DLOAD_FONT6=1 + -DLOAD_FONT7=1 + -DLOAD_FONT8=1 + -DLOAD_GFXFF=1 + -DSMOOTH_FONT=1 + -DSPI_FREQUENCY=40000000 + -DSPI_READ_FREQUENCY=20000000 + -DLV_CONF_SKIP + -DLV_COLOR_DEPTH=16 + -DLV_MEM_SIZE=49152 + -DLV_FONT_MONTSERRAT_14=1 + -DLV_FONT_MONTSERRAT_16=1 + -DLV_FONT_MONTSERRAT_18=1 + -DLV_FONT_MONTSERRAT_20=1 + -DLV_USE_TFT_ESPI=1 + -DLV_USE_FLEX=1 + -DLV_USE_LABEL=1 + -DLV_USE_BAR=1 + -I${PROJECT_DIR}/include + +extra_scripts = pre:copy_lv_conf.py + +; Optional 3.5" CYD ESP32-3248S035C (ST7796 + GT911 capacitive). Default [env:cyd]. +; Build: pio run -e cyd_35r +; Keep TOUCH_CS=-1 — GPIO33 is GT911 SDA, not resistive touch CS. +; TFT_BL=GPIO27 (not 21 — 21 is GT911 INT). See ../CYD_PROJECT_GUIDE.md +[env:cyd_35r] +platform = espressif32 +board = esp32dev +framework = arduino +monitor_speed = 460800 +upload_speed = 460800 +board_build.partitions = default.csv +lib_deps = + bodmer/TFT_eSPI@^2.5.43 + lvgl/lvgl@9.2.2 +extra_scripts = pre:copy_lv_conf.py +build_flags = + -DCORE_DEBUG_LEVEL=0 + -DUSER_SETUP_LOADED=1 + -DST7796_DRIVER=1 + -DTFT_WIDTH=320 + -DTFT_HEIGHT=480 + -DTFT_MISO=12 + -DTFT_MOSI=13 + -DTFT_SCLK=14 + -DTFT_CS=15 + -DTFT_DC=2 + -DTFT_RST=-1 + -DTFT_BL=27 + -DTFT_BACKLIGHT_ON=HIGH + -DTOUCH_CS=-1 + -DLOAD_GLCD=1 + -DLOAD_FONT2=1 + -DLOAD_FONT4=1 + -DLOAD_FONT6=1 + -DLOAD_FONT7=1 + -DLOAD_FONT8=1 + -DLOAD_GFXFF=1 + -DSMOOTH_FONT=1 + -DSPI_FREQUENCY=40000000 + -DSPI_READ_FREQUENCY=20000000 + -DUSE_HSPI_PORT + -DSUPPORT_TRANSACTIONS + -DTFT_RGB_ORDER=TFT_BGR + -DCYD_BOARD_3248S035=1 + -DLV_CONF_SKIP + -DLV_COLOR_DEPTH=16 + -DLV_MEM_SIZE=32768 + -DLV_FONT_MONTSERRAT_14=1 + -DLV_FONT_MONTSERRAT_16=1 + -DLV_FONT_MONTSERRAT_18=1 + -DLV_FONT_MONTSERRAT_20=1 + -DLV_USE_TFT_ESPI=1 + -DLV_USE_FLEX=1 + -DLV_USE_LABEL=1 + -DLV_USE_BAR=1 + -I${PROJECT_DIR}/include diff --git a/src/host_info.cpp b/src/host_info.cpp new file mode 100644 index 0000000..36400be --- /dev/null +++ b/src/host_info.cpp @@ -0,0 +1,152 @@ +#include "host_info.h" + +#include +#include +#include + +static HostInfo g_info = {}; +static char g_line[384]; +static size_t g_len = 0; + +static void copy_trunc(char *dst, size_t dst_sz, const char *src) { + if (!dst || dst_sz == 0) { + return; + } + if (!src) { + dst[0] = '\0'; + return; + } + strncpy(dst, src, dst_sz - 1); + dst[dst_sz - 1] = '\0'; +} + +static const char *find_field(const char *line, const char *key) { + // Look for |key=value| or start-of-string after "SI|" + const size_t klen = strlen(key); + const char *p = line; + while (p && *p) { + if (strncmp(p, key, klen) == 0 && p[klen] == '=') { + return p + klen + 1; + } + p = strchr(p, '|'); + if (!p) { + break; + } + ++p; + } + return nullptr; +} + +static void copy_field(char *dst, size_t dst_sz, const char *line, const char *key) { + const char *v = find_field(line, key); + if (!v) { + dst[0] = '\0'; + return; + } + size_t n = 0; + while (v[n] && v[n] != '|' && n + 1 < dst_sz) { + dst[n] = v[n]; + ++n; + } + dst[n] = '\0'; +} + +static float field_float(const char *line, const char *key, float fallback) { + const char *v = find_field(line, key); + if (!v || !*v) { + return fallback; + } + return (float)atof(v); +} + +static void apply_line(const char *line) { + // SI|host=...|cpu=...|cpu%=..|gpu=...|gpu%=..|ram=used/total|disk=used/total + if (strncmp(line, "SI|", 3) != 0) { + return; + } + + HostInfo next = g_info; + next.valid = true; + next.updated_ms = millis(); + + copy_field(next.host, sizeof(next.host), line, "host"); + copy_field(next.cpu, sizeof(next.cpu), line, "cpu"); + copy_field(next.gpu, sizeof(next.gpu), line, "gpu"); + next.cpu_pct = field_float(line, "cpu%", next.cpu_pct); + next.gpu_pct = field_float(line, "gpu%", next.gpu_pct); + + const char *ram = find_field(line, "ram"); + if (ram) { + float u = 0, t = 0; + if (sscanf(ram, "%f/%f", &u, &t) == 2) { + next.ram_used_gb = u; + next.ram_total_gb = t; + } + } + const char *disk = find_field(line, "disk"); + if (disk) { + float u = 0, t = 0; + if (sscanf(disk, "%f/%f", &u, &t) == 2) { + next.disk_used_gb = u; + next.disk_total_gb = t; + } + } + + if (next.cpu_pct < 0) { + next.cpu_pct = 0; + } + if (next.cpu_pct > 100) { + next.cpu_pct = 100; + } + if (next.gpu_pct > 100) { + next.gpu_pct = 100; + } + + g_info = next; +} + +void host_info_begin() { + memset(&g_info, 0, sizeof(g_info)); + g_info.gpu_pct = -1.0f; + g_info.valid = false; + g_len = 0; + g_line[0] = '\0'; +} + +bool host_info_poll() { + bool got = false; + while (Serial.available() > 0) { + const int c = Serial.read(); + if (c < 0) { + break; + } + if (c == '\r') { + continue; + } + if (c == '\n') { + g_line[g_len] = '\0'; + if (g_len > 0) { + apply_line(g_line); + got = g_info.valid; + } + g_len = 0; + continue; + } + if (g_len + 1 < sizeof(g_line)) { + g_line[g_len++] = (char)c; + } else { + // Overflow — drop line + g_len = 0; + } + } + return got; +} + +const HostInfo &host_info_get() { return g_info; } + +bool host_info_is_live(uint32_t stale_ms) { + if (!g_info.valid) { + return false; + } + return (millis() - g_info.updated_ms) <= stale_ms; +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..01bcd0e --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include "host_info.h" +#include "pins.h" +#include "touch.h" +#include "ui.h" + +static constexpr uint32_t DRAW_BUF_SIZE = + (SCREEN_WIDTH * SCREEN_HEIGHT / DRAW_BUF_DIV * (LV_COLOR_DEPTH / 8)); +static uint32_t draw_buf[DRAW_BUF_SIZE / 4]; + +void setup() { + Serial.setRxBufferSize(2048); + Serial.begin(460800); + delay(200); + Serial.println(); + Serial.printf("LVGL %d.%d.%d | CYD Server Info\n", lv_version_major(), + lv_version_minor(), lv_version_patch()); + Serial.println("BOOT_OK"); + Serial.println("PROTO SI|host=..|cpu=..|cpu%=..|gpu=..|gpu%=..|ram=u/t|disk=u/t"); + Serial.printf("heap=%u BL=%d panel=%dx%d ui=%dx%d\n", + (unsigned)ESP.getFreeHeap(), TFT_BL_PIN, SCREEN_WIDTH, + SCREEN_HEIGHT, UI_HOR_RES, UI_VER_RES); + + pinMode(TFT_BL_PIN, OUTPUT); + digitalWrite(TFT_BL_PIN, HIGH); + + touch_init(); + host_info_begin(); + + lv_init(); + lv_display_t *disp = + lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf)); + lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270); + + lv_indev_t *indev = lv_indev_create(); + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(indev, touch_read_cb); + lv_indev_set_display(indev, disp); + + ui_create(); + Serial.printf("ui_ok heap=%u\n", (unsigned)ESP.getFreeHeap()); +} + +void loop() { + host_info_poll(); + lv_timer_handler(); + lv_tick_inc(5); + ui_tick(); + delay(5); +} diff --git a/src/touch.cpp b/src/touch.cpp new file mode 100644 index 0000000..bd64e07 --- /dev/null +++ b/src/touch.cpp @@ -0,0 +1,284 @@ +#include "touch.h" +#include "pins.h" + +#include + +#if defined(CYD_BOARD_3248S035) +#include +#endif + +/** + * 2.8": dedicated SoftSPI + BL/TR diagonal fix. + * 3.5C (3248S035C): capacitive GT911 on I2C SDA33/SCL32/INT21/RST25. + */ + +int16_t g_dbg_phys_x = -1; +int16_t g_dbg_phys_y = -1; +int16_t g_dbg_logic_x = -1; +int16_t g_dbg_logic_y = -1; +bool g_dbg_touch_down = false; + +static int16_t clampi(int32_t v, int32_t lo, int32_t hi) { + if (v < lo) { + return (int16_t)lo; + } + if (v > hi) { + return (int16_t)hi; + } + return (int16_t)v; +} + +#if defined(CYD_BOARD_3248S035) + +/* Minimal GT911 — no config rewrite (TAMC setResolution can hang Wire). */ +static uint8_t s_gt_addr = 0x5D; +static constexpr uint16_t GT_POINT_INFO = 0x814E; +static constexpr uint16_t GT_POINT_1 = 0x814F; + +static bool gt_write8(uint16_t reg, uint8_t val) { + Wire.beginTransmission(s_gt_addr); + Wire.write((uint8_t)(reg >> 8)); + Wire.write((uint8_t)(reg & 0xFF)); + Wire.write(val); + return Wire.endTransmission() == 0; +} + +static bool gt_read(uint16_t reg, uint8_t *buf, uint8_t len) { + Wire.beginTransmission(s_gt_addr); + Wire.write((uint8_t)(reg >> 8)); + Wire.write((uint8_t)(reg & 0xFF)); + if (Wire.endTransmission(false) != 0) { + return false; + } + const uint8_t n = Wire.requestFrom(s_gt_addr, len); + if (n != len) { + return false; + } + for (uint8_t i = 0; i < len; i++) { + buf[i] = Wire.read(); + } + return true; +} + +static bool gt_probe(uint8_t addr) { + Wire.beginTransmission(addr); + return Wire.endTransmission() == 0; +} + +/* TAMC-compatible address select: INT low => 0x5D, INT high => 0x14 */ +static void gt_hw_reset(bool addr_0x14) { + pinMode(CYD_GT911_INT, OUTPUT); + pinMode(CYD_GT911_RST, OUTPUT); + digitalWrite(CYD_GT911_INT, LOW); + digitalWrite(CYD_GT911_RST, LOW); + delay(10); + digitalWrite(CYD_GT911_INT, addr_0x14 ? HIGH : LOW); + delay(1); + digitalWrite(CYD_GT911_RST, HIGH); + delay(5); + digitalWrite(CYD_GT911_INT, LOW); + delay(50); + /* Backlight is GPIO27 — leave INT as input for polling / optional IRQ. */ + pinMode(CYD_GT911_INT, INPUT); + delay(50); +} + +static bool gt_find_addr() { + for (uint8_t a : {uint8_t(0x5D), uint8_t(0x14)}) { + if (gt_probe(a)) { + s_gt_addr = a; + return true; + } + } + return false; +} + +void touch_init() { + Serial.println("Touch: GT911 I2C capacitive (3.5C) minimal"); + Wire.begin(CYD_GT911_SDA, CYD_GT911_SCL); + Wire.setClock(100000); +#if defined(WIRE_HAS_TIMEOUT) || defined(ESP32) + Wire.setTimeOut(50); +#endif + + if (!gt_find_addr()) { + Serial.println("GT911 not found — hw reset 0x5D"); + gt_hw_reset(false); + if (!gt_find_addr()) { + Serial.println("GT911 not found — hw reset 0x14"); + gt_hw_reset(true); + gt_find_addr(); + } + } + + Serial.printf("GT911 probe 0x5D=%s 0x14=%s using=0x%02X\n", + gt_probe(0x5D) ? "OK" : "no", gt_probe(0x14) ? "OK" : "no", + s_gt_addr); + Serial.printf("GT911 SDA=%d SCL=%d INT=%d RST=%d\n", CYD_GT911_SDA, + CYD_GT911_SCL, CYD_GT911_INT, CYD_GT911_RST); +} + +void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) { + uint8_t info = 0; + static uint32_t last_log = 0; + const bool do_log = (millis() - last_log > 200); + + if (!gt_read(GT_POINT_INFO, &info, 1)) { + data->state = LV_INDEV_STATE_RELEASED; + g_dbg_touch_down = false; + if (do_log) { + last_log = millis(); + Serial.println("TouchProbe GT911 I2C err"); + } + return; + } + + const uint8_t touches = info & 0x0F; + const bool buffered = (info & 0x80) != 0; + + if (!buffered || touches == 0) { + if (buffered) { + gt_write8(GT_POINT_INFO, 0); + } + data->state = LV_INDEV_STATE_RELEASED; + g_dbg_touch_down = false; + return; + } + + uint8_t pt[7]; + if (!gt_read(GT_POINT_1, pt, 7)) { + gt_write8(GT_POINT_INFO, 0); + data->state = LV_INDEV_STATE_RELEASED; + g_dbg_touch_down = false; + return; + } + gt_write8(GT_POINT_INFO, 0); + + const int16_t raw_x = (int16_t)(pt[1] | (pt[2] << 8)); + const int16_t raw_y = (int16_t)(pt[3] | (pt[4] << 8)); + + /* Native 320x480 → LVGL 270. Mirror long axis so L/R matches UI + * (else preset 3 hits 10). Resulting UI ≈ (H-1-raw_y, raw_x). */ + const int16_t nx = clampi(raw_x, 0, SCREEN_WIDTH - 1); + const int16_t ny = clampi(raw_y, 0, SCREEN_HEIGHT - 1); + const int16_t feed_x = (int16_t)((SCREEN_WIDTH - 1) - nx); + const int16_t feed_y = (int16_t)((SCREEN_HEIGHT - 1) - ny); + const int16_t ui_x = (int16_t)((SCREEN_HEIGHT - 1) - ny); + const int16_t ui_y = nx; + + g_dbg_phys_x = raw_x; + g_dbg_phys_y = raw_y; + g_dbg_logic_x = ui_x; + g_dbg_logic_y = ui_y; + g_dbg_touch_down = true; + + if (do_log) { + last_log = millis(); + Serial.printf("Touch HIT GT911 raw(%d,%d) ~ui(%d,%d)\n", raw_x, raw_y, + ui_x, ui_y); + } + + data->point.x = feed_x; + data->point.y = feed_y; + data->state = LV_INDEV_STATE_PRESSED; +} + +#else + +static void soft_write(uint8_t data) { + for (int i = 7; i >= 0; --i) { + digitalWrite(CYD_TOUCH_MOSI, (data >> i) & 1); + digitalWrite(CYD_TOUCH_CLK, HIGH); + delayMicroseconds(2); + digitalWrite(CYD_TOUCH_CLK, LOW); + delayMicroseconds(2); + } +} + +static uint16_t soft_read12() { + uint16_t v = 0; + digitalWrite(CYD_TOUCH_CLK, HIGH); + delayMicroseconds(2); + digitalWrite(CYD_TOUCH_CLK, LOW); + delayMicroseconds(2); + for (int i = 0; i < 12; ++i) { + digitalWrite(CYD_TOUCH_CLK, HIGH); + delayMicroseconds(2); + v = (uint16_t)((v << 1) | (digitalRead(CYD_TOUCH_MISO) ? 1u : 0u)); + digitalWrite(CYD_TOUCH_CLK, LOW); + delayMicroseconds(2); + } + return v; +} + +static uint16_t xpt_read(uint8_t cmd) { + digitalWrite(CYD_TOUCH_CS, LOW); + soft_write(cmd); + uint16_t v = soft_read12(); + digitalWrite(CYD_TOUCH_CS, HIGH); + return v; +} + +void touch_init() { + pinMode(CYD_TOUCH_CS, OUTPUT); + pinMode(CYD_TOUCH_CLK, OUTPUT); + pinMode(CYD_TOUCH_MOSI, OUTPUT); + pinMode(CYD_TOUCH_MISO, INPUT); + pinMode(CYD_TOUCH_IRQ, INPUT); + digitalWrite(CYD_TOUCH_CS, HIGH); + digitalWrite(CYD_TOUCH_CLK, LOW); + Serial.println("Touch: SoftSPI + diagonal fix (BL/TR)"); +} + +void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) { + uint16_t adc_x = xpt_read(0x90); + uint16_t adc_y = xpt_read(0xD0); + adc_x = (uint16_t)((adc_x + xpt_read(0x90)) / 2); + adc_y = (uint16_t)((adc_y + xpt_read(0xD0)) / 2); + + const bool irq = (digitalRead(CYD_TOUCH_IRQ) == LOW); + const bool in_range = + (adc_x > 80 && adc_x < 4000 && adc_y > 80 && adc_y < 4000); + + if (!in_range || (!irq && (adc_x < 200 || adc_y < 200))) { + data->state = LV_INDEV_STATE_RELEASED; + g_dbg_touch_down = false; + return; + } + + const int16_t p_x = (int16_t)adc_x; + const int16_t p_y = (int16_t)(4095 - adc_y); + + int16_t x = map(p_x, 200, 3700, 1, SCREEN_WIDTH); + int16_t y = map(p_y, 240, 3800, 1, SCREEN_HEIGHT); + x = clampi(x, 0, SCREEN_WIDTH - 1); + y = clampi(y, 0, SCREEN_HEIGHT - 1); + + const int32_t old_ux = y; + const int32_t old_uy = (SCREEN_WIDTH - 1) - x; + int16_t ui_x = clampi((old_uy * (UI_HOR_RES - 1)) / (UI_VER_RES - 1), 0, + UI_HOR_RES - 1); + int16_t ui_y = clampi((old_ux * (UI_VER_RES - 1)) / (UI_HOR_RES - 1), 0, + UI_VER_RES - 1); + + const int16_t feed_x = (int16_t)((SCREEN_WIDTH - 1) - ui_y); + const int16_t feed_y = ui_x; + + g_dbg_phys_x = x; + g_dbg_phys_y = y; + g_dbg_logic_x = ui_x; + g_dbg_logic_y = ui_y; + g_dbg_touch_down = true; + + static uint32_t last_log = 0; + if (millis() - last_log > 80) { + last_log = millis(); + Serial.printf("Touch UI(%d,%d)\n", ui_x, ui_y); + } + + data->point.x = feed_x; + data->point.y = feed_y; + data->state = LV_INDEV_STATE_PRESSED; +} + +#endif diff --git a/src/ui.cpp b/src/ui.cpp new file mode 100644 index 0000000..5a7f7b8 --- /dev/null +++ b/src/ui.cpp @@ -0,0 +1,223 @@ +#include "ui.h" + +#include "host_info.h" +#include "pins.h" + +#include +#include +#include + +static constexpr uint32_t COL_BG = 0x0B121A; +static constexpr uint32_t COL_INK = 0xE8EEF4; +static constexpr uint32_t COL_MUTED = 0x7A8B9C; +static constexpr uint32_t COL_LINE = 0x1A2734; +static constexpr uint32_t COL_OK = 0x3DDC97; +static constexpr uint32_t COL_WARN = 0xE8B84A; +static constexpr uint32_t COL_BAD = 0xE85D4C; +static constexpr uint32_t COL_ACCENT = 0x4FC3F7; +static constexpr uint32_t COL_BAR_BG = 0x162230; + +struct MetricRow { + lv_obj_t *lbl_tag; + lv_obj_t *lbl_name; + lv_obj_t *lbl_pct; + lv_obj_t *bar; +}; + +static lv_obj_t *scr = nullptr; +static lv_obj_t *lbl_title = nullptr; +static lv_obj_t *lbl_host = nullptr; +static lv_obj_t *lbl_status = nullptr; +static MetricRow row_cpu = {}; +static MetricRow row_gpu = {}; +static MetricRow row_ram = {}; +static MetricRow row_disk = {}; +static uint32_t last_paint_ms = 0; + +static int pct_of(float used, float total) { + if (total <= 0.01f) { + return 0; + } + int p = (int)((used * 100.0f) / total + 0.5f); + if (p < 0) { + p = 0; + } + if (p > 100) { + p = 100; + } + return p; +} + +static uint32_t color_for_pct(int pct) { + if (pct >= 85) { + return COL_BAD; + } + if (pct >= 65) { + return COL_WARN; + } + return COL_OK; +} + +static void style_bar(lv_obj_t *bar) { + lv_obj_set_style_bg_color(bar, lv_color_hex(COL_BAR_BG), LV_PART_MAIN); + lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_set_style_radius(bar, 4, LV_PART_MAIN); + lv_obj_set_style_pad_all(bar, 0, LV_PART_MAIN); + lv_obj_set_style_bg_color(bar, lv_color_hex(COL_OK), LV_PART_INDICATOR); + lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, LV_PART_INDICATOR); + lv_obj_set_style_radius(bar, 4, LV_PART_INDICATOR); + lv_bar_set_range(bar, 0, 100); + lv_bar_set_value(bar, 0, LV_ANIM_OFF); +} + +static MetricRow make_row(lv_obj_t *parent, const char *tag, int y) { + MetricRow r = {}; + + r.lbl_tag = lv_label_create(parent); + lv_label_set_text(r.lbl_tag, tag); + lv_obj_set_style_text_color(r.lbl_tag, lv_color_hex(COL_ACCENT), 0); + lv_obj_set_style_text_font(r.lbl_tag, &lv_font_montserrat_14, 0); + lv_obj_set_pos(r.lbl_tag, UX(8), UY(y)); + + r.lbl_name = lv_label_create(parent); + lv_label_set_text(r.lbl_name, "—"); + lv_label_set_long_mode(r.lbl_name, LV_LABEL_LONG_DOT); + lv_obj_set_width(r.lbl_name, UW(190)); + lv_obj_set_style_text_color(r.lbl_name, lv_color_hex(COL_INK), 0); + lv_obj_set_style_text_font(r.lbl_name, &lv_font_montserrat_14, 0); + lv_obj_set_pos(r.lbl_name, UX(52), UY(y)); + + r.lbl_pct = lv_label_create(parent); + lv_label_set_text(r.lbl_pct, "--%"); + lv_obj_set_style_text_color(r.lbl_pct, lv_color_hex(COL_MUTED), 0); + lv_obj_set_style_text_font(r.lbl_pct, &lv_font_montserrat_14, 0); + lv_obj_align(r.lbl_pct, LV_ALIGN_TOP_RIGHT, UX(-8), UY(y)); + + r.bar = lv_bar_create(parent); + lv_obj_set_size(r.bar, UI_HOR_RES - UW(16), UH(10)); + lv_obj_set_pos(r.bar, UX(8), UY(y + 18)); + style_bar(r.bar); + + return r; +} + +static void set_row_pct(MetricRow &r, int pct, const char *name, const char *right) { + if (r.lbl_name && name) { + lv_label_set_text(r.lbl_name, name); + } + if (r.lbl_pct && right) { + lv_label_set_text(r.lbl_pct, right); + lv_obj_set_style_text_color(r.lbl_pct, lv_color_hex(color_for_pct(pct)), 0); + } + if (r.bar) { + lv_bar_set_value(r.bar, pct, LV_ANIM_OFF); + lv_obj_set_style_bg_color(r.bar, lv_color_hex(color_for_pct(pct)), + LV_PART_INDICATOR); + } +} + +void ui_create() { + scr = lv_obj_create(nullptr); + lv_obj_set_style_bg_color(scr, lv_color_hex(COL_BG), 0); + lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0); + lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE); + lv_screen_load(scr); + + lbl_title = lv_label_create(scr); + lv_label_set_text(lbl_title, "PC Monitor"); + lv_obj_set_style_text_color(lbl_title, lv_color_hex(COL_INK), 0); + lv_obj_set_style_text_font(lbl_title, &lv_font_montserrat_18, 0); + lv_obj_set_pos(lbl_title, UX(8), UY(6)); + + lbl_status = lv_label_create(scr); + lv_label_set_text(lbl_status, "waiting"); + lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_WARN), 0); + lv_obj_set_style_text_font(lbl_status, &lv_font_montserrat_14, 0); + lv_obj_align(lbl_status, LV_ALIGN_TOP_RIGHT, UX(-8), UY(8)); + + lbl_host = lv_label_create(scr); + lv_label_set_text(lbl_host, "Connect USB + run host script"); + lv_label_set_long_mode(lbl_host, LV_LABEL_LONG_DOT); + lv_obj_set_width(lbl_host, UI_HOR_RES - UW(16)); + lv_obj_set_style_text_color(lbl_host, lv_color_hex(COL_MUTED), 0); + lv_obj_set_style_text_font(lbl_host, &lv_font_montserrat_14, 0); + lv_obj_set_pos(lbl_host, UX(8), UY(28)); + + lv_obj_t *line = lv_obj_create(scr); + lv_obj_set_size(line, UI_HOR_RES - UW(16), 1); + lv_obj_set_pos(line, UX(8), UY(48)); + lv_obj_set_style_bg_color(line, lv_color_hex(COL_LINE), 0); + lv_obj_set_style_bg_opa(line, LV_OPA_COVER, 0); + lv_obj_set_style_border_width(line, 0, 0); + lv_obj_set_style_pad_all(line, 0, 0); + lv_obj_clear_flag(line, LV_OBJ_FLAG_SCROLLABLE); + + row_cpu = make_row(scr, "CPU", 56); + row_gpu = make_row(scr, "GPU", 96); + row_ram = make_row(scr, "RAM", 136); + row_disk = make_row(scr, "DISK", 176); + + last_paint_ms = 0; +} + +static void paint() { + const HostInfo &h = host_info_get(); + const bool live = host_info_is_live(5000); + + if (lbl_status) { + if (!h.valid) { + lv_label_set_text(lbl_status, "waiting"); + lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_WARN), 0); + } else if (live) { + lv_label_set_text(lbl_status, "live"); + lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_OK), 0); + } else { + lv_label_set_text(lbl_status, "stale"); + lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_BAD), 0); + } + } + + if (!h.valid) { + return; + } + + if (lbl_host) { + lv_label_set_text(lbl_host, h.host[0] ? h.host : "(unknown host)"); + } + + char buf[48]; + + int cpu_pct = (int)(h.cpu_pct + 0.5f); + snprintf(buf, sizeof(buf), "%d%%", cpu_pct); + set_row_pct(row_cpu, cpu_pct, h.cpu[0] ? h.cpu : "CPU", buf); + + if (h.gpu_pct < 0) { + set_row_pct(row_gpu, 0, h.gpu[0] ? h.gpu : "GPU", "n/a"); + if (row_gpu.lbl_pct) { + lv_obj_set_style_text_color(row_gpu.lbl_pct, lv_color_hex(COL_MUTED), 0); + } + } else { + int gpu_pct = (int)(h.gpu_pct + 0.5f); + snprintf(buf, sizeof(buf), "%d%%", gpu_pct); + set_row_pct(row_gpu, gpu_pct, h.gpu[0] ? h.gpu : "GPU", buf); + } + + int ram_pct = pct_of(h.ram_used_gb, h.ram_total_gb); + snprintf(buf, sizeof(buf), "%.1f/%.0fG", (double)h.ram_used_gb, + (double)h.ram_total_gb); + set_row_pct(row_ram, ram_pct, "Memory", buf); + + int disk_pct = pct_of(h.disk_used_gb, h.disk_total_gb); + snprintf(buf, sizeof(buf), "%.0f/%.0fG", (double)h.disk_used_gb, + (double)h.disk_total_gb); + set_row_pct(row_disk, disk_pct, "System drive", buf); +} + +void ui_tick() { + const uint32_t now = millis(); + if (now - last_paint_ms < 200) { + return; + } + last_paint_ms = now; + paint(); +}