Strip TF and audio; keep touch-only screen test UI.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
70
CHANGELOG.md
70
CHANGELOG.md
@@ -1,70 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-10-17 — Sunton official touch + log offset
|
||||
## 2026-10-17 — Touch-only firmware
|
||||
|
||||
- Use esp32-2432S028R.json touch flags: X=0xD1 Y=0x91, linear/4096, MIRROR_X.
|
||||
- Keep landscape 240x320+ROTATION_270.
|
||||
- Serial/user: red cursor lower-left of finger → UI corr (+45,-45), inverse-feed LVGL.
|
||||
|
||||
## 2026-10-17 — Restore landscape + RNT touch map
|
||||
|
||||
- Revert create(320x240) 花屏; RNT map superseded by Sunton official above.
|
||||
|
||||
## 2026-10-17 — Touch = draw coords (CYD LVGL9 official) [reverted]
|
||||
|
||||
- create(320x240) without LVGL rot caused 花屏 on this panel; superseded.
|
||||
|
||||
## 2026-10-17 — SoftSPI touch + LVGL inverse (fix mux)
|
||||
|
||||
|
||||
- VSPI remux broke SD mount and froze touch at raw(0,4095).
|
||||
- Touch back to SoftSPI on 25/32/39/33; SD keeps exclusive VSPI 18/19/23/5.
|
||||
- Same temperature UI map, then invert LVGL ROTATION_270 for button hits.
|
||||
|
||||
## 2026-10-17 — Touch = temperature driver + LVGL inverse
|
||||
|
||||
- Attempted XPT2046 HW SPI + VSPI pin mux; superseded (broke SD/touch).
|
||||
|
||||
## 2026-10-17 — Touch calib from COM3 logs
|
||||
|
||||
- Logged live `Touch phys/logic` on COM3; inverted physical X so bottom matches UI.
|
||||
- On-screen `Touch L:x,y` debug; larger 150×60 buttons.
|
||||
- Verified Speaker ON/OFF via serial during capture.
|
||||
|
||||
## 2026-10-17 — Fix touch Y (buttons dead zone)
|
||||
|
||||
- Root cause: feeding already-landscape 320x240 points while LVGL also applies
|
||||
ROTATION_270 (double transform) → only upper ~1/3 hit-tested correctly.
|
||||
- Fix: report physical 240x320 coords; let LVGL rotate (matches `lv_indev.c`).
|
||||
|
||||
## 2026-10-17 — Restore LVGL touch
|
||||
|
||||
- SoftSPI + LVGL `lv_indev` (with `lv_indev_set_display`).
|
||||
- Larger clickable speaker/play buttons.
|
||||
|
||||
## 2026-10-17 — Fix touch offset
|
||||
|
||||
- SoftSPI touch now uses the same rotation=2 transform + map as `temperature`.
|
||||
- (Pressure-only path was too strict; superseded by restore above.)
|
||||
|
||||
## 2026-10-17 — Prebuilt firmware folder
|
||||
|
||||
- Add `firmware/cyd/` with bin + `flash.ps1` / `flash.bat` (same pattern as temperature).
|
||||
- Add `firmware/export_from_build.ps1` to refresh archived binaries after builds.
|
||||
- Track `firmware/**/*.bin` in git; document flash-without-compile in README.
|
||||
|
||||
## 2026-10-17 — Touch speaker control + music
|
||||
|
||||
- SoftSPI XPT2046 touch (GPIO 32/39/25/33/36), coexists with SD.
|
||||
- LVGL buttons: **Speaker ON/OFF**, **Play Music / Stop**.
|
||||
- Non-blocking built-in melody (Twinkle Twinkle); no auto beeps.
|
||||
- Docs updated.
|
||||
|
||||
## 2026-10-17 — Speaker audio test
|
||||
|
||||
- Add GPIO26 speaker path (onboard SC8002B amp / P4 connector).
|
||||
- (Superseded by touch control above.)
|
||||
|
||||
## 2026-10-17 — Initial
|
||||
|
||||
- CYD TF/microSD mount (VSPI) + LVGL storage info UI.
|
||||
- Removed TF/SD and speaker/audio code entirely.
|
||||
- UI: TL/TR/BL/BR/CENTER targets + red cursor for alignment.
|
||||
|
||||
67
README.md
67
README.md
@@ -1,68 +1,15 @@
|
||||
# CYD TF 卡信息 + 触屏扬声器(LVGL)
|
||||
# CYD 触控测试(仅屏幕)
|
||||
|
||||
在 **ESP32-2432S028(CYD)** 上:
|
||||
ESP32-2432S028R:LVGL 横屏 + SoftSPI 触摸。无 TF、无喇叭。
|
||||
|
||||
- 挂载 TF/microSD,LVGL 显示卡信息
|
||||
- **触屏按钮**开关扬声器、播放内置音乐
|
||||
|
||||
## 硬件
|
||||
|
||||
| 功能 | GPIO |
|
||||
|------|------|
|
||||
| TFT MOSI / MISO / SCLK | 13 / 12 / 14 |
|
||||
| TFT CS / DC / BL | 15 / 2 / 21 |
|
||||
| **SD MOSI / MISO / SCK / CS** | **23 / 19 / 18 / 5** |
|
||||
| **Speaker(SC8002B → P4)** | **26** |
|
||||
| **Touch MOSI / MISO / CLK / CS / IRQ** | **32 / 39 / 25 / 33 / 36** |
|
||||
|
||||
触摸使用 SoftSPI bitbang,不占用 VSPI,可与 SD 同时工作。喇叭接 **SPEAK / P4**。
|
||||
|
||||
## 触屏操作
|
||||
|
||||
| 按钮 | 作用 |
|
||||
|------|------|
|
||||
| **Speaker ON / OFF** | 打开/关闭扬声器(关=静音并停止播放) |
|
||||
| **Play Music / Stop** | 播放内置旋律《小星星》;播放中变为 Stop |
|
||||
|
||||
建议顺序:先点 **Speaker ON**(会有短提示音)→ 再点 **Play Music**。
|
||||
|
||||
## 屏幕信息
|
||||
|
||||
- Status / Type / Format / Capacity(TF)
|
||||
- Audio:当前音频状态
|
||||
- 用量进度条
|
||||
|
||||
## 预编译固件(随时刷,不必编译)
|
||||
|
||||
与 `temperature` 工程相同:归档在 [`firmware/cyd/`](firmware/cyd/)(含 bootloader / partitions / app):
|
||||
|
||||
```powershell
|
||||
cd firmware\cyd
|
||||
.\flash.ps1
|
||||
# 或 .\flash.ps1 -Port COM3
|
||||
```
|
||||
|
||||
说明见 [`firmware/README.md`](firmware/README.md)。源码更新后重新打包:
|
||||
|
||||
```powershell
|
||||
.\firmware\export_from_build.ps1
|
||||
# 已编译过、只复制:
|
||||
.\firmware\export_from_build.ps1 -SkipBuild
|
||||
```
|
||||
|
||||
## 从源码编译烧录
|
||||
## 构建 / 烧录
|
||||
|
||||
```bash
|
||||
pio run -e cyd -t upload
|
||||
pio device monitor
|
||||
pio run -e cyd -t upload --upload-port COM3
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
或用 `firmware/cyd/flash.bat`。
|
||||
|
||||
- TF 需 **FAT/FAT32**。
|
||||
- 触屏校准与 `temperature` 工程一致(`setRotation(2)` + map);仍偏可改 `src/touch.cpp` 的 `map(...)` 边界。
|
||||
- 变更请更新 `CHANGELOG.md`、必要时重新 `export_from_build.ps1`,并提交本地 git。
|
||||
## 界面
|
||||
|
||||
## 版本
|
||||
|
||||
见 [CHANGELOG.md](CHANGELOG.md)。
|
||||
四角 + 中心按钮;红点跟随触点。点中按钮变绿并串口打印 `HIT …`。
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: CYD TF card + touch speaker (cyd)
|
||||
git: 8a29196
|
||||
git: 8705827
|
||||
date: 2026-10-17
|
||||
board: ESP32-2432S028R (ILI9341)
|
||||
features: TF/SD info, SoftSPI touch, GPIO26 speaker
|
||||
@@ -12,5 +12,5 @@ offsets:
|
||||
sha256:
|
||||
bootloader.bin 3D234A7471F67B013686DABD4DEE7C1FA915C9928463616A94BC9297ACF1ABF8
|
||||
boot_app0.bin F94C5D786A7A8FAB06AC5D10E33BF37711A6697636DC037559EA19CC410A17F0
|
||||
firmware.bin 71D6F25E57435D8D3EAE086122BD459C0ECEFA6CBEB78065C653F580EC787545
|
||||
firmware.bin DB8EEBFEF7FF0995BBF7AF3D525CDE2560060CC008C2656094CF28C6DD7FEEAB
|
||||
partitions.bin 148B959CBFF1C38AA8E1D5C0BA9D612C54997B945E56A63F41223EEF650653A1
|
||||
|
||||
Binary file not shown.
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void audio_init();
|
||||
|
||||
// 扬声器总开关(关则静音并停止播放)
|
||||
void audio_set_enabled(bool on);
|
||||
bool audio_is_enabled();
|
||||
|
||||
// 开始/停止内置音乐(非阻塞,在 audio_tick 推进)
|
||||
void audio_play_music();
|
||||
void audio_stop();
|
||||
bool audio_is_playing();
|
||||
|
||||
// 主循环调用
|
||||
void audio_tick();
|
||||
|
||||
const char *audio_last_status();
|
||||
@@ -1,27 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
// CYD ESP32-2432S028R 引脚
|
||||
// CYD ESP32-2432S028R — 仅屏幕 + 触摸
|
||||
|
||||
// TFT (HSPI / TFT_eSPI 已在 platformio.ini 配置)
|
||||
#define TFT_BL_PIN 21
|
||||
|
||||
// TF / microSD (独立 VSPI)
|
||||
#define SD_MISO 19
|
||||
#define SD_MOSI 23
|
||||
#define SD_SCK 18
|
||||
#define SD_CS 5
|
||||
|
||||
// 板载 SC8002B 功放 → P4 喇叭座(DAC2)
|
||||
#define SPEAKER_PIN 26
|
||||
|
||||
// 触摸 XPT2046(SoftSPI 专用脚,VSPI 留给 SD;勿用 TOUCH_CS,会与 TFT_eSPI 冲突)
|
||||
#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
|
||||
|
||||
// 与 Random Nerd Tutorials / temperature 一致:物理竖屏 + LVGL 270 得横屏
|
||||
// 物理竖屏;LVGL ROTATION_270 → 横屏 UI
|
||||
#define SCREEN_WIDTH 240
|
||||
#define SCREEN_HEIGHT 320
|
||||
#define UI_HOR_RES 320
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
struct SdCardInfo {
|
||||
bool mounted;
|
||||
bool present;
|
||||
const char *type; // MMC / SDSC / SDHC / UNKNOWN / NONE
|
||||
const char *filesystem; // FAT12 / FAT16 / FAT32 / exFAT / FAT / —
|
||||
uint64_t card_bytes; // 卡物理容量
|
||||
uint64_t total_bytes; // 分区总容量
|
||||
uint64_t used_bytes;
|
||||
uint64_t free_bytes;
|
||||
uint32_t root_files; // 根目录条目数
|
||||
String error; // 失败原因
|
||||
};
|
||||
|
||||
bool sd_init();
|
||||
void sd_deinit();
|
||||
SdCardInfo sd_read_info();
|
||||
String format_bytes(uint64_t bytes);
|
||||
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "sd_info.h"
|
||||
#include <stdint.h>
|
||||
|
||||
void ui_create();
|
||||
void ui_update(const SdCardInfo &info);
|
||||
void ui_refresh_audio_buttons();
|
||||
void ui_set_touch_debug(int16_t lx, int16_t ly, bool down);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; CYD ESP32-2432S028 — TF 卡信息 + LVGL 显示
|
||||
; CYD ESP32-2432S028 — 屏幕 + 触控测试
|
||||
[env:cyd]
|
||||
platform = espressif32
|
||||
board = esp32dev
|
||||
@@ -35,7 +35,6 @@ build_flags =
|
||||
-DSMOOTH_FONT=1
|
||||
-DSPI_FREQUENCY=40000000
|
||||
-DSPI_READ_FREQUENCY=20000000
|
||||
; lv_conf.h 放在 .pio/libdeps/cyd/lv_conf.h(见 extra_script)
|
||||
-DLV_CONF_SKIP
|
||||
-DLV_COLOR_DEPTH=16
|
||||
-DLV_MEM_SIZE=49152
|
||||
@@ -44,10 +43,8 @@ build_flags =
|
||||
-DLV_FONT_MONTSERRAT_18=1
|
||||
-DLV_FONT_MONTSERRAT_20=1
|
||||
-DLV_USE_TFT_ESPI=1
|
||||
-DLV_USE_FLEX=1
|
||||
-DLV_USE_BUTTON=1
|
||||
-DLV_USE_LABEL=1
|
||||
-DLV_USE_BAR=1
|
||||
-I${PROJECT_DIR}/include
|
||||
|
||||
extra_scripts = pre:copy_lv_conf.py
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
#include "audio_test.h"
|
||||
#include "pins.h"
|
||||
|
||||
static const char *g_status = "Speaker OFF";
|
||||
static bool g_enabled = false;
|
||||
static bool g_playing = false;
|
||||
static size_t g_note_i = 0;
|
||||
static uint32_t g_until_ms = 0;
|
||||
|
||||
enum Phase : uint8_t { kStartNote, kWaitNote, kWaitGap };
|
||||
static Phase g_phase = kStartNote;
|
||||
|
||||
struct Note {
|
||||
uint16_t freq;
|
||||
uint16_t ms;
|
||||
};
|
||||
|
||||
// Twinkle Twinkle Little Star
|
||||
static const Note k_music[] = {
|
||||
{262, 280}, {262, 280}, {392, 280}, {392, 280}, {440, 280}, {440, 280}, {392, 520},
|
||||
{349, 280}, {349, 280}, {330, 280}, {330, 280}, {294, 280}, {294, 280}, {262, 520},
|
||||
{392, 280}, {392, 280}, {349, 280}, {349, 280}, {330, 280}, {330, 280}, {294, 520},
|
||||
{392, 280}, {392, 280}, {349, 280}, {349, 280}, {330, 280}, {330, 280}, {294, 520},
|
||||
{262, 280}, {262, 280}, {392, 280}, {392, 280}, {440, 280}, {440, 280}, {392, 520},
|
||||
{349, 280}, {349, 280}, {330, 280}, {330, 280}, {294, 280}, {294, 280}, {262, 700},
|
||||
};
|
||||
|
||||
static constexpr size_t k_music_len = sizeof(k_music) / sizeof(k_music[0]);
|
||||
|
||||
static void speaker_off_pin() {
|
||||
noTone(SPEAKER_PIN);
|
||||
digitalWrite(SPEAKER_PIN, LOW);
|
||||
}
|
||||
|
||||
void audio_init() {
|
||||
pinMode(SPEAKER_PIN, OUTPUT);
|
||||
speaker_off_pin();
|
||||
g_enabled = false;
|
||||
g_playing = false;
|
||||
g_status = "Speaker OFF";
|
||||
Serial.printf("Speaker init GPIO%d (touch-controlled)\n", SPEAKER_PIN);
|
||||
}
|
||||
|
||||
void audio_set_enabled(bool on) {
|
||||
g_enabled = on;
|
||||
if (!on) {
|
||||
audio_stop();
|
||||
g_status = "Speaker OFF";
|
||||
} else {
|
||||
g_status = "Speaker ON";
|
||||
tone(SPEAKER_PIN, 1000);
|
||||
delay(80);
|
||||
speaker_off_pin();
|
||||
}
|
||||
Serial.printf("Speaker %s\n", on ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
bool audio_is_enabled() {
|
||||
return g_enabled;
|
||||
}
|
||||
|
||||
void audio_stop() {
|
||||
g_playing = false;
|
||||
g_note_i = 0;
|
||||
g_phase = kStartNote;
|
||||
speaker_off_pin();
|
||||
if (g_enabled) {
|
||||
g_status = "Speaker ON";
|
||||
}
|
||||
}
|
||||
|
||||
bool audio_is_playing() {
|
||||
return g_playing;
|
||||
}
|
||||
|
||||
void audio_play_music() {
|
||||
if (!g_enabled) {
|
||||
g_status = "Turn ON first";
|
||||
Serial.println("Audio: blocked (speaker off)");
|
||||
return;
|
||||
}
|
||||
g_playing = true;
|
||||
g_note_i = 0;
|
||||
g_phase = kStartNote;
|
||||
g_until_ms = 0;
|
||||
g_status = "Playing...";
|
||||
Serial.println("Audio: play music");
|
||||
}
|
||||
|
||||
void audio_tick() {
|
||||
if (!g_playing || !g_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t now = millis();
|
||||
if (now < g_until_ms) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_phase == kWaitNote) {
|
||||
speaker_off_pin();
|
||||
g_phase = kWaitGap;
|
||||
g_until_ms = now + 35;
|
||||
g_note_i++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_phase == kWaitGap) {
|
||||
g_phase = kStartNote;
|
||||
}
|
||||
|
||||
// kStartNote
|
||||
if (g_note_i >= k_music_len) {
|
||||
audio_stop();
|
||||
g_status = "Music done";
|
||||
Serial.println("Audio: music done");
|
||||
return;
|
||||
}
|
||||
|
||||
const Note &n = k_music[g_note_i];
|
||||
if (n.freq == 0) {
|
||||
speaker_off_pin();
|
||||
} else {
|
||||
tone(SPEAKER_PIN, n.freq);
|
||||
}
|
||||
g_until_ms = now + n.ms;
|
||||
g_phase = kWaitNote;
|
||||
}
|
||||
|
||||
const char *audio_last_status() {
|
||||
return g_status;
|
||||
}
|
||||
49
src/main.cpp
49
src/main.cpp
@@ -1,10 +1,7 @@
|
||||
#include <Arduino.h>
|
||||
#include <TFT_eSPI.h>
|
||||
#include <lvgl.h>
|
||||
|
||||
#include "audio_test.h"
|
||||
#include "pins.h"
|
||||
#include "sd_info.h"
|
||||
#include "touch.h"
|
||||
#include "ui.h"
|
||||
|
||||
@@ -12,41 +9,21 @@ static constexpr uint32_t DRAW_BUF_SIZE =
|
||||
(SCREEN_WIDTH * SCREEN_HEIGHT / 10 * (LV_COLOR_DEPTH / 8));
|
||||
static uint32_t draw_buf[DRAW_BUF_SIZE / 4];
|
||||
|
||||
static uint32_t last_refresh_ms = 0;
|
||||
static uint32_t last_btn_ui_ms = 0;
|
||||
static constexpr uint32_t REFRESH_MS = 3000;
|
||||
|
||||
static void refresh_sd_ui() {
|
||||
SdCardInfo info = sd_read_info();
|
||||
|
||||
Serial.println("---- TF Card ----");
|
||||
if (!info.mounted) {
|
||||
Serial.printf("Error: %s\n", info.error.c_str());
|
||||
} else {
|
||||
Serial.printf("Type: %s FS: %s Cap: %s\n", info.type, info.filesystem,
|
||||
format_bytes(info.card_bytes).c_str());
|
||||
}
|
||||
|
||||
ui_update(info);
|
||||
}
|
||||
static uint32_t last_dbg_ms = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(200);
|
||||
Serial.println();
|
||||
Serial.printf("LVGL %d.%d.%d | CYD TF + Touch Speaker\n", lv_version_major(),
|
||||
Serial.printf("LVGL %d.%d.%d | touch test only\n", lv_version_major(),
|
||||
lv_version_minor(), lv_version_patch());
|
||||
|
||||
pinMode(TFT_BL_PIN, OUTPUT);
|
||||
digitalWrite(TFT_BL_PIN, HIGH);
|
||||
|
||||
audio_init();
|
||||
touch_init();
|
||||
|
||||
lv_init();
|
||||
|
||||
// Sunton 官方:240x320 + ROTATION_270 → 横屏(勿 create 320x240,会花屏)
|
||||
// 触摸见 touch.cpp(官方 mirror_x + 日志补偿)
|
||||
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);
|
||||
@@ -57,31 +34,15 @@ void setup() {
|
||||
lv_indev_set_display(indev, disp);
|
||||
|
||||
ui_create();
|
||||
|
||||
if (!sd_init()) {
|
||||
Serial.println("SD mount failed at boot");
|
||||
}
|
||||
|
||||
refresh_sd_ui();
|
||||
last_refresh_ms = millis();
|
||||
last_btn_ui_ms = millis();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
lv_timer_handler();
|
||||
lv_tick_inc(5);
|
||||
audio_tick();
|
||||
|
||||
uint32_t now = millis();
|
||||
if (now - last_refresh_ms >= REFRESH_MS) {
|
||||
last_refresh_ms = now;
|
||||
refresh_sd_ui();
|
||||
}
|
||||
|
||||
// 播放过程中刷新按钮文字;同步触点调试
|
||||
if (now - last_btn_ui_ms >= 200) {
|
||||
last_btn_ui_ms = now;
|
||||
ui_refresh_audio_buttons();
|
||||
const uint32_t now = millis();
|
||||
if (now - last_dbg_ms >= 50) {
|
||||
last_dbg_ms = now;
|
||||
ui_set_touch_debug(g_dbg_logic_x, g_dbg_logic_y, g_dbg_touch_down);
|
||||
}
|
||||
|
||||
|
||||
137
src/sd_info.cpp
137
src/sd_info.cpp
@@ -1,137 +0,0 @@
|
||||
#include "sd_info.h"
|
||||
#include "pins.h"
|
||||
|
||||
#include <FS.h>
|
||||
#include <SD.h>
|
||||
#include <SPI.h>
|
||||
|
||||
static SPIClass sd_spi(VSPI);
|
||||
static bool sd_ready = false;
|
||||
|
||||
static const char *card_type_name(uint8_t t) {
|
||||
switch (t) {
|
||||
case CARD_MMC: return "MMC";
|
||||
case CARD_SD: return "SDSC";
|
||||
case CARD_SDHC: return "SDHC";
|
||||
case CARD_NONE: return "NONE";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *detect_filesystem() {
|
||||
File root = SD.open("/");
|
||||
if (!root) {
|
||||
return "—";
|
||||
}
|
||||
root.close();
|
||||
|
||||
uint64_t total = SD.totalBytes();
|
||||
if (total == 0) {
|
||||
return "FAT";
|
||||
}
|
||||
if (total > 32ULL * 1024ULL * 1024ULL) {
|
||||
return "FAT32";
|
||||
}
|
||||
if (total > 2ULL * 1024ULL * 1024ULL) {
|
||||
return "FAT16";
|
||||
}
|
||||
return "FAT12/16";
|
||||
}
|
||||
|
||||
static uint32_t count_root_entries() {
|
||||
File root = SD.open("/");
|
||||
if (!root || !root.isDirectory()) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t n = 0;
|
||||
File f = root.openNextFile();
|
||||
while (f) {
|
||||
n++;
|
||||
f = root.openNextFile();
|
||||
}
|
||||
root.close();
|
||||
return n;
|
||||
}
|
||||
|
||||
bool sd_init() {
|
||||
if (sd_ready) {
|
||||
return true;
|
||||
}
|
||||
|
||||
pinMode(SD_CS, OUTPUT);
|
||||
digitalWrite(SD_CS, HIGH);
|
||||
// 触摸 SoftSPI CS 拉高,避免干扰(脚位不同,但防误选)
|
||||
pinMode(CYD_TOUCH_CS, OUTPUT);
|
||||
digitalWrite(CYD_TOUCH_CS, HIGH);
|
||||
|
||||
sd_spi.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
|
||||
|
||||
if (!SD.begin(SD_CS, sd_spi, 25000000)) {
|
||||
sd_ready = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
sd_ready = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void sd_deinit() {
|
||||
if (sd_ready) {
|
||||
SD.end();
|
||||
sd_ready = false;
|
||||
}
|
||||
}
|
||||
|
||||
SdCardInfo sd_read_info() {
|
||||
SdCardInfo info{};
|
||||
info.mounted = false;
|
||||
info.present = false;
|
||||
info.type = "NONE";
|
||||
info.filesystem = "—";
|
||||
info.card_bytes = 0;
|
||||
info.total_bytes = 0;
|
||||
info.used_bytes = 0;
|
||||
info.free_bytes = 0;
|
||||
info.root_files = 0;
|
||||
|
||||
if (!sd_ready && !sd_init()) {
|
||||
info.error = "Mount failed";
|
||||
return info;
|
||||
}
|
||||
|
||||
uint8_t type = SD.cardType();
|
||||
info.type = card_type_name(type);
|
||||
|
||||
if (type == CARD_NONE) {
|
||||
info.error = "No card";
|
||||
info.present = false;
|
||||
return info;
|
||||
}
|
||||
|
||||
info.present = true;
|
||||
info.mounted = true;
|
||||
info.card_bytes = SD.cardSize();
|
||||
info.total_bytes = SD.totalBytes();
|
||||
info.used_bytes = SD.usedBytes();
|
||||
info.free_bytes =
|
||||
(info.total_bytes > info.used_bytes) ? (info.total_bytes - info.used_bytes) : 0;
|
||||
info.filesystem = detect_filesystem();
|
||||
info.root_files = count_root_entries();
|
||||
info.error = "";
|
||||
return info;
|
||||
}
|
||||
|
||||
String format_bytes(uint64_t bytes) {
|
||||
const double b = static_cast<double>(bytes);
|
||||
char buf[48];
|
||||
if (bytes >= (1ULL << 30)) {
|
||||
snprintf(buf, sizeof(buf), "%.2f GB", b / (1024.0 * 1024.0 * 1024.0));
|
||||
} else if (bytes >= (1ULL << 20)) {
|
||||
snprintf(buf, sizeof(buf), "%.2f MB", b / (1024.0 * 1024.0));
|
||||
} else if (bytes >= (1ULL << 10)) {
|
||||
snprintf(buf, sizeof(buf), "%.2f KB", b / 1024.0);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%llu B", static_cast<unsigned long long>(bytes));
|
||||
}
|
||||
return String(buf);
|
||||
}
|
||||
@@ -4,11 +4,8 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
/**
|
||||
* SoftSPI XPT2046 — Sunton esp32-2432S028R 官方通道 + 线性映射
|
||||
* X CMD=0xD1, Y CMD=0x91, scale /4096 → 240x320
|
||||
* SoftSPI 与官方 HW SPI 极性不同:不做 MIRROR_X(日志证明 mirror 会把 UI Y 压到顶部)
|
||||
* 显示:240x320 + LV_DISPLAY_ROTATION_270;触点直接喂物理坐标,由 LVGL 旋转
|
||||
* 板级:https://github.com/rzeldent/platformio-espressif32-sunton
|
||||
* SoftSPI XPT2046 → 物理 240x320 → 交给 LVGL ROTATION_270
|
||||
* RNT 标定区间 + rot2;红点用变换后 UI 坐标
|
||||
*/
|
||||
|
||||
int16_t g_dbg_phys_x = -1;
|
||||
@@ -59,15 +56,14 @@ void touch_init() {
|
||||
pinMode(CYD_TOUCH_IRQ, INPUT);
|
||||
digitalWrite(CYD_TOUCH_CS, HIGH);
|
||||
digitalWrite(CYD_TOUCH_CLK, LOW);
|
||||
Serial.println("Touch: Sunton 0xD1/0x91 linear, SoftSPI no-mirror");
|
||||
Serial.println("Touch: SoftSPI rot2 map 240x320");
|
||||
}
|
||||
|
||||
void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) {
|
||||
(void)xpt_read(0xD1);
|
||||
uint16_t adc_x = xpt_read(0xD1);
|
||||
uint16_t adc_y = xpt_read(0x91);
|
||||
adc_x = (uint16_t)((adc_x + xpt_read(0xD1)) / 2);
|
||||
adc_y = (uint16_t)((adc_y + xpt_read(0x91)) / 2);
|
||||
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 =
|
||||
@@ -79,40 +75,41 @@ void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) {
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t px = (int16_t)(((uint32_t)adc_x * SCREEN_WIDTH) / 4096);
|
||||
int16_t py = (int16_t)(((uint32_t)adc_y * SCREEN_HEIGHT) / 4096);
|
||||
// setRotation(2)
|
||||
const int16_t p_x = (int16_t)adc_x;
|
||||
const int16_t p_y = (int16_t)(4095 - adc_y);
|
||||
|
||||
if (px < 0) {
|
||||
px = 0;
|
||||
int16_t x = map(p_x, 200, 3700, 1, SCREEN_WIDTH);
|
||||
int16_t y = map(p_y, 240, 3800, 1, SCREEN_HEIGHT);
|
||||
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
}
|
||||
if (py < 0) {
|
||||
py = 0;
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
}
|
||||
if (px >= SCREEN_WIDTH) {
|
||||
px = SCREEN_WIDTH - 1;
|
||||
if (x >= SCREEN_WIDTH) {
|
||||
x = SCREEN_WIDTH - 1;
|
||||
}
|
||||
if (py >= SCREEN_HEIGHT) {
|
||||
py = SCREEN_HEIGHT - 1;
|
||||
if (y >= SCREEN_HEIGHT) {
|
||||
y = SCREEN_HEIGHT - 1;
|
||||
}
|
||||
|
||||
// LVGL 270 后预估 UI(红点);触点仍喂物理坐标
|
||||
const int16_t ui_x = py;
|
||||
const int16_t ui_y = (int16_t)((SCREEN_WIDTH - 1) - px);
|
||||
|
||||
g_dbg_phys_x = px;
|
||||
g_dbg_phys_y = py;
|
||||
g_dbg_logic_x = ui_x;
|
||||
g_dbg_logic_y = ui_y;
|
||||
// LVGL 270 → UI
|
||||
g_dbg_phys_x = x;
|
||||
g_dbg_phys_y = y;
|
||||
g_dbg_logic_x = y;
|
||||
g_dbg_logic_y = (int16_t)((SCREEN_WIDTH - 1) - x);
|
||||
g_dbg_touch_down = true;
|
||||
|
||||
static uint32_t last_log = 0;
|
||||
if (millis() - last_log > 100) {
|
||||
if (millis() - last_log > 80) {
|
||||
last_log = millis();
|
||||
Serial.printf("Touch adc(%u,%u) phys(%d,%d) UI~(%d,%d)\n", adc_x, adc_y, px,
|
||||
py, ui_x, ui_y);
|
||||
Serial.printf("Touch phys(%d,%d) UI(%d,%d)\n", x, y, g_dbg_logic_x,
|
||||
g_dbg_logic_y);
|
||||
}
|
||||
|
||||
data->point.x = px;
|
||||
data->point.y = py;
|
||||
data->point.x = x;
|
||||
data->point.y = y;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
}
|
||||
|
||||
230
src/ui.cpp
230
src/ui.cpp
@@ -1,164 +1,69 @@
|
||||
#include "ui.h"
|
||||
#include "audio_test.h"
|
||||
#include "pins.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <lvgl.h>
|
||||
|
||||
static lv_obj_t *lbl_status;
|
||||
static lv_obj_t *lbl_type;
|
||||
static lv_obj_t *lbl_fs;
|
||||
static lv_obj_t *lbl_capacity;
|
||||
static lv_obj_t *lbl_audio;
|
||||
static lv_obj_t *bar_usage;
|
||||
static lv_obj_t *btn_speaker;
|
||||
static lv_obj_t *btn_play;
|
||||
static lv_obj_t *lbl_speaker;
|
||||
static lv_obj_t *lbl_play;
|
||||
static lv_obj_t *lbl_touch_dbg;
|
||||
static lv_obj_t *lbl_info;
|
||||
static lv_obj_t *lbl_hit;
|
||||
static lv_obj_t *touch_cursor;
|
||||
|
||||
static lv_obj_t *make_row(lv_obj_t *parent, const char *title, lv_obj_t **value_out) {
|
||||
lv_obj_t *row = lv_obj_create(parent);
|
||||
lv_obj_set_width(row, LV_PCT(100));
|
||||
lv_obj_set_height(row, LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_bg_opa(row, LV_OPA_TRANSP, 0);
|
||||
lv_obj_set_style_border_width(row, 0, 0);
|
||||
lv_obj_set_style_pad_all(row, 1, 0);
|
||||
lv_obj_set_flex_flow(row, LV_FLEX_FLOW_ROW);
|
||||
lv_obj_set_flex_align(row, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER,
|
||||
LV_FLEX_ALIGN_CENTER);
|
||||
lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
lv_obj_t *t = lv_label_create(row);
|
||||
lv_label_set_text(t, title);
|
||||
lv_obj_set_style_text_color(t, lv_color_hex(0x90A4AE), 0);
|
||||
lv_obj_set_style_text_font(t, &lv_font_montserrat_14, 0);
|
||||
|
||||
lv_obj_t *v = lv_label_create(row);
|
||||
lv_label_set_text(v, "—");
|
||||
lv_obj_set_style_text_color(v, lv_color_hex(0xECEFF1), 0);
|
||||
lv_obj_set_style_text_font(v, &lv_font_montserrat_14, 0);
|
||||
*value_out = v;
|
||||
return row;
|
||||
static void on_target_clicked(lv_event_t *e) {
|
||||
lv_obj_t *btn = (lv_obj_t *)lv_event_get_target(e);
|
||||
const char *name = (const char *)lv_event_get_user_data(e);
|
||||
lv_obj_set_style_bg_color(btn, lv_color_hex(0x2E7D32), 0);
|
||||
if (lbl_hit && name) {
|
||||
char buf[48];
|
||||
snprintf(buf, sizeof(buf), "HIT: %s", name);
|
||||
lv_label_set_text(lbl_hit, buf);
|
||||
}
|
||||
Serial.printf("HIT %s\n", name ? name : "?");
|
||||
}
|
||||
|
||||
static void on_speaker_clicked(lv_event_t *e) {
|
||||
LV_UNUSED(e);
|
||||
audio_set_enabled(!audio_is_enabled());
|
||||
ui_refresh_audio_buttons();
|
||||
}
|
||||
static lv_obj_t *make_target(lv_obj_t *parent, const char *name, int x, int y,
|
||||
int w, int h) {
|
||||
lv_obj_t *btn = lv_button_create(parent);
|
||||
lv_obj_set_pos(btn, x, y);
|
||||
lv_obj_set_size(btn, w, h);
|
||||
lv_obj_set_style_radius(btn, 6, 0);
|
||||
lv_obj_set_style_bg_color(btn, lv_color_hex(0x1565C0), 0);
|
||||
lv_obj_add_flag(btn, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_add_event_cb(btn, on_target_clicked, LV_EVENT_CLICKED, (void *)name);
|
||||
|
||||
static void on_play_clicked(lv_event_t *e) {
|
||||
LV_UNUSED(e);
|
||||
if (audio_is_playing()) {
|
||||
audio_stop();
|
||||
} else {
|
||||
audio_play_music();
|
||||
}
|
||||
ui_refresh_audio_buttons();
|
||||
}
|
||||
|
||||
void ui_refresh_audio_buttons() {
|
||||
if (lbl_speaker) {
|
||||
lv_label_set_text(lbl_speaker, audio_is_enabled() ? "Speaker ON" : "Speaker OFF");
|
||||
}
|
||||
if (btn_speaker) {
|
||||
lv_obj_set_style_bg_color(
|
||||
btn_speaker,
|
||||
audio_is_enabled() ? lv_color_hex(0x2E7D32) : lv_color_hex(0x455A64), 0);
|
||||
}
|
||||
|
||||
if (lbl_play) {
|
||||
lv_label_set_text(lbl_play, audio_is_playing() ? "Stop" : "Play Music");
|
||||
}
|
||||
if (btn_play) {
|
||||
lv_obj_set_style_bg_color(
|
||||
btn_play,
|
||||
audio_is_playing() ? lv_color_hex(0xC62828) : lv_color_hex(0x1565C0), 0);
|
||||
}
|
||||
|
||||
if (lbl_audio) {
|
||||
lv_label_set_text(lbl_audio, audio_last_status());
|
||||
}
|
||||
lv_obj_t *lbl = lv_label_create(btn);
|
||||
lv_label_set_text(lbl, name);
|
||||
lv_obj_set_style_text_font(lbl, &lv_font_montserrat_14, 0);
|
||||
lv_obj_center(lbl);
|
||||
return btn;
|
||||
}
|
||||
|
||||
void ui_create() {
|
||||
lv_obj_t *scr = lv_screen_active();
|
||||
lv_obj_set_style_bg_color(scr, lv_color_hex(0x0D1B2A), 0);
|
||||
lv_obj_set_style_bg_color(scr, lv_color_hex(0x102027), 0);
|
||||
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
|
||||
lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
lv_obj_t *col = lv_obj_create(scr);
|
||||
lv_obj_set_size(col, LV_PCT(100), LV_PCT(100));
|
||||
lv_obj_set_style_bg_opa(col, LV_OPA_TRANSP, 0);
|
||||
lv_obj_set_style_border_width(col, 0, 0);
|
||||
lv_obj_set_style_pad_left(col, 10, 0);
|
||||
lv_obj_set_style_pad_right(col, 10, 0);
|
||||
lv_obj_set_style_pad_top(col, 6, 0);
|
||||
lv_obj_set_style_pad_bottom(col, 6, 0);
|
||||
lv_obj_set_flex_flow(col, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(col, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER,
|
||||
LV_FLEX_ALIGN_CENTER);
|
||||
lv_obj_set_style_pad_row(col, 2, 0);
|
||||
lv_obj_clear_flag(col, LV_OBJ_FLAG_SCROLLABLE);
|
||||
// 四角 + 中心,专门验触控与绘制是否重合
|
||||
make_target(scr, "TL", 8, 8, 90, 56);
|
||||
make_target(scr, "TR", UI_HOR_RES - 98, 8, 90, 56);
|
||||
make_target(scr, "BL", 8, UI_VER_RES - 64, 90, 56);
|
||||
make_target(scr, "BR", UI_HOR_RES - 98, UI_VER_RES - 64, 90, 56);
|
||||
make_target(scr, "CENTER", UI_HOR_RES / 2 - 60, UI_VER_RES / 2 - 28, 120, 56);
|
||||
|
||||
lv_obj_t *title = lv_label_create(col);
|
||||
lv_label_set_text(title, "TF + Speaker");
|
||||
lv_obj_set_style_text_font(title, &lv_font_montserrat_18, 0);
|
||||
lv_obj_set_style_text_color(title, lv_color_hex(0x4FC3F7), 0);
|
||||
lbl_info = lv_label_create(scr);
|
||||
lv_label_set_text(lbl_info, "Touch: --");
|
||||
lv_obj_set_style_text_color(lbl_info, lv_color_hex(0xFFB74D), 0);
|
||||
lv_obj_set_style_text_font(lbl_info, &lv_font_montserrat_16, 0);
|
||||
lv_obj_align(lbl_info, LV_ALIGN_TOP_MID, 0, 72);
|
||||
|
||||
make_row(col, "Status", &lbl_status);
|
||||
make_row(col, "Type", &lbl_type);
|
||||
make_row(col, "Format", &lbl_fs);
|
||||
make_row(col, "Capacity", &lbl_capacity);
|
||||
make_row(col, "Audio", &lbl_audio);
|
||||
lbl_hit = lv_label_create(scr);
|
||||
lv_label_set_text(lbl_hit, "Tap a button");
|
||||
lv_obj_set_style_text_color(lbl_hit, lv_color_hex(0xECEFF1), 0);
|
||||
lv_obj_set_style_text_font(lbl_hit, &lv_font_montserrat_16, 0);
|
||||
lv_obj_align(lbl_hit, LV_ALIGN_BOTTOM_MID, 0, -72);
|
||||
|
||||
bar_usage = lv_bar_create(col);
|
||||
lv_obj_set_width(bar_usage, LV_PCT(100));
|
||||
lv_obj_set_height(bar_usage, 10);
|
||||
lv_bar_set_range(bar_usage, 0, 100);
|
||||
lv_bar_set_value(bar_usage, 0, LV_ANIM_OFF);
|
||||
lv_obj_set_style_bg_color(bar_usage, lv_color_hex(0x263238), 0);
|
||||
lv_obj_set_style_bg_color(bar_usage, lv_color_hex(0x29B6F6), LV_PART_INDICATOR);
|
||||
|
||||
// 底部两个大按钮(加大命中区)
|
||||
lv_obj_t *row_btns = lv_obj_create(col);
|
||||
lv_obj_set_width(row_btns, LV_PCT(100));
|
||||
lv_obj_set_height(row_btns, 64);
|
||||
lv_obj_set_style_bg_opa(row_btns, LV_OPA_TRANSP, 0);
|
||||
lv_obj_set_style_border_width(row_btns, 0, 0);
|
||||
lv_obj_set_style_pad_all(row_btns, 0, 0);
|
||||
lv_obj_set_flex_flow(row_btns, LV_FLEX_FLOW_ROW);
|
||||
lv_obj_set_flex_align(row_btns, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER,
|
||||
LV_FLEX_ALIGN_CENTER);
|
||||
lv_obj_clear_flag(row_btns, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
btn_speaker = lv_button_create(row_btns);
|
||||
lv_obj_set_size(btn_speaker, 150, 60);
|
||||
lv_obj_set_style_radius(btn_speaker, 8, 0);
|
||||
lv_obj_add_flag(btn_speaker, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_add_event_cb(btn_speaker, on_speaker_clicked, LV_EVENT_CLICKED, nullptr);
|
||||
lbl_speaker = lv_label_create(btn_speaker);
|
||||
lv_label_set_text(lbl_speaker, "Speaker OFF");
|
||||
lv_obj_set_style_text_font(lbl_speaker, &lv_font_montserrat_14, 0);
|
||||
lv_obj_center(lbl_speaker);
|
||||
|
||||
btn_play = lv_button_create(row_btns);
|
||||
lv_obj_set_size(btn_play, 150, 60);
|
||||
lv_obj_set_style_radius(btn_play, 8, 0);
|
||||
lv_obj_add_flag(btn_play, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_add_event_cb(btn_play, on_play_clicked, LV_EVENT_CLICKED, nullptr);
|
||||
lbl_play = lv_label_create(btn_play);
|
||||
lv_label_set_text(lbl_play, "Play Music");
|
||||
lv_obj_set_style_text_font(lbl_play, &lv_font_montserrat_14, 0);
|
||||
lv_obj_center(lbl_play);
|
||||
|
||||
lbl_touch_dbg = lv_label_create(col);
|
||||
lv_label_set_text(lbl_touch_dbg, "Touch: --");
|
||||
lv_obj_set_style_text_font(lbl_touch_dbg, &lv_font_montserrat_14, 0);
|
||||
lv_obj_set_style_text_color(lbl_touch_dbg, lv_color_hex(0xFFB74D), 0);
|
||||
|
||||
// 触点光标:落在哪画在哪,验证与按钮重合
|
||||
touch_cursor = lv_obj_create(scr);
|
||||
lv_obj_set_size(touch_cursor, 16, 16);
|
||||
lv_obj_set_size(touch_cursor, 18, 18);
|
||||
lv_obj_set_style_radius(touch_cursor, LV_RADIUS_CIRCLE, 0);
|
||||
lv_obj_set_style_bg_color(touch_cursor, lv_color_hex(0xFF5252), 0);
|
||||
lv_obj_set_style_bg_opa(touch_cursor, LV_OPA_COVER, 0);
|
||||
@@ -167,56 +72,25 @@ void ui_create() {
|
||||
lv_obj_add_flag(touch_cursor, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_clear_flag(touch_cursor, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_clear_flag(touch_cursor, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
ui_refresh_audio_buttons();
|
||||
}
|
||||
|
||||
void ui_set_touch_debug(int16_t lx, int16_t ly, bool down) {
|
||||
if (!lbl_touch_dbg) {
|
||||
if (!lbl_info) {
|
||||
return;
|
||||
}
|
||||
if (!down) {
|
||||
lv_label_set_text(lbl_touch_dbg, "Touch: up");
|
||||
lv_label_set_text(lbl_info, "Touch: up");
|
||||
if (touch_cursor) {
|
||||
lv_obj_add_flag(touch_cursor, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
return;
|
||||
}
|
||||
char buf[40];
|
||||
snprintf(buf, sizeof(buf), "Touch L:%d,%d", (int)lx, (int)ly);
|
||||
lv_label_set_text(lbl_touch_dbg, buf);
|
||||
snprintf(buf, sizeof(buf), "Touch %d,%d", (int)lx, (int)ly);
|
||||
lv_label_set_text(lbl_info, buf);
|
||||
if (touch_cursor) {
|
||||
lv_obj_clear_flag(touch_cursor, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_set_pos(touch_cursor, lx - 8, ly - 8);
|
||||
lv_obj_set_pos(touch_cursor, lx - 9, ly - 9);
|
||||
lv_obj_move_foreground(touch_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_update(const SdCardInfo &info) {
|
||||
if (!info.mounted || !info.present) {
|
||||
lv_label_set_text(lbl_status, info.error.length() ? info.error.c_str() : "Fail");
|
||||
lv_obj_set_style_text_color(lbl_status, lv_color_hex(0xEF5350), 0);
|
||||
lv_label_set_text(lbl_type, info.type);
|
||||
lv_label_set_text(lbl_fs, "—");
|
||||
lv_label_set_text(lbl_capacity, "—");
|
||||
lv_bar_set_value(bar_usage, 0, LV_ANIM_OFF);
|
||||
} else {
|
||||
lv_label_set_text(lbl_status, "Mounted OK");
|
||||
lv_obj_set_style_text_color(lbl_status, lv_color_hex(0x66BB6A), 0);
|
||||
lv_label_set_text(lbl_type, info.type);
|
||||
lv_label_set_text(lbl_fs, info.filesystem);
|
||||
String cap = format_bytes(info.card_bytes);
|
||||
lv_label_set_text(lbl_capacity, cap.c_str());
|
||||
|
||||
int pct = 0;
|
||||
if (info.total_bytes > 0) {
|
||||
pct = static_cast<int>((info.used_bytes * 100ULL) / info.total_bytes);
|
||||
if (pct > 100) {
|
||||
pct = 100;
|
||||
}
|
||||
}
|
||||
lv_bar_set_value(bar_usage, pct, LV_ANIM_ON);
|
||||
}
|
||||
|
||||
ui_refresh_audio_buttons();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user