From 6383756c82a0f61d7da6d896805559bf0140ad9a Mon Sep 17 00:00:00 2001 From: Andrew Dunai Date: Wed, 20 Mar 2024 21:33:15 +0200 Subject: [PATCH 1/2] lib: rename isDone to isFinished, add isFinished to menu, allow alert closing with A or Start --- firmware/doom/src/main.cpp | 2 +- firmware/keira/src/apps/demos/keyboard.cpp | 4 +- firmware/keira/src/apps/demos/letris.cpp | 2 +- firmware/keira/src/apps/demos/scan_i2c.cpp | 2 +- firmware/keira/src/apps/demos/transform.cpp | 2 +- firmware/keira/src/apps/launcher.cpp | 226 ++++++++++---------- firmware/keira/src/apps/lua/luarunner.cpp | 4 +- firmware/keira/src/apps/mjs/mjsrunner.cpp | 2 +- firmware/keira/src/apps/wifi_config.cpp | 15 +- sdk/lib/lilka/src/lilka/ui.cpp | 26 ++- sdk/lib/lilka/src/lilka/ui.h | 26 ++- sdk/lib/lilka/src/main.cpp | 2 +- 12 files changed, 161 insertions(+), 152 deletions(-) diff --git a/firmware/doom/src/main.cpp b/firmware/doom/src/main.cpp index dcc632b6..e893086a 100644 --- a/firmware/doom/src/main.cpp +++ b/firmware/doom/src/main.cpp @@ -130,7 +130,7 @@ void setup() { if (!found) { lilka::Alert alert("Doom", "Не знайдено .WAD-файлу на картці пам'яті"); alert.draw(&lilka::display); - while (!alert.isDone()) { + while (!alert.isFinished()) { alert.update(); } esp_restart(); diff --git a/firmware/keira/src/apps/demos/keyboard.cpp b/firmware/keira/src/apps/demos/keyboard.cpp index 6f81327f..27d5a31f 100644 --- a/firmware/keira/src/apps/demos/keyboard.cpp +++ b/firmware/keira/src/apps/demos/keyboard.cpp @@ -10,7 +10,7 @@ void KeyboardApp::run() { dialog.update(); dialog.draw(canvas); queueDraw(); - if (dialog.isDone()) { + if (dialog.isFinished()) { break; } } @@ -20,7 +20,7 @@ void KeyboardApp::run() { queueDraw(); while (true) { alert.update(); - if (alert.isDone()) { + if (alert.isFinished()) { break; } } diff --git a/firmware/keira/src/apps/demos/letris.cpp b/firmware/keira/src/apps/demos/letris.cpp index 0d3ce1aa..f4e913f4 100644 --- a/firmware/keira/src/apps/demos/letris.cpp +++ b/firmware/keira/src/apps/demos/letris.cpp @@ -295,7 +295,7 @@ void LetrisApp::run() { queueDraw(); while (1) { alert.update(); - if (alert.isDone()) { + if (alert.isFinished()) { break; } taskYIELD(); diff --git a/firmware/keira/src/apps/demos/scan_i2c.cpp b/firmware/keira/src/apps/demos/scan_i2c.cpp index a2060d47..9f113cb9 100644 --- a/firmware/keira/src/apps/demos/scan_i2c.cpp +++ b/firmware/keira/src/apps/demos/scan_i2c.cpp @@ -52,7 +52,7 @@ void ScanI2CApp::run() { lilka::Alert alert("Помилка", "Ця програма потребує Лілку версії 2 або вище."); alert.draw(canvas); queueDraw(); - while (!alert.isDone()) { + while (!alert.isFinished()) { alert.update(); } #endif diff --git a/firmware/keira/src/apps/demos/transform.cpp b/firmware/keira/src/apps/demos/transform.cpp index aec0b0c4..ce3f098e 100644 --- a/firmware/keira/src/apps/demos/transform.cpp +++ b/firmware/keira/src/apps/demos/transform.cpp @@ -12,7 +12,7 @@ void TransformApp::run() { lilka::Alert alert("Помилка", "Не вдалось завантажити face.bmp з SD-карти."); alert.draw(canvas); queueDraw(); - while (!alert.isDone()) { + while (!alert.isFinished()) { alert.update(); } return; diff --git a/firmware/keira/src/apps/launcher.cpp b/firmware/keira/src/apps/launcher.cpp index 4829832b..0a95e7c1 100644 --- a/firmware/keira/src/apps/launcher.cpp +++ b/firmware/keira/src/apps/launcher.cpp @@ -45,25 +45,24 @@ void LauncherApp::run() { menu.addItem("Налаштування", &settings, lilka::display.color565(255, 200, 224)); while (1) { - menu.update(); - menu.draw(canvas); - queueDraw(); - int16_t index = menu.getSelectedIndex(); - if (index != -1) { - if (index == 0) { - appsMenu(); - } else if (index == 1) { - sdBrowserMenu("/"); - } else if (index == 2) { - spiffsBrowserMenu(); - } else if (index == 3) { - // dev_menu(); - devMenu(); - } else if (index == 4) { - settingsMenu(); - } + while (!menu.isFinished()) { + menu.update(); + menu.draw(canvas); + queueDraw(); + } + int16_t index = menu.getCursor(); + if (index == 0) { + appsMenu(); + } else if (index == 1) { + sdBrowserMenu("/"); + } else if (index == 2) { + spiffsBrowserMenu(); + } else if (index == 3) { + // dev_menu(); + devMenu(); + } else if (index == 4) { + settingsMenu(); } - taskYIELD(); } } @@ -86,17 +85,16 @@ void LauncherApp::appsMenu() { } menu.addItem("<< Назад"); while (1) { - menu.update(); - menu.draw(canvas); - queueDraw(); - int16_t index = menu.getSelectedIndex(); - if (index != -1) { - if (index == appCount) { - break; - } - AppManager::getInstance()->runApp(app_items[index].construct()); + while (!menu.isFinished()) { + menu.update(); + menu.draw(canvas); + queueDraw(); } - taskYIELD(); + int16_t index = menu.getCursor(); + if (index == appCount) { + break; + } + AppManager::getInstance()->runApp(app_items[index].construct()); } } @@ -162,19 +160,18 @@ void LauncherApp::sdBrowserMenu(String path) { menu.addItem("<< Назад", 0, 0); while (1) { - menu.update(); - menu.draw(canvas); - queueDraw(); - int16_t index = menu.getSelectedIndex(); - if (index != -1) { - if (index >= numEntries - 1) break; - if (entries[index].type == lilka::EntryType::ENT_DIRECTORY) { - sdBrowserMenu(path + entries[index].name + "/"); - } else { - selectFile(lilka::sdcard.abspath(path + entries[index].name)); - } + while (!menu.isFinished()) { + menu.update(); + menu.draw(canvas); + queueDraw(); + } + int16_t index = menu.getCursor(); + if (index >= numEntries - 1) break; + if (entries[index].type == lilka::EntryType::ENT_DIRECTORY) { + sdBrowserMenu(path + entries[index].name + "/"); + } else { + selectFile(lilka::sdcard.abspath(path + entries[index].name)); } - taskYIELD(); } return; @@ -209,16 +206,16 @@ void LauncherApp::spiffsBrowserMenu() { menu.addItem(filenames[i], icons[i], colors[i]); } while (1) { - menu.update(); - menu.draw(canvas); - queueDraw(); - int16_t index = menu.getSelectedIndex(); - if (index != -1) { - if (index == numEntries - 1) { - return; - } - selectFile(lilka::filesystem.abspath(filenames[index])); + while (!menu.isFinished()) { + menu.update(); + menu.draw(canvas); + queueDraw(); + } + int16_t index = menu.getCursor(); + if (index == numEntries - 1) { + return; } + selectFile(lilka::filesystem.abspath(filenames[index])); } } @@ -292,16 +289,16 @@ void LauncherApp::devMenu() { } menu.addItem("<< Назад"); while (1) { - menu.update(); - menu.draw(canvas); - queueDraw(); - int16_t index = menu.getSelectedIndex(); - if (index != -1) { - if (index == appCount) { - return; - } - AppManager::getInstance()->runApp(app_items[index].construct()); + while (!menu.isFinished()) { + menu.update(); + menu.draw(canvas); + queueDraw(); } + int16_t index = menu.getCursor(); + if (index == appCount) { + return; + } + AppManager::getInstance()->runApp(app_items[index].construct()); } } @@ -320,68 +317,67 @@ void LauncherApp::settingsMenu() { menu.addItem(titles[i]); } while (1) { - menu.update(); - menu.draw(canvas); - queueDraw(); - int16_t index = menu.getSelectedIndex(); - if (index != -1) { - if (index == count - 1) { - return; + while (!menu.isFinished()) { + menu.update(); + menu.draw(canvas); + queueDraw(); + } + int16_t index = menu.getCursor(); + if (index == count - 1) { + return; + } + if (index == 0) { + AppManager::getInstance()->runApp(new WiFiConfigApp()); + } else if (index == 1) { + alert("Keira OS", "by Андерсон & friends"); + } else if (index == 2) { + char buf[256]; + NetworkService* networkService = + static_cast(ServiceManager::getInstance()->getService()); + // TODO: use dynamic_cast and assert networkService != nullptr + sprintf( + buf, + "Модель: %s\n" + "Ревізія: %d\n" + "Версія ESP-IDF: %s\n" + "Частота: %d МГц\n" + "Кількість ядер: %d\n" + "IP: %s", + ESP.getChipModel(), + ESP.getChipRevision(), + esp_get_idf_version(), + ESP.getCpuFreqMHz(), + ESP.getChipCores(), + networkService->getIpAddr().c_str() + ); + alert("Інфо про пристрій", buf); + } else if (index == 3) { + String labels[16]; + int labelCount = lilka::sys.get_partition_labels(labels); + labels[labelCount++] = "<< Назад"; + lilka::Menu partitionMenu("Таблиця розділів"); + for (int i = 0; i < labelCount; i++) { + partitionMenu.addItem(labels[i]); } - if (index == 0) { - AppManager::getInstance()->runApp(new WiFiConfigApp()); - } else if (index == 1) { - alert("Keira OS", "by Андерсон & friends"); - } else if (index == 2) { - char buf[256]; - NetworkService* networkService = - static_cast(ServiceManager::getInstance()->getService()); - // TODO: use dynamic_cast and assert networkService != nullptr - sprintf( - buf, - "Модель: %s\n" - "Ревізія: %d\n" - "Версія ESP-IDF: %s\n" - "Частота: %d МГц\n" - "Кількість ядер: %d\n" - "IP: %s", - ESP.getChipModel(), - ESP.getChipRevision(), - esp_get_idf_version(), - ESP.getCpuFreqMHz(), - ESP.getChipCores(), - networkService->getIpAddr().c_str() - ); - alert("Інфо про пристрій", buf); - } else if (index == 3) { - String labels[16]; - int labelCount = lilka::sys.get_partition_labels(labels); - labels[labelCount++] = "<< Назад"; - lilka::Menu partitionMenu("Таблиця розділів"); - for (int i = 0; i < labelCount; i++) { - partitionMenu.addItem(labels[i]); - } - while (1) { + while (1) { + while (!partitionMenu.isFinished()) { partitionMenu.update(); partitionMenu.draw(canvas); queueDraw(); - int16_t partitionIndex = partitionMenu.getSelectedIndex(); - if (partitionIndex != -1) { - if (partitionIndex == labelCount - 1) { - break; - } - alert( - labels[partitionIndex], - String("Адреса: 0x") + - String(lilka::sys.get_partition_address(labels[partitionIndex].c_str()), HEX) + "\n" + - "Розмір: 0x" + - String(lilka::sys.get_partition_size(labels[partitionIndex].c_str()), HEX) - ); - } } - } else if (index == 4) { - esp_restart(); + int16_t partitionIndex = partitionMenu.getCursor(); + if (partitionIndex == labelCount - 1) { + break; + } + alert( + labels[partitionIndex], + String("Адреса: 0x") + + String(lilka::sys.get_partition_address(labels[partitionIndex].c_str()), HEX) + "\n" + + "Розмір: 0x" + String(lilka::sys.get_partition_size(labels[partitionIndex].c_str()), HEX) + ); } + } else if (index == 4) { + esp_restart(); } } } @@ -392,7 +388,7 @@ void LauncherApp::alert(String title, String message) { queueDraw(); while (1) { alert.update(); - if (alert.isDone()) { + if (alert.isFinished()) { break; } taskYIELD(); diff --git a/firmware/keira/src/apps/lua/luarunner.cpp b/firmware/keira/src/apps/lua/luarunner.cpp index fc2f6cd5..2915af04 100644 --- a/firmware/keira/src/apps/lua/luarunner.cpp +++ b/firmware/keira/src/apps/lua/luarunner.cpp @@ -302,7 +302,7 @@ void LuaFileRunnerApp::run() { lilka::Alert alert("Lua", String("Помилка: ") + err); alert.draw(canvas); queueDraw(); - while (!alert.isDone()) { + while (!alert.isFinished()) { alert.update(); } } @@ -430,7 +430,7 @@ void LuaLiveRunnerApp::execSource(String source) { lilka::Alert alert("Lua", String("Помилка: ") + err); alert.draw(canvas); queueDraw(); - while (!alert.isDone()) { + while (!alert.isFinished()) { alert.update(); } } diff --git a/firmware/keira/src/apps/mjs/mjsrunner.cpp b/firmware/keira/src/apps/mjs/mjsrunner.cpp index 07d83e21..e5c346c8 100644 --- a/firmware/keira/src/apps/mjs/mjsrunner.cpp +++ b/firmware/keira/src/apps/mjs/mjsrunner.cpp @@ -18,7 +18,7 @@ void MJSApp::run() { lilka::Alert alert("mJS", String("Помилка: ") + err); alert.draw(canvas); queueDraw(); - while (!alert.isDone()) { + while (!alert.isFinished()) { alert.update(); } } diff --git a/firmware/keira/src/apps/wifi_config.cpp b/firmware/keira/src/apps/wifi_config.cpp index 271d75d5..c8d99ce0 100644 --- a/firmware/keira/src/apps/wifi_config.cpp +++ b/firmware/keira/src/apps/wifi_config.cpp @@ -38,7 +38,7 @@ void WiFiConfigApp::run() { lilka::Alert alert("Помилка", "Не вдалося сканувати мережі, код помилки: " + String(count)); alert.draw(canvas); queueDraw(); - while (!alert.isDone()) { + while (!alert.isFinished()) { alert.update(); } return; @@ -66,23 +66,22 @@ void WiFiConfigApp::run() { menu.addItem("<< Назад"); count++; while (1) { - int index = -1; - while (index == -1) { + while (!menu.isFinished()) { menu.update(); menu.draw(canvas); queueDraw(); - index = menu.getSelectedIndex(); } - if (index == count - 1) { + int cursor = menu.getCursor(); + if (cursor == count - 1) { return; } - String ssid = networks[index]; + String ssid = networks[cursor]; lilka::InputDialog passwordDialog("Введіть пароль:"); passwordDialog.setMasked(true); passwordDialog.setValue(networkService->getPassword(ssid)); - while (!passwordDialog.isDone()) { + while (!passwordDialog.isFinished()) { passwordDialog.update(); passwordDialog.draw(canvas); queueDraw(); @@ -124,7 +123,7 @@ void WiFiConfigApp::run() { alert.draw(canvas); queueDraw(); - while (!alert.isDone()) { + while (!alert.isFinished()) { alert.update(); } diff --git a/sdk/lib/lilka/src/lilka/ui.cpp b/sdk/lib/lilka/src/lilka/ui.cpp index c1997553..b91625d6 100644 --- a/sdk/lib/lilka/src/lilka/ui.cpp +++ b/sdk/lib/lilka/src/lilka/ui.cpp @@ -17,8 +17,8 @@ namespace lilka { Menu::Menu(String title) { this->title = title; this->scroll = 0; - this->selectedIndex = -1; this->setCursor(0); + this->done = false; } void Menu::addItem(String title, const menu_icon_t* icon, uint16_t color) { @@ -56,7 +56,7 @@ void Menu::update() { } } else if (state.a.justPressed) { // Execute selected function - this->selectedIndex = cursor; + done = true; } if (cursor < scroll) { @@ -133,16 +133,23 @@ void Menu::draw(Arduino_GFX* canvas) { } } -int16_t Menu::getSelectedIndex() { - int16_t index = selectedIndex; - selectedIndex = -1; - return index; +bool Menu::isFinished() { + if (done) { + done = false; + return true; + } + return false; +} + +int16_t Menu::getCursor() { + return cursor; } Alert::Alert(String title, String message) { this->title = title; this->message = message; this->done = false; + this->button = Button::COUNT; } void Alert::setTitle(String title) { @@ -155,7 +162,8 @@ void Alert::setMessage(String message) { void Alert::update() { State state = controller.getState(); - if (state.a.justPressed) { + if (state.a.justPressed || state.start.justPressed) { + button = state.a.justPressed ? Button::A : Button::START; done = true; } } @@ -187,7 +195,7 @@ void Alert::draw(Arduino_GFX* canvas) { canvas->println(message); } -bool Alert::isDone() { +bool Alert::isFinished() { if (done) { done = false; return true; @@ -473,7 +481,7 @@ void InputDialog::draw(Arduino_GFX* canvas) { } } -bool InputDialog::isDone() { +bool InputDialog::isFinished() { if (done) { done = false; return true; diff --git a/sdk/lib/lilka/src/lilka/ui.h b/sdk/lib/lilka/src/lilka/ui.h index 7266c515..5c25617f 100644 --- a/sdk/lib/lilka/src/lilka/ui.h +++ b/sdk/lib/lilka/src/lilka/ui.h @@ -5,6 +5,7 @@ #include #include "Arduino_GFX.h" #include "display.h" +#include "controller.h" typedef uint16_t const menu_icon_t[576]; // 24x24px icon @@ -73,19 +74,19 @@ class Menu { /// // ... /// @endcode void draw(Arduino_GFX* canvas); - /// Отримати індекс обраного пункту меню. - /// - /// Якщо жоден пункт не обрано, повертається ``-1``. + /// Перевірити, чи обрано пункт меню. /// - /// Також ця функція очищує обраний пункт, тому щойно вона поверне індекс обраного пункту, вона почне повертати ``-1`` до тих пір, поки не буде обрано новий пункт. - int16_t getSelectedIndex(); + /// Якщо пункт обрано (користувач натиснув кнопку "A"), повертається ``true``, інакше ``false``. Після виклику цієї функції пункт перестає бути обраним. + bool isFinished(); + /// Отримати індекс обраного пункту меню. + int16_t getCursor(); private: int16_t cursor; int16_t scroll; String title; std::vector items; - int16_t selectedIndex; + bool done; }; /// Клас для відображення сповіщення. @@ -104,7 +105,7 @@ class Menu { /// void loop() { /// lilka::Alert warning("Увага", "Повітряна тривога в москві, загроза балістичних ракет!"); /// warning.draw(&lilka::display); -/// while (!warning.isDone()) { +/// while (!warning.isFinished()) { /// warning.update(); /// } /// } @@ -141,13 +142,18 @@ class Alert { void draw(Arduino_GFX* canvas); /// Перевірити, чи користувач закрив сповіщення. /// - /// Якщо сповіщення закрито (користувач натиснув кнопку "A"), повертається ``true``, інакше ``false``. - bool isDone(); + /// Якщо сповіщення закрито (користувач натиснув кнопку "A" або "Start"), повертається ``true``, інакше ``false``. + bool isFinished(); + /// Отримати кнопку, якою користувач закрив сповіщення. + /// + /// Якщо сповіщення не закрито, результат буде невизначеним. Рекомендується використовувати цю функцію тільки після того, як ``isFinished()`` поверне ``true``. + Button getButton(); private: String title; String message; bool done; + Button button; }; /// Клас для відображення індикатора виконання. @@ -217,7 +223,7 @@ class InputDialog { void setValue(String value); void update(); void draw(Arduino_GFX* canvas); - bool isDone(); + bool isFinished(); String getValue(); private: diff --git a/sdk/lib/lilka/src/main.cpp b/sdk/lib/lilka/src/main.cpp index 5bbbe390..bba6e7f0 100644 --- a/sdk/lib/lilka/src/main.cpp +++ b/sdk/lib/lilka/src/main.cpp @@ -11,7 +11,7 @@ void __attribute__((weak)) loop() { // TODO: FreeRTOS experiment lilka::Alert alert("Лілка", "Ця прошивка не має\nжодного коду."); alert.draw(&lilka::display); - while (!alert.isDone()) { + while (!alert.isFinished()) { alert.update(); delay(100); } From 349acc0bd0798e9650efce1ce3ed88368fd37730 Mon Sep 17 00:00:00 2001 From: Andrew Dunai Date: Wed, 20 Mar 2024 21:48:25 +0200 Subject: [PATCH 2/2] keira: add SD card formatting option keira: increase FTP auth timeout to 30 s lib: fix missing Alert::getButton implementation --- .../lib/SimpleFTPServer/src/FtpServerKey.h | 4 +- firmware/keira/src/apps/launcher.cpp | 39 +++++++++++++++++-- sdk/lib/lilka/src/lilka/ui.cpp | 4 ++ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/firmware/keira/lib/SimpleFTPServer/src/FtpServerKey.h b/firmware/keira/lib/SimpleFTPServer/src/FtpServerKey.h index e67c4306..bb3fba94 100644 --- a/firmware/keira/lib/SimpleFTPServer/src/FtpServerKey.h +++ b/firmware/keira/lib/SimpleFTPServer/src/FtpServerKey.h @@ -115,8 +115,8 @@ However, the last one, that will break anything that uses the ESP32 WiFi library #define FTP_TIME_OUT 5 * 60 -// Wait for authentication for 10 seconds (expressed in seconds) -#define FTP_AUTH_TIME_OUT 10 +// Wait for authentication for 30 seconds (expressed in seconds) +#define FTP_AUTH_TIME_OUT 30 // Size of file buffer for read/write diff --git a/firmware/keira/src/apps/launcher.cpp b/firmware/keira/src/apps/launcher.cpp index 0a95e7c1..6a601115 100644 --- a/firmware/keira/src/apps/launcher.cpp +++ b/firmware/keira/src/apps/launcher.cpp @@ -1,3 +1,5 @@ +#include + #include "launcher.h" #include "appmanager.h" @@ -308,6 +310,7 @@ void LauncherApp::settingsMenu() { "Про систему", "Інфо про пристрій", "Таблиця розділів", + "Форматування SD-карти", "Перезавантаження", "<< Назад", }; @@ -377,6 +380,37 @@ void LauncherApp::settingsMenu() { ); } } else if (index == 4) { + if (!lilka::sdcard.available()) { + alert("Помилка", "SD-карта не знайдена"); + continue; + } + lilka::Alert confirm( + "Форматування", "УВАГА: Це очистить ВСІ дані з SD-карти!\n\nПродовжити?\n\nSTART - так\nA - скасувати" + ); + confirm.draw(canvas); + queueDraw(); + while (!confirm.isFinished()) { + confirm.update(); + taskYIELD(); + } + if (confirm.getButton() != lilka::Button::START) { + continue; + } + lilka::ProgressDialog dialog("Форматування", "Будь ласка, зачекайте..."); + dialog.draw(canvas); + queueDraw(); + const uint32_t workSize = FF_MAX_SS * 4; + void* work = ps_malloc(workSize + ); // Buffer (4 sectors), otherwise f_mkfs tries to allocate in stack and fails due to task stack size + FRESULT result = f_mkfs("/sd", FM_ANY, 0, work, workSize); // TODO - hardcoded mountpoint + free(work); + if (result != FR_OK) { + this->alert("Помилка", "Не вдалося сформатувати SD-карту, код помилки: " + String(result)); + continue; + } + this->alert("Форматування", "Форматування SD-карти завершено!"); + + } else if (index == 5) { esp_restart(); } } @@ -386,11 +420,8 @@ void LauncherApp::alert(String title, String message) { lilka::Alert alert(title, message); alert.draw(canvas); queueDraw(); - while (1) { + while (!alert.isFinished()) { alert.update(); - if (alert.isFinished()) { - break; - } taskYIELD(); } } diff --git a/sdk/lib/lilka/src/lilka/ui.cpp b/sdk/lib/lilka/src/lilka/ui.cpp index b91625d6..4b98637a 100644 --- a/sdk/lib/lilka/src/lilka/ui.cpp +++ b/sdk/lib/lilka/src/lilka/ui.cpp @@ -203,6 +203,10 @@ bool Alert::isFinished() { return false; } +Button Alert::getButton() { + return button; +} + ProgressDialog::ProgressDialog(String title, String message) { this->title = title; this->message = message;