From f09518267319f74114299094b761bc31eff55fb3fc1ba9aa9a40d170cf508ac2 Mon Sep 17 00:00:00 2001 From: AidarKC Date: Wed, 10 Jun 2026 13:24:50 +0400 Subject: [PATCH] =?UTF-8?q?ESP32:=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=B4=D0=BE=208=20Wi-Fi=20=D1=81=D0=B5?= =?UTF-8?q?=D1=82=D0=B5=D0=B9=20=D0=B8=20=D1=83=D1=81=D0=BA=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20burn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-06-08_1940_esp32_nav_minimal_test.md | 5 + .../shine_subserver_ui_nav_minimal_spec.md | 38 ++++-- .../test-device/README.md | 4 + .../test-device/burn.sh | 53 ++++++-- .../lvgl_nav_minimal_test.ino | 113 +++++++++++++++++- VERSION.properties | 4 +- 6 files changed, 191 insertions(+), 26 deletions(-) diff --git a/Dev_Docs/Pending_Features/2026-06-08_1940_esp32_nav_minimal_test.md b/Dev_Docs/Pending_Features/2026-06-08_1940_esp32_nav_minimal_test.md index 77f147b..7535120 100644 --- a/Dev_Docs/Pending_Features/2026-06-08_1940_esp32_nav_minimal_test.md +++ b/Dev_Docs/Pending_Features/2026-06-08_1940_esp32_nav_minimal_test.md @@ -20,6 +20,9 @@ - `SELECT NETWORK` запускает скан; - после скана показывается список доступных SSID; - выбор SSID открывает общий экран редактирования текста для пароля; + - если для этого SSID пароль уже сохранялся раньше, он автоматически подставляется в редактор; + - если затем ввести пароль для другого SSID, пароль первой сети не теряется; + - одновременно хранится до `8` паролей для разных SSID; - на этом экране видно старое значение, курсор стоит в конце; - две верхние служебные строки над полем ввода отсутствуют; - при вводе пароля Wi-Fi текст показывается открыто, без точек; @@ -40,6 +43,7 @@ - медленный свайп по экрану не должен превращаться в случайное нажатие кнопки; - `ABC/123`, `SHIFT`, `DEL`, `SAVE`, `CANCEL` работают; - при успехе SSID и пароль сохраняются, а `HOME` показывает `Wi-Fi connected`; + - если после подключения ко второй сети снова выбрать первую, её старый пароль уже подставлен и достаточно нажать `SAVE`; - при ошибке показывается `Connection failed`; - `CLEAR SAVED WI-FI` очищает сохранённые настройки; - если сеть была ранее успешно сохранена, после потери связи устройство автоматически пытается переподключиться; @@ -62,6 +66,7 @@ - `EDIT MANUALLY` открывает общий экран редактирования и сохраняет значение в NVS; - `Secret` открывает экран-заглушку, где сказано, что настройка ещё не реализована; - `Secret` теперь открывает меню секрета с показом секрета, ручным вводом и генерацией; + - в `SHOW SECRET` сам секрет показывается увеличенным шрифтом и разбит по `10` символов в строке; - при смене `login` сохранённый секрет сбрасывается в `not set`; - во время генерации секрета есть `CANCEL` и подтверждение остановки; - при отмене генерации старый секрет, если он был, не должен теряться; diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/shine_subserver_ui_nav_minimal_spec.md b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/shine_subserver_ui_nav_minimal_spec.md index 9e1198f..896c39e 100644 --- a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/shine_subserver_ui_nav_minimal_spec.md +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/shine_subserver_ui_nav_minimal_spec.md @@ -90,7 +90,7 @@ Показывает: - текущий Wi-Fi статус; -- сохранённый SSID; +- сохранённый SSID и число известных сетей; - статусное сообщение; - кнопку `SELECT NETWORK`; - кнопку `CLEAR SAVED WI-FI`; @@ -109,6 +109,13 @@ Нажатие на SSID открывает `TEXT_EDIT_SCREEN` для ввода пароля. +Поведение: +- если для выбранного SSID пароль уже был сохранён раньше, он сразу подставляется в поле ввода; +- если пароль меняют для другого SSID, старые сохранённые пароли других сетей не теряются; +- после успешного подключения выбранная сеть становится текущей `wifi_ssid/wifi_pass`; +- одновременно хранится до `8` известных сетей `SSID -> password`; +- `CLEAR SAVED WI-FI` очищает все сохранённые сети и текущую сеть. + Переходы: - свайп вправо из любого режима `WIFI_SCREEN` -> `SETTINGS_MENU` - кнопка `BACK` -> `SETTINGS_MENU` @@ -162,16 +169,26 @@ ## ACCOUNT_SECRET_SCREEN -Пока это заглушка. - Показывает: -- текущий статус секрета `set/not set`; -- сообщение, что настройка секрета пока не реализована; -- кнопку `BACK`. +- кнопку `SHOW SECRET` или серую `SECRET NOT SET`, если секрета ещё нет; +- кнопку `ENTER SECRET MANUALLY (NOT RECOMMENDED)`; +- кнопку `GENERATE SECRET`. Переходы: - свайп вправо -> `ACCOUNT_SCREEN` -- `BACK` -> `ACCOUNT_SCREEN` +- `SHOW SECRET` -> экран просмотра секрета +- `ENTER SECRET MANUALLY (NOT RECOMMENDED)` -> `TEXT_EDIT_SCREEN` +- `GENERATE SECRET` -> экран подтверждения генерации + +## SECRET_SHOW_SCREEN + +Показывает: +- заголовок `SECRET`; +- подпись `Current secret in base58:`; +- полный секрет открытым текстом; +- увеличенный шрифт для значения секрета; +- разбиение секрета по строкам по `10` символов; +- кнопку `BACK`. ## TEXT_EDIT_SCREEN @@ -211,8 +228,13 @@ - `wifi_ssid` - `wifi_pass` - `wifi_known_good` +- до `8` сохранённых пар `SSID -> password` -При старте устройства, если сохранён SSID, выполняется попытка подключения к сохранённой сети. +При старте устройства, если сохранён SSID, выполняется попытка подключения к текущей сохранённой сети. + +Если пользователь уже вводил пароль для сети раньше: +- при повторном выборе этого SSID старый пароль сразу подставляется в editor screen; +- сохранение пароля для другой сети не удаляет уже сохранённые пароли остальных сетей. Если сеть раньше уже была успешно подключена и помечена как валидная: - после потери связи устройство автоматически пытается переподключиться; diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/README.md b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/README.md index f5126ca..9ce964a 100644 --- a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/README.md +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/README.md @@ -1,6 +1,10 @@ # Test Device Скрипт заливает официальные Arduino-примеры для быстрой проверки платы. +`burn.sh` теперь: +- сам пытается найти USB-порт ESP32; +- сначала делает быструю инкрементальную сборку; +- если быстрая сборка не удалась, автоматически повторяет полную `clean`-сборку. Для режимов `widgets`, `audio` и `hello` рядом должен лежать локальный checkout `official-demo/` из официального репозитория Waveshare. В основной git он не добавляется, потому что это большой внешний набор примеров, библиотек, прошивок и артефактов. diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/burn.sh b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/burn.sh index 8ea2156..4783e44 100755 --- a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/burn.sh +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/burn.sh @@ -5,11 +5,29 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BOARD_DIR="$(cd "${ROOT_DIR}/.." && pwd)" DEMO_BASE="${BOARD_DIR}/official-demo/examples/Arduino-v3.3.5" MODE="${1:-widgets}" -PORT="${PORT:-/dev/ttyACM0}" +PORT="${PORT:-}" FQBN="${FQBN:-esp32:esp32:esp32s3:USBMode=hwcdc,CDCOnBoot=cdc,UploadSpeed=921600,CPUFreq=240,FlashMode=dio,FlashSize=16M,PartitionScheme=app3M_fat9M_16MB,PSRAM=opi}" BUILD_DIR="${BUILD_DIR:-${ROOT_DIR}/.arduino-build/build-${MODE}}" OUT_DIR="${OUT_DIR:-${ROOT_DIR}/.arduino-build/out-${MODE}}" +detect_port() { + local detected + detected="$(arduino-cli board list 2>/dev/null | awk '/\/dev\/tty(ACM|USB)/ {print $1; exit}')" + if [[ -n "${detected}" ]]; then + echo "${detected}" + return 0 + fi + + for candidate in /dev/ttyACM* /dev/ttyUSB*; do + if [[ -e "${candidate}" ]]; then + echo "${candidate}" + return 0 + fi + done + + return 1 +} + case "${MODE}" in hello) SKETCH_DIR="${DEMO_BASE}/examples/01_HelloWorld" ;; widgets) SKETCH_DIR="${DEMO_BASE}/examples/05_LVGL_Widgets" ;; @@ -34,6 +52,13 @@ case "${MODE}" in ;; esac +if [[ -z "${PORT}" ]]; then + if ! PORT="$(detect_port)"; then + echo "Failed to auto-detect ESP32 port. Set PORT=/dev/ttyACM0 ./burn.sh ${MODE}" >&2 + exit 3 + fi +fi + echo "== Mode: ${MODE}" echo "== Sketch: ${SKETCH_DIR}" echo "== Port: ${PORT}" @@ -41,17 +66,23 @@ echo "== FQBN: ${FQBN}" mkdir -p "${BUILD_DIR}" "${OUT_DIR}" -arduino-cli compile \ - --clean \ - --fqbn "${FQBN}" \ - --build-path "${BUILD_DIR}" \ - --output-dir "${OUT_DIR}" \ - --library "${DEMO_BASE}/libraries/GFX_Library_for_Arduino" \ - --library "${DEMO_BASE}/libraries/SensorLib" \ - --library "${DEMO_BASE}/libraries/XPowersLib" \ - --library "${DEMO_BASE}/libraries/lvgl" \ - --library "${DEMO_BASE}/libraries/Mylibrary" \ +compile_args=( + --fqbn "${FQBN}" + --build-path "${BUILD_DIR}" + --output-dir "${OUT_DIR}" + --library "${DEMO_BASE}/libraries/GFX_Library_for_Arduino" + --library "${DEMO_BASE}/libraries/SensorLib" + --library "${DEMO_BASE}/libraries/XPowersLib" + --library "${DEMO_BASE}/libraries/lvgl" + --library "${DEMO_BASE}/libraries/Mylibrary" "${SKETCH_DIR}" +) + +echo "== Compile: fast incremental build" +if ! arduino-cli compile "${compile_args[@]}"; then + echo "== Compile: fast build failed, retrying clean build" + arduino-cli compile --clean "${compile_args[@]}" +fi arduino-cli upload \ -p "${PORT}" \ diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/test_sketches/lvgl_nav_minimal_test/lvgl_nav_minimal_test.ino b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/test_sketches/lvgl_nav_minimal_test/lvgl_nav_minimal_test.ino index 4e30667..141c332 100644 --- a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/test_sketches/lvgl_nav_minimal_test/lvgl_nav_minimal_test.ino +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/test_sketches/lvgl_nav_minimal_test/lvgl_nav_minimal_test.ino @@ -27,6 +27,7 @@ #define SWIPE_THRESHOLD 48 #define TAP_CANCEL_THRESHOLD 18 #define MAX_SCAN_RESULTS 8 +#define MAX_SAVED_WIFI_NETWORKS 8 #define WIFI_CONNECT_TIMEOUT_MS 12000 #define WIFI_RECONNECT_FAST_MS 10000 #define WIFI_RECONNECT_SLOW_MS 30000 @@ -144,6 +145,8 @@ static uint32_t gLastHandledTouchSequence = 0; static String gWifiSavedSsid; static String gWifiSavedPassword; static String gWifiSelectedSsid; +static String gKnownWifiSsids[MAX_SAVED_WIFI_NETWORKS]; +static String gKnownWifiPasswords[MAX_SAVED_WIFI_NETWORKS]; static String gWifiStatusMessage = "No Wi-Fi configured"; static String gScanResults[MAX_SCAN_RESULTS]; static int gScanResultCount = 0; @@ -182,6 +185,10 @@ static void handleSwipe(SwipeDirection swipe); static void loadPrefs(); static void saveWifiPrefs(); static void clearWifiPrefs(); +static void clearSavedWifiList(); +static int findKnownWifiIndex(const String &ssid); +static String savedPasswordForSsid(const String &ssid); +static void upsertKnownWifi(const String &ssid, const String &password); static void saveServerPrefs(); static void saveAccountPrefs(); static void beginSavedWifi(); @@ -334,10 +341,86 @@ static void showMessageAt(const String &text, lv_coord_t y) { lv_obj_align(label, LV_ALIGN_TOP_MID, 0, y); } +static void clearSavedWifiList() { + for (int i = 0; i < MAX_SAVED_WIFI_NETWORKS; ++i) { + gKnownWifiSsids[i] = ""; + gKnownWifiPasswords[i] = ""; + } +} + +static int findKnownWifiIndex(const String &ssid) { + if (ssid.isEmpty()) { + return -1; + } + for (int i = 0; i < MAX_SAVED_WIFI_NETWORKS; ++i) { + if (gKnownWifiSsids[i] == ssid) { + return i; + } + } + return -1; +} + +static String savedPasswordForSsid(const String &ssid) { + int index = findKnownWifiIndex(ssid); + return index >= 0 ? gKnownWifiPasswords[index] : ""; +} + +static String splitFixedWidth(const String &value, size_t chunkSize) { + if (chunkSize == 0 || value.isEmpty()) { + return value; + } + + String out; + for (size_t i = 0; i < value.length(); ++i) { + if (i > 0 && (i % chunkSize) == 0) { + out += '\n'; + } + out += value.charAt(i); + } + return out; +} + +static void upsertKnownWifi(const String &ssid, const String &password) { + if (ssid.isEmpty()) { + return; + } + + int existing = findKnownWifiIndex(ssid); + if (existing >= 0) { + gKnownWifiPasswords[existing] = password; + return; + } + + for (int i = 0; i < MAX_SAVED_WIFI_NETWORKS; ++i) { + if (gKnownWifiSsids[i].isEmpty()) { + gKnownWifiSsids[i] = ssid; + gKnownWifiPasswords[i] = password; + return; + } + } + + for (int i = 0; i < MAX_SAVED_WIFI_NETWORKS - 1; ++i) { + gKnownWifiSsids[i] = gKnownWifiSsids[i + 1]; + gKnownWifiPasswords[i] = gKnownWifiPasswords[i + 1]; + } + gKnownWifiSsids[MAX_SAVED_WIFI_NETWORKS - 1] = ssid; + gKnownWifiPasswords[MAX_SAVED_WIFI_NETWORKS - 1] = password; +} + static void loadPrefs() { + clearSavedWifiList(); gWifiSavedSsid = gPrefs.getString("wifi_ssid", ""); gWifiSavedPassword = gPrefs.getString("wifi_pass", ""); gWifiKnownGood = gPrefs.getBool("wifi_known_good", false); + for (int i = 0; i < MAX_SAVED_WIFI_NETWORKS; ++i) { + String ssidKey = String("wifi_ssid_") + i; + String passKey = String("wifi_pass_") + i; + gKnownWifiSsids[i] = gPrefs.getString(ssidKey.c_str(), ""); + gKnownWifiPasswords[i] = gPrefs.getString(passKey.c_str(), ""); + } + if (!gWifiSavedSsid.isEmpty() && findKnownWifiIndex(gWifiSavedSsid) < 0) { + upsertKnownWifi(gWifiSavedSsid, gWifiSavedPassword); + } gSolanaRpcUrl = gPrefs.getString("solana_rpc", "https://api.devnet.solana.com"); gShineServerUrl = gPrefs.getString("shine_server", "https://shineup.me"); gLoginValue = gPrefs.getString("login", ""); @@ -358,6 +441,17 @@ static void saveWifiPrefs() { gPrefs.putString("wifi_ssid", gWifiSavedSsid); gPrefs.putString("wifi_pass", gWifiSavedPassword); gPrefs.putBool("wifi_known_good", gWifiKnownGood); + for (int i = 0; i < MAX_SAVED_WIFI_NETWORKS; ++i) { + String ssidKey = String("wifi_ssid_") + i; + String passKey = String("wifi_pass_") + i; + if (gKnownWifiSsids[i].isEmpty()) { + gPrefs.remove(ssidKey.c_str()); + gPrefs.remove(passKey.c_str()); + } else { + gPrefs.putString(ssidKey.c_str(), gKnownWifiSsids[i]); + gPrefs.putString(passKey.c_str(), gKnownWifiPasswords[i]); + } + } } static void saveServerPrefs() { @@ -382,6 +476,7 @@ static void clearWifiPrefs() { gWifiSavedSsid = ""; gWifiSavedPassword = ""; gWifiSelectedSsid = ""; + clearSavedWifiList(); gWifiKnownGood = false; gWifiReconnectEnabled = false; gWifiDisconnectedSinceMs = 0; @@ -418,7 +513,13 @@ static String wifiSavedLabel() { if (gWifiSavedSsid.isEmpty()) { return "Saved: none"; } - return String("Saved: ") + gWifiSavedSsid; + int knownCount = 0; + for (int i = 0; i < MAX_SAVED_WIFI_NETWORKS; ++i) { + if (!gKnownWifiSsids[i].isEmpty()) { + knownCount++; + } + } + return String("Saved: ") + gWifiSavedSsid + " (" + knownCount + ")"; } static String wifiHomeSummary() { @@ -652,6 +753,7 @@ static bool connectWifiNow(const String &ssid, const String &password) { if (WiFi.status() == WL_CONNECTED) { gWifiSavedSsid = ssid; gWifiSavedPassword = password; + upsertKnownWifi(ssid, password); gWifiKnownGood = true; gWifiReconnectEnabled = true; gWifiDisconnectedSinceMs = 0; @@ -830,7 +932,7 @@ static void networkSelectCb(lv_event_t *event) { SCREEN_WIFI, "ENTER PASSWORD", String("SSID: ") + gWifiSelectedSsid, - "", + savedPasswordForSsid(gWifiSelectedSsid), true); } @@ -1276,12 +1378,13 @@ static void drawSecretShowScreen() { if (gSecretConfigured) { showMessageAt("Current secret in base58:", 56); lv_obj_t *label = lv_label_create(gRoot); - lv_label_set_text(label, gSecretBase58.c_str()); + String secretLines = splitFixedWidth(gSecretBase58, 10); + lv_label_set_text(label, secretLines.c_str()); lv_obj_set_width(label, 420); lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); - lv_obj_set_style_text_font(label, &lv_font_montserrat_16, 0); + lv_obj_set_style_text_font(label, &lv_font_montserrat_20, 0); lv_obj_set_style_text_color(label, lv_color_hex(0xD9E1EA), 0); - lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 112); + lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 102); } else { showMessageAt("Secret not set", 96); } diff --git a/VERSION.properties b/VERSION.properties index 58bff52..08a46c7 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.150 -server.version=1.2.142 +client.version=1.2.151 +server.version=1.2.143