From e29e03dc47928c4ae7747f4357e6ec0aeb126a7c Mon Sep 17 00:00:00 2001 From: Andrew Dunai Date: Fri, 8 Mar 2024 02:02:07 +0200 Subject: [PATCH] main: RTOS demos & Lua runner from SD card --- firmware/main/bak/demos.h | 14 - firmware/main/src/app.cpp | 33 +- firmware/main/src/app.h | 5 +- firmware/main/src/appmanager.cpp | 11 +- .../main/{bak => src/apps/demos}/ball.cpp | 13 +- .../main/src/apps/{hud.h => demos/ball.h} | 4 +- .../{bak/disc.cpp => src/apps/demos/disk.cpp} | 13 +- firmware/main/src/apps/demos/disk.h | 9 + .../main/{bak => src/apps/demos}/epilepsy.cpp | 7 +- firmware/main/src/apps/demos/epilepsy.h | 9 + .../main/{bak => src/apps/demos}/letris.cpp | 22 +- firmware/main/src/apps/demos/letris.h | 9 + .../{bak => src/apps/demos}/letris_splash.h | 0 firmware/main/src/{ => apps}/demos/lines.cpp | 2 +- firmware/main/src/{ => apps}/demos/lines.h | 5 +- .../main/{bak => src/apps/demos}/scan_i2c.cpp | 11 +- firmware/main/src/apps/demos/scan_i2c.h | 9 + .../main/{bak => src/apps/demos}/user_spi.cpp | 27 +- firmware/main/src/apps/demos/user_spi.h | 9 + .../{bak => src/apps/demos}/wifi_scan.cpp | 25 +- firmware/main/src/apps/demos/wifi_scan.h | 9 + firmware/main/src/apps/launcher.cpp | 314 +++++++++++------- firmware/main/src/apps/launcher.h | 2 + .../src/{ => apps}/lua/lualilka_console.cpp | 0 .../src/{ => apps}/lua/lualilka_console.h | 0 .../{ => apps}/lua/lualilka_controller.cpp | 0 .../src/{ => apps}/lua/lualilka_controller.h | 0 .../src/{ => apps}/lua/lualilka_display.cpp | 37 ++- .../src/{ => apps}/lua/lualilka_display.h | 0 .../src/{ => apps}/lua/lualilka_geometry.cpp | 0 .../src/{ => apps}/lua/lualilka_geometry.h | 0 .../main/src/{ => apps}/lua/lualilka_gpio.cpp | 0 .../main/src/{ => apps}/lua/lualilka_gpio.h | 0 .../main/src/{ => apps}/lua/lualilka_math.cpp | 0 .../main/src/{ => apps}/lua/lualilka_math.h | 0 .../src/{ => apps}/lua/lualilka_resources.cpp | 0 .../src/{ => apps}/lua/lualilka_resources.h | 0 .../src/{ => apps}/lua/lualilka_state.cpp | 0 .../main/src/{ => apps}/lua/lualilka_state.h | 0 .../main/src/{ => apps}/lua/lualilka_util.cpp | 3 +- .../main/src/{ => apps}/lua/lualilka_util.h | 0 .../main/src/{ => apps}/lua/luarunner.cpp | 303 ++++++++--------- firmware/main/src/apps/lua/luarunner.h | 48 +++ .../main/src/apps/{hud.cpp => statusbar.cpp} | 6 +- firmware/main/src/apps/statusbar.h | 9 + firmware/main/src/lua/luarunner.h | 29 -- firmware/main/src/main.cpp | 4 +- sdk/lib/lilka/src/lilka.cpp | 1 - sdk/lib/lilka/src/lilka/ui.cpp | 3 +- 49 files changed, 608 insertions(+), 397 deletions(-) delete mode 100644 firmware/main/bak/demos.h rename firmware/main/{bak => src/apps/demos}/ball.cpp (93%) rename firmware/main/src/apps/{hud.h => demos/ball.h} (57%) rename firmware/main/{bak/disc.cpp => src/apps/demos/disk.cpp} (91%) create mode 100644 firmware/main/src/apps/demos/disk.h rename firmware/main/{bak => src/apps/demos}/epilepsy.cpp (91%) create mode 100644 firmware/main/src/apps/demos/epilepsy.h rename firmware/main/{bak => src/apps/demos}/letris.cpp (95%) create mode 100644 firmware/main/src/apps/demos/letris.h rename firmware/main/{bak => src/apps/demos}/letris_splash.h (100%) rename firmware/main/src/{ => apps}/demos/lines.cpp (95%) rename firmware/main/src/{ => apps}/demos/lines.h (63%) rename firmware/main/{bak => src/apps/demos}/scan_i2c.cpp (85%) create mode 100644 firmware/main/src/apps/demos/scan_i2c.h rename firmware/main/{bak => src/apps/demos}/user_spi.cpp (72%) create mode 100644 firmware/main/src/apps/demos/user_spi.h rename firmware/main/{bak => src/apps/demos}/wifi_scan.cpp (55%) create mode 100644 firmware/main/src/apps/demos/wifi_scan.h rename firmware/main/src/{ => apps}/lua/lualilka_console.cpp (100%) rename firmware/main/src/{ => apps}/lua/lualilka_console.h (100%) rename firmware/main/src/{ => apps}/lua/lualilka_controller.cpp (100%) rename firmware/main/src/{ => apps}/lua/lualilka_controller.h (100%) rename firmware/main/src/{ => apps}/lua/lualilka_display.cpp (91%) rename firmware/main/src/{ => apps}/lua/lualilka_display.h (100%) rename firmware/main/src/{ => apps}/lua/lualilka_geometry.cpp (100%) rename firmware/main/src/{ => apps}/lua/lualilka_geometry.h (100%) rename firmware/main/src/{ => apps}/lua/lualilka_gpio.cpp (100%) rename firmware/main/src/{ => apps}/lua/lualilka_gpio.h (100%) rename firmware/main/src/{ => apps}/lua/lualilka_math.cpp (100%) rename firmware/main/src/{ => apps}/lua/lualilka_math.h (100%) rename firmware/main/src/{ => apps}/lua/lualilka_resources.cpp (100%) rename firmware/main/src/{ => apps}/lua/lualilka_resources.h (100%) rename firmware/main/src/{ => apps}/lua/lualilka_state.cpp (100%) rename firmware/main/src/{ => apps}/lua/lualilka_state.h (100%) rename firmware/main/src/{ => apps}/lua/lualilka_util.cpp (91%) rename firmware/main/src/{ => apps}/lua/lualilka_util.h (100%) rename firmware/main/src/{ => apps}/lua/luarunner.cpp (70%) create mode 100644 firmware/main/src/apps/lua/luarunner.h rename firmware/main/src/apps/{hud.cpp => statusbar.cpp} (85%) create mode 100644 firmware/main/src/apps/statusbar.h delete mode 100644 firmware/main/src/lua/luarunner.h diff --git a/firmware/main/bak/demos.h b/firmware/main/bak/demos.h deleted file mode 100644 index 3891372b..00000000 --- a/firmware/main/bak/demos.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef LILKA_DEMOS_H -#define LILKA_DEMOS_H - -#include - -void demo_lines(lilka::Canvas *canvas); -void demo_disc(lilka::Canvas *canvas); -void demo_epilepsy(lilka::Canvas *canvas); -void demo_ball(lilka::Canvas *canvas); -void demo_letris(lilka::Canvas *canvas); -void demo_user_spi(lilka::Canvas *canvas); -void demo_scan_i2c(lilka::Canvas *canvas); - -#endif // LILKA_DEMOS_H diff --git a/firmware/main/src/app.cpp b/firmware/main/src/app.cpp index 23c3444b..f21e9a40 100644 --- a/firmware/main/src/app.cpp +++ b/firmware/main/src/app.cpp @@ -12,26 +12,33 @@ App::App(const char *name, uint16_t x, uint16_t y, uint16_t w, uint16_t h) : nam } void App::start() { - xTaskCreate( - [](void *app) { - App *a = (App *)app; - a->run(); - }, - name, 4096, this, 1, NULL - ); + Serial.println("Starting app " + String(name)); + xTaskCreate(_run, name, 32768, this, 1, &taskHandle); +} + +void App::_run(void *data) { + App *app = (App *)data; + app->run(); + if (app->getState() != eTaskState::eDeleted) { + // App might have been stopped by itself. If not, stop it, or we'll get panic from FreeRTOS kernel. + app->stop(); + } } void App::suspend() { // TODO: Check if the task is already suspended + Serial.println("Suspending app " + String(name) + " (state = " + String(getState()) + ")"); vTaskSuspend(taskHandle); } void App::resume() { // TODO: Check if the task is already running + Serial.println("Resuming app " + String(name) + " (state = " + String(getState()) + ")"); vTaskResume(taskHandle); } void App::stop() { + Serial.println("Stopping app " + String(name) + " (state = " + String(getState()) + ")"); vTaskDelete(taskHandle); } @@ -42,12 +49,19 @@ App::~App() { } void App::queueDraw() { // Swap the front and back canvases + // Serial.println("Queuing draw for " + String(name) + ", canvas address = " + String((uint32_t)canvas)); xSemaphoreTake(backCanvasMutex, portMAX_DELAY); lilka::Canvas *temp = canvas; canvas = backCanvas; backCanvas = temp; - xSemaphoreGive(backCanvasMutex); dirty = true; + // uint64_t now = micros(); + // Too slow... + // memcpy( + // canvas->getFramebuffer(), backCanvas->getFramebuffer(), canvas->width() * canvas->height() * 2 + // ); // TODO: Hard-coded 2 bytes per pixel + // Serial.println("Copy took " + String(micros() - now) + "us"); + xSemaphoreGive(backCanvasMutex); taskYIELD(); } @@ -72,5 +86,8 @@ void App::releaseBackCanvas() { } eTaskState App::getState() { + if (taskHandle == NULL) { + return eTaskState::eInvalid; + } return eTaskGetState(taskHandle); } diff --git a/firmware/main/src/app.h b/firmware/main/src/app.h index 3d3345ba..246a4856 100644 --- a/firmware/main/src/app.h +++ b/firmware/main/src/app.h @@ -13,6 +13,7 @@ class App { void suspend(); void resume(); void stop(); + static void _run(void *data); virtual void run() = 0; void queueDraw(); bool needsRedraw(); @@ -24,10 +25,8 @@ class App { eTaskState getState(); - lilka::Canvas *backCanvas; - -protected: lilka::Canvas *canvas; + lilka::Canvas *backCanvas; private: const char *name; diff --git a/firmware/main/src/appmanager.cpp b/firmware/main/src/appmanager.cpp index ceb66d3d..69ff6258 100644 --- a/firmware/main/src/appmanager.cpp +++ b/firmware/main/src/appmanager.cpp @@ -49,16 +49,15 @@ void AppManager::loop() { xSemaphoreTake(mutex, portMAX_DELAY); // Draw panel + panel->acquireBackCanvas(); if (panel->needsRedraw()) { - Serial.println("Redrawing panel"); - panel->acquireBackCanvas(); lilka::display.draw16bitRGBBitmap( panel->backCanvas->x(), panel->backCanvas->y(), panel->backCanvas->getFramebuffer(), panel->backCanvas->width(), panel->backCanvas->height() ); - panel->releaseBackCanvas(); panel->markClean(); } + panel->releaseBackCanvas(); // Check if top app has finished App *topApp = apps.back(); @@ -78,15 +77,15 @@ void AppManager::loop() { } // Draw top app + topApp->acquireBackCanvas(); if (topApp->needsRedraw()) { - Serial.println("Redrawing " + String(topApp->getName())); - topApp->acquireBackCanvas(); lilka::display.draw16bitRGBBitmap( topApp->backCanvas->x(), topApp->backCanvas->y(), topApp->backCanvas->getFramebuffer(), topApp->backCanvas->width(), topApp->backCanvas->height() ); - topApp->releaseBackCanvas(); topApp->markClean(); } + topApp->releaseBackCanvas(); + xSemaphoreGive(mutex); } diff --git a/firmware/main/bak/ball.cpp b/firmware/main/src/apps/demos/ball.cpp similarity index 93% rename from firmware/main/bak/ball.cpp rename to firmware/main/src/apps/demos/ball.cpp index 65603912..72770d95 100644 --- a/firmware/main/bak/ball.cpp +++ b/firmware/main/src/apps/demos/ball.cpp @@ -1,6 +1,8 @@ -#include +#include "ball.h" -void demo_ball(lilka::Canvas *canvas) { +BallApp::BallApp() : App("Ball") {} + +void BallApp::run() { float x = (float)canvas->width() / 2; float y = (float)canvas->height() / 4; float xVelo = 320, yVelo = 0; @@ -10,8 +12,6 @@ void demo_ball(lilka::Canvas *canvas) { while (1) { float delta = (millis() - prevRenderTime) / 1000.0; - canvas->fillScreen(canvas->color565(0, 0, 0)); - yVelo += gravity * delta; x += xVelo * delta; y += yVelo * delta; @@ -26,7 +26,6 @@ void demo_ball(lilka::Canvas *canvas) { y = y < radius ? radius : canvas->height() - radius; } - canvas->fillCircle(x, y, radius, canvas->color565(255, 200, 0)); lilka::State state = lilka::controller.getState(); if (state.a.justPressed) { return; @@ -40,9 +39,13 @@ void demo_ball(lilka::Canvas *canvas) { xVelo = 500; } + canvas->fillScreen(canvas->color565(0, 0, 0)); + canvas->fillCircle(x, y, radius, canvas->color565(255, 200, 0)); // Calculate FPS canvas->setCursor(16, 32); canvas->println("FPS: " + String(1000 / (millis() - prevRenderTime))); + prevRenderTime = millis(); + queueDraw(); } } diff --git a/firmware/main/src/apps/hud.h b/firmware/main/src/apps/demos/ball.h similarity index 57% rename from firmware/main/src/apps/hud.h rename to firmware/main/src/apps/demos/ball.h index 8c442fbc..3d5b1074 100644 --- a/firmware/main/src/apps/hud.h +++ b/firmware/main/src/apps/demos/ball.h @@ -2,8 +2,8 @@ #include "app.h" -class HUDApp : public App { +class BallApp : public App { public: - HUDApp(); + BallApp(); void run(); }; diff --git a/firmware/main/bak/disc.cpp b/firmware/main/src/apps/demos/disk.cpp similarity index 91% rename from firmware/main/bak/disc.cpp rename to firmware/main/src/apps/demos/disk.cpp index 548b8223..6ba99bb3 100644 --- a/firmware/main/bak/disc.cpp +++ b/firmware/main/src/apps/demos/disk.cpp @@ -1,6 +1,8 @@ -#include +#include "disk.h" -void demo_disc(lilka::Canvas *canvas) { +DiskApp::DiskApp() : App("Disk") {} + +void DiskApp::run() { float x = random(16, canvas->width() - 16); float y = random(16, canvas->height() - 16); float xDir = 3; @@ -8,7 +10,6 @@ void demo_disc(lilka::Canvas *canvas) { int16_t radius = 16; uint64_t prevRenderTime = millis(); while (1) { - canvas->fillScreen(canvas->color565(0, 0, 0)); x += xDir; y += yDir; bool hit = false; @@ -26,15 +27,15 @@ void demo_disc(lilka::Canvas *canvas) { xDir = xDirNew; yDir = yDirNew; } - canvas->drawCircle(x, y, radius, 0xFFFF); if (lilka::controller.getState().a.justPressed) { return; } - // Calculate FPS + canvas->fillScreen(canvas->color565(0, 0, 0)); + canvas->drawCircle(x, y, radius, 0xFFFF); canvas->setCursor(16, 32); canvas->println("FPS: " + String(1000 / (millis() - prevRenderTime))); prevRenderTime = millis(); - vTaskDelay(0); + queueDraw(); } } diff --git a/firmware/main/src/apps/demos/disk.h b/firmware/main/src/apps/demos/disk.h new file mode 100644 index 00000000..9bd85ece --- /dev/null +++ b/firmware/main/src/apps/demos/disk.h @@ -0,0 +1,9 @@ +#pragma once + +#include "app.h" + +class DiskApp : public App { +public: + DiskApp(); + void run(); +}; diff --git a/firmware/main/bak/epilepsy.cpp b/firmware/main/src/apps/demos/epilepsy.cpp similarity index 91% rename from firmware/main/bak/epilepsy.cpp rename to firmware/main/src/apps/demos/epilepsy.cpp index b36ffde1..32a30766 100644 --- a/firmware/main/bak/epilepsy.cpp +++ b/firmware/main/src/apps/demos/epilepsy.cpp @@ -1,6 +1,8 @@ -#include +#include "epilepsy.h" -void demo_epilepsy(lilka::Canvas *canvas) { +EpilepsyApp::EpilepsyApp() : App("Epilepsy") {} + +void EpilepsyApp::run() { while (1) { float time = millis() / 1000.0; float size = sin(time * PI * 1.5) * LILKA_DISPLAY_WIDTH; @@ -20,6 +22,7 @@ void demo_epilepsy(lilka::Canvas *canvas) { }; canvas->fillTriangle(points[0][0], points[0][1], points[1][0], points[1][1], points[2][0], points[2][1], color); canvas->fillTriangle(points[0][0], points[0][1], points[2][0], points[2][1], points[3][0], points[3][1], color); + queueDraw(); if (lilka::controller.getState().a.justPressed) { return; diff --git a/firmware/main/src/apps/demos/epilepsy.h b/firmware/main/src/apps/demos/epilepsy.h new file mode 100644 index 00000000..085b262b --- /dev/null +++ b/firmware/main/src/apps/demos/epilepsy.h @@ -0,0 +1,9 @@ +#pragma once + +#include "app.h" + +class EpilepsyApp : public App { +public: + EpilepsyApp(); + void run(); +}; diff --git a/firmware/main/bak/letris.cpp b/firmware/main/src/apps/demos/letris.cpp similarity index 95% rename from firmware/main/bak/letris.cpp rename to firmware/main/src/apps/demos/letris.cpp index a49a6406..73e8912d 100644 --- a/firmware/main/bak/letris.cpp +++ b/firmware/main/src/apps/demos/letris.cpp @@ -1,5 +1,4 @@ -#include - +#include "letris.h" #include "letris_splash.h" #define BLOCK_SIZE 10 @@ -173,7 +172,9 @@ class Field { lilka::Canvas *canvas; }; -void demo_letris(lilka::Canvas *canvas) { +LetrisApp::LetrisApp() : App("Letris") {} + +void LetrisApp::run() { // Створюємо поле та фігуру Field field(canvas); Shape shape(canvas); @@ -239,7 +240,7 @@ void demo_letris(lilka::Canvas *canvas) { fastDrop = true; nextMove = 0; } - // Чи може фігура рухатися в горизонтально? + // Чи може фігура рухатися горизонтально? if (field.willCollide(&shape, dx, 0)) { // Ні, фігура зіткнеться зі стіною. Змінюємо напрямок руху на 0 dx = 0; @@ -250,6 +251,7 @@ void demo_letris(lilka::Canvas *canvas) { field.draw(); shape.draw(); nextShape.draw(true); + queueDraw(); // Відображаємо зміни на екрані // lilka::display.renderCanvas(canvas); } @@ -269,5 +271,15 @@ void demo_letris(lilka::Canvas *canvas) { // Гра закінчилася. Виводимо повідомлення на екран // TODO: FreeRTOS experiment - lilka::ui_alert(canvas, "Game over", "Гру завершено!\nТи намагався. :)"); + // lilka::ui_alert(canvas, "Game over", "Гру завершено!\nТи намагався. :)"); + lilka::Alert alert("Game over", "Гру завершено!\nТи намагався. :)"); + alert.draw(canvas); + queueDraw(); + while (1) { + alert.update(); + if (alert.isDone()) { + break; + } + taskYIELD(); + } } diff --git a/firmware/main/src/apps/demos/letris.h b/firmware/main/src/apps/demos/letris.h new file mode 100644 index 00000000..ef5ec2a0 --- /dev/null +++ b/firmware/main/src/apps/demos/letris.h @@ -0,0 +1,9 @@ +#pragma once + +#include "app.h" + +class LetrisApp : public App { +public: + LetrisApp(); + void run(); +}; diff --git a/firmware/main/bak/letris_splash.h b/firmware/main/src/apps/demos/letris_splash.h similarity index 100% rename from firmware/main/bak/letris_splash.h rename to firmware/main/src/apps/demos/letris_splash.h diff --git a/firmware/main/src/demos/lines.cpp b/firmware/main/src/apps/demos/lines.cpp similarity index 95% rename from firmware/main/src/demos/lines.cpp rename to firmware/main/src/apps/demos/lines.cpp index 87d76354..b57885bc 100644 --- a/firmware/main/src/demos/lines.cpp +++ b/firmware/main/src/apps/demos/lines.cpp @@ -16,7 +16,7 @@ void DemoLines::run() { canvas->drawLine(x1, y1, x2, y2, color); queueDraw(); if (lilka::controller.getState().a.justPressed) { - vTaskDelete(NULL); + stop(); return; } vTaskDelay(30 / portTICK_PERIOD_MS); diff --git a/firmware/main/src/demos/lines.h b/firmware/main/src/apps/demos/lines.h similarity index 63% rename from firmware/main/src/demos/lines.h rename to firmware/main/src/apps/demos/lines.h index e9c39c5f..3e63ad35 100644 --- a/firmware/main/src/demos/lines.h +++ b/firmware/main/src/apps/demos/lines.h @@ -1,5 +1,4 @@ -#ifndef DEMO_LINES_H -#define DEMO_LINES_H +#pragma once #include "app.h" #include "lilka.h" @@ -9,5 +8,3 @@ class DemoLines : public App { DemoLines(); void run(); }; - -#endif //DEMO_LINES_H diff --git a/firmware/main/bak/scan_i2c.cpp b/firmware/main/src/apps/demos/scan_i2c.cpp similarity index 85% rename from firmware/main/bak/scan_i2c.cpp rename to firmware/main/src/apps/demos/scan_i2c.cpp index 7ae4adde..698facf1 100644 --- a/firmware/main/bak/scan_i2c.cpp +++ b/firmware/main/src/apps/demos/scan_i2c.cpp @@ -1,16 +1,18 @@ #include #include -#include +#include "scan_i2c.h" -void demo_scan_i2c(lilka::Canvas *canvas) { +ScanI2CApp::ScanI2CApp() : App("I2C Scanner") {} + +void ScanI2CApp::run() { Wire.begin(9, 10, 100000); canvas->fillScreen(canvas->color565(0, 0, 0)); canvas->setTextBound(4, 0, LILKA_DISPLAY_WIDTH - 8, LILKA_DISPLAY_HEIGHT); canvas->setCursor(4, 48); - canvas->println("Starting I2C scan..."); + queueDraw(); uint8_t found = 0; for (uint16_t address = 1; address <= 127; address++) { @@ -30,10 +32,11 @@ void demo_scan_i2c(lilka::Canvas *canvas) { canvas->println("I2C scan done."); canvas->printf("Found %d devices.", found); + queueDraw(); Wire.end(); while (!lilka::controller.getState().a.justPressed) { - delay(10); + taskYIELD(); } } diff --git a/firmware/main/src/apps/demos/scan_i2c.h b/firmware/main/src/apps/demos/scan_i2c.h new file mode 100644 index 00000000..866b7ab2 --- /dev/null +++ b/firmware/main/src/apps/demos/scan_i2c.h @@ -0,0 +1,9 @@ +#pragma once + +#include "app.h" + +class ScanI2CApp : public App { +public: + ScanI2CApp(); + void run(); +}; diff --git a/firmware/main/bak/user_spi.cpp b/firmware/main/src/apps/demos/user_spi.cpp similarity index 72% rename from firmware/main/bak/user_spi.cpp rename to firmware/main/src/apps/demos/user_spi.cpp index 5b82badd..87ec9829 100644 --- a/firmware/main/bak/user_spi.cpp +++ b/firmware/main/src/apps/demos/user_spi.cpp @@ -1,44 +1,53 @@ -#include +#include "user_spi.h" -void demo_user_spi(lilka::Canvas *canvas) { +UserSPIApp::UserSPIApp() : App("SPI") {} + +void UserSPIApp::run() { #ifdef SPI2_NUM canvas->fillScreen(canvas->color565(0, 0, 0)); canvas->setTextBound(4, 0, LILKA_DISPLAY_WIDTH - 8, LILKA_DISPLAY_HEIGHT); canvas->setCursor(4, 48); - canvas->println("SPI2 begin"); + queueDraw(); + lilka::SPI2.begin(45, 48, 47); - delay(500); + vTaskDelay(500 / portTICK_PERIOD_MS); canvas->println("beginTransaction"); canvas->println(" SPI mode: 0"); + queueDraw(); lilka::SPI2.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); canvas->println(" write: 0xAA"); + queueDraw(); lilka::SPI2.write(0xAA); canvas->println("endTransaction"); + queueDraw(); lilka::SPI2.endTransaction(); - delay(10); + vTaskDelay(10 / portTICK_PERIOD_MS); canvas->println("beginTransaction"); canvas->println(" SPI mode: 3"); + queueDraw(); lilka::SPI2.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE3)); canvas->println(" SPI mode: 0xAA"); + queueDraw(); lilka::SPI2.write(0xAA); canvas->println("endTransaction"); + queueDraw(); lilka::SPI2.endTransaction(); - delay(500); + vTaskDelay(500 / portTICK_PERIOD_MS); canvas->println("SPI2 end"); + queueDraw(); lilka::SPI2.end(); canvas->println("Press A to exit"); + queueDraw(); while (!lilka::controller.getState().a.justPressed) { - delay(10); + taskYIELD(); } -#else - lilka::ui_alert("Помилка", "SPI2 не підтримується"); #endif } diff --git a/firmware/main/src/apps/demos/user_spi.h b/firmware/main/src/apps/demos/user_spi.h new file mode 100644 index 00000000..f37e82b1 --- /dev/null +++ b/firmware/main/src/apps/demos/user_spi.h @@ -0,0 +1,9 @@ +#pragma once + +#include "app.h" + +class UserSPIApp : public App { +public: + UserSPIApp(); + void run(); +}; diff --git a/firmware/main/bak/wifi_scan.cpp b/firmware/main/src/apps/demos/wifi_scan.cpp similarity index 55% rename from firmware/main/bak/wifi_scan.cpp rename to firmware/main/src/apps/demos/wifi_scan.cpp index c8ced8fd..700ea58c 100644 --- a/firmware/main/bak/wifi_scan.cpp +++ b/firmware/main/src/apps/demos/wifi_scan.cpp @@ -1,14 +1,19 @@ -#include #include -void demo_scan_wifi(lilka::Canvas *canvas) { +#include "wifi_scan.h" + +WifiScanApp::WifiScanApp() : App("WiFi Scanner") {} + +void WifiScanApp::run() { canvas->fillScreen(canvas->color565(0, 0, 0)); canvas->setCursor(4, 150); + queueDraw(); WiFi.mode(WIFI_STA); WiFi.disconnect(); canvas->println("Скануємо мережі WiFi..."); + queueDraw(); int16_t count = WiFi.scanNetworks(false); // while (count == WIFI_SCAN_RUNNING) { @@ -22,7 +27,21 @@ void demo_scan_wifi(lilka::Canvas *canvas) { } // TODO: FreeRTOS experiment - lilka::ui_menu(canvas, "Мережі", networks, count, 0); + // lilka::ui_menu(canvas, "Мережі", networks, count, 0); + lilka::Menu menu("Мережі"); + for (int16_t i = 0; i < count; i++) { + menu.addItem(networks[i]); + } + menu.draw(canvas); + queueDraw(); + while (1) { + menu.update(); + menu.draw(canvas); + queueDraw(); + if (menu.getSelectedIndex() != -1) { + break; + } + } // while (!lilka::controller.getState().a.justPressed) { // delay(10); diff --git a/firmware/main/src/apps/demos/wifi_scan.h b/firmware/main/src/apps/demos/wifi_scan.h new file mode 100644 index 00000000..c1919500 --- /dev/null +++ b/firmware/main/src/apps/demos/wifi_scan.h @@ -0,0 +1,9 @@ +#pragma once + +#include "app.h" + +class WifiScanApp : public App { +public: + WifiScanApp(); + void run(); +}; diff --git a/firmware/main/src/apps/launcher.cpp b/firmware/main/src/apps/launcher.cpp index 8594ea3a..fa44d546 100644 --- a/firmware/main/src/apps/launcher.cpp +++ b/firmware/main/src/apps/launcher.cpp @@ -2,6 +2,13 @@ #include "appmanager.h" #include "demos/lines.h" +#include "demos/disk.h" +#include "demos/ball.h" +#include "demos/epilepsy.h" +#include "demos/letris.h" +#include "demos/user_spi.h" +#include "demos/scan_i2c.h" +#include "lua/luarunner.h" #include "icons/demos.h" #include "icons/sdcard.h" @@ -37,6 +44,7 @@ void LauncherApp::run() { if (index == 0) { demosMenu(); } else if (index == 1) { + sdBrowserMenu("/"); // sd_browser_menu("/"); } else if (index == 2) { // spiffs_browser_menu(); @@ -68,8 +76,13 @@ void LauncherApp::demosMenu() { String demos[] = { // "Лінії", "Шайба", "М'ячик", "Епілепсія", "Летріс", "Тест SPI", "I2C-сканер", "<< Назад", - "Лінії", - "<< Назад", + "Лінії", "Шайба", "М'ячик", "Епілепсія", "Летріс", "Тест SPI", "I2C-сканер", "<< Назад", + }; + // vector of functions + std::vector> demo_funcs = { + []() { return new DemoLines(); }, []() { return new DiskApp(); }, []() { return new BallApp(); }, + []() { return new EpilepsyApp(); }, []() { return new LetrisApp(); }, []() { return new UserSPIApp(); }, + []() { return new ScanI2CApp(); }, }; int count = sizeof(demos) / sizeof(demos[0]); lilka::Menu menu("Демо"); @@ -82,11 +95,11 @@ void LauncherApp::demosMenu() { menu.draw(canvas); queueDraw(); int16_t index = menu.getSelectedIndex(); - if (index == count - 1) { - break; - } - if (index == 0) { - AppManager::getInstance()->addApp(new DemoLines()); + if (index != -1) { + if (index == count - 1) { + break; + } + AppManager::getInstance()->addApp(demo_funcs[index]()); } // if (index != -1) { // demo_funcs[index](canvas); @@ -123,123 +136,178 @@ const uint16_t get_file_color(const String &filename) { } } -// void select_file(String path) { -// // if (path.endsWith(".rom") || path.endsWith(".nes")) { -// // char *argv[1]; -// // char fullFilename[256]; -// // strcpy(fullFilename, path.c_str()); -// // argv[0] = fullFilename; -// // -// // Serial.print("NoFrendo start! Filename: "); -// // Serial.println(argv[0]); -// // nofrendo_main(1, argv); -// // Serial.println("NoFrendo end!\n"); -// // } else if (path.endsWith(".bin")) { -// // int error; -// // error = lilka::multiboot.start(path); -// // if (error) { -// // lilka::ui_alert(canvas, "Помилка", String("Етап: 1\nКод: ") + error); -// // return; -// // } -// // lilka::display.fillScreen(lilka::display.color565(0, 0, 0)); -// // lilka::display.setTextColor(lilka::display.color565(255, 255, 255), lilka::display.color565(0, 0, 0)); -// // lilka::display.setFont(u8g2_font_10x20_t_cyrillic); -// // lilka::display.setTextBound(16, 0, LILKA_DISPLAY_WIDTH - 16, LILKA_DISPLAY_HEIGHT); -// // while ((error = lilka::multiboot.process()) != 0) { -// // float progress = (float)lilka::multiboot.getBytesWritten() / lilka::multiboot.getBytesTotal(); -// // lilka::display.setCursor(16, LILKA_DISPLAY_HEIGHT / 2 - 10); -// // lilka::display.printf("Завантаження (%d%%)\n", (int)(progress * 100)); -// // lilka::display.println(path); -// // String buf = String(lilka::multiboot.getBytesWritten()) + " / " + lilka::multiboot.getBytesTotal(); -// // int16_t x, y; -// // uint16_t w, h; -// // lilka::display.getTextBounds( -// // buf, lilka::display.getCursorX(), lilka::display.getCursorY(), &x, &y, &w, &h -// // ); -// // lilka::display.fillRect(x, y, w, h, lilka::display.color565(0, 0, 0)); -// // lilka::display.println(buf); -// // lilka::display.fillRect( -// // 16, LILKA_DISPLAY_HEIGHT / 2 + 40, LILKA_DISPLAY_WIDTH - 32, 5, lilka::display.color565(64, 64, 64) -// // ); -// // lilka::display.fillRect( -// // 16, LILKA_DISPLAY_HEIGHT / 2 + 40, (LILKA_DISPLAY_WIDTH - 32) * progress, 5, -// // lilka::display.color565(255, 128, 0) -// // ); -// // } -// // if (error) { -// // lilka::ui_alert(canvas, "Помилка", String("Етап: 2\nКод: ") + error); -// // return; -// // } -// // error = lilka::multiboot.finishAndReboot(); -// // if (error) { -// // lilka::ui_alert(canvas, "Помилка", String("Етап: 3\nКод: ") + error); -// // return; -// // } -// // } else if (path.endsWith(".lua")) { -// // int retCode = lilka::lua_runfile(canvas, path); -// // if (retCode) { -// // lilka::ui_alert(canvas, "Lua", String("Увага!\nКод завершення: ") + retCode); -// // } -// // } else if (path.endsWith(".js")) { -// // int retCode = lilka::mjs_run(canvas, path); -// // if (retCode) { -// // lilka::ui_alert(canvas, "Lua", String("Увага!\nКод завершення: ") + retCode); -// // } -// // } else { -// // // Get file size -// // FILE *file = fopen(path.c_str(), "r"); -// // if (!file) { -// // lilka::ui_alert(canvas, "Помилка", "Не вдалося відкрити файл"); -// // return; -// // } -// // fseek(file, 0, SEEK_END); -// // long size = ftell(file); -// // fclose(file); -// // lilka::ui_alert(canvas, path, String("Розмір:\n") + size + " байт"); -// // } -// } +void LauncherApp::sdBrowserMenu(String path) { + if (!lilka::sdcard.available()) { + lilka::Alert alert("Помилка", "SD-карта не знайдена"); + alert.draw(canvas); + queueDraw(); + while (1) { + alert.update(); + if (alert.isDone()) { + break; + } + taskYIELD(); + } + } + + lilka::Entry entries + [32]; // TODO - allocate dynamically (increasing to 64 causes task stack overflow which is limited by ARDUINO_LOOP_STACK_SIZE) + int numEntries = lilka::sdcard.listDir(path, entries); + + if (numEntries == -1) { + // lilka::ui_alert(canvas, "Помилка", "Не вдалося прочитати директорію"); + lilka::Alert alert("Помилка", "Не вдалося прочитати директорію"); + alert.draw(canvas); + queueDraw(); + while (1) { + alert.update(); + if (alert.isDone()) { + break; + } + taskYIELD(); + } + return; + } + + String filenames[32]; + const menu_icon_t *icons[32]; + uint16_t colors[32]; + for (int i = 0; i < numEntries; i++) { + filenames[i] = entries[i].name; + icons[i] = entries[i].type == lilka::EntryType::ENT_DIRECTORY ? &folder : get_file_icon(filenames[i]); + colors[i] = entries[i].type == lilka::EntryType::ENT_DIRECTORY ? lilka::display.color565(255, 255, 200) + : get_file_color(filenames[i]); + } + filenames[numEntries++] = "<< Назад"; + icons[numEntries - 1] = 0; + colors[numEntries - 1] = 0; + + lilka::Menu menu("SD: " + path); + for (int i = 0; i < numEntries; i++) { + 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; + } + // cursor = lilka::ui_menu(canvas, String("SD: ") + path, filenames, numEntries, cursor, icons, colors); + // if (cursor == numEntries - 1) { + // return; + // } + if (entries[index].type == lilka::EntryType::ENT_DIRECTORY) { + sdBrowserMenu(path + entries[index].name + "/"); + } else { + selectFile(lilka::sdcard.abspath(path + entries[index].name)); + } + } + taskYIELD(); + } +} + +void LauncherApp::selectFile(String path) { + if (path.endsWith(".rom") || path.endsWith(".nes")) { + return; + // char *argv[1]; + // char fullFilename[256]; + // strcpy(fullFilename, path.c_str()); + // argv[0] = fullFilename; + // + // Serial.print("NoFrendo start! Filename: "); + // Serial.println(argv[0]); + // nofrendo_main(1, argv); + // Serial.println("NoFrendo end!\n"); + } else if (path.endsWith(".bin")) { + return; + // int error; + // error = lilka::multiboot.start(path); + // if (error) { + // lilka::ui_alert(canvas, "Помилка", String("Етап: 1\nКод: ") + error); + // return; + // } + // lilka::display.fillScreen(lilka::display.color565(0, 0, 0)); + // lilka::display.setTextColor(lilka::display.color565(255, 255, 255), lilka::display.color565(0, 0, 0)); + // lilka::display.setFont(u8g2_font_10x20_t_cyrillic); + // lilka::display.setTextBound(16, 0, LILKA_DISPLAY_WIDTH - 16, LILKA_DISPLAY_HEIGHT); + // while ((error = lilka::multiboot.process()) != 0) { + // float progress = (float)lilka::multiboot.getBytesWritten() / lilka::multiboot.getBytesTotal(); + // lilka::display.setCursor(16, LILKA_DISPLAY_HEIGHT / 2 - 10); + // lilka::display.printf("Завантаження (%d%%)\n", (int)(progress * 100)); + // lilka::display.println(path); + // String buf = String(lilka::multiboot.getBytesWritten()) + " / " + lilka::multiboot.getBytesTotal(); + // int16_t x, y; + // uint16_t w, h; + // lilka::display.getTextBounds(buf, lilka::display.getCursorX(), lilka::display.getCursorY(), &x, &y, &w, &h); + // lilka::display.fillRect(x, y, w, h, lilka::display.color565(0, 0, 0)); + // lilka::display.println(buf); + // lilka::display.fillRect( + // 16, LILKA_DISPLAY_HEIGHT / 2 + 40, LILKA_DISPLAY_WIDTH - 32, 5, lilka::display.color565(64, 64, 64) + // ); + // lilka::display.fillRect( + // 16, LILKA_DISPLAY_HEIGHT / 2 + 40, (LILKA_DISPLAY_WIDTH - 32) * progress, 5, + // lilka::display.color565(255, 128, 0) + // ); + // } + // if (error) { + // lilka::ui_alert(canvas, "Помилка", String("Етап: 2\nКод: ") + error); + // return; + // } + // error = lilka::multiboot.finishAndReboot(); + // if (error) { + // lilka::ui_alert(canvas, "Помилка", String("Етап: 3\nКод: ") + error); + // return; + // } + } else if (path.endsWith(".lua")) { + AppManager::getInstance()->addApp(new LuaFileRunnerApp(path)); + // int retCode = lilka::lua_runfile(canvas, path); + // if (retCode) { + // lilka::ui_alert(canvas, "Lua", String("Увага!\nКод завершення: ") + retCode); + // } + } else if (path.endsWith(".js")) { + return; + // int retCode = lilka::mjs_run(canvas, path); + // if (retCode) { + // lilka::ui_alert(canvas, "Lua", String("Увага!\nКод завершення: ") + retCode); + // } + } else { + // Get file size + FILE *file = fopen(path.c_str(), "r"); + if (!file) { + lilka::Alert alert("Помилка", "Не вдалося відкрити файл"); + alert.draw(canvas); + queueDraw(); + while (1) { + alert.update(); + if (alert.isDone()) { + break; + } + taskYIELD(); + } + return; + } + fseek(file, 0, SEEK_END); + long size = ftell(file); + fclose(file); + // lilka::ui_alert(canvas, path, String("Розмір:\n") + size + " байт"); + lilka::Alert alert(path, String("Розмір:\n") + size + " байт"); + alert.draw(canvas); + queueDraw(); + while (1) { + alert.update(); + if (alert.isDone()) { + break; + } + taskYIELD(); + } + } +} + // -// void sd_browser_menu(String path) { -// // if (!lilka::sdcard.available()) { -// // lilka::ui_alert(canvas, "Помилка", "SD-карта не знайдена"); -// // return; -// // } -// // -// // lilka::Entry entries -// // [32]; // TODO - allocate dynamically (increasing to 64 causes task stack overflow which is limited by ARDUINO_LOOP_STACK_SIZE) -// // int numEntries = lilka::sdcard.listDir(path, entries); -// // -// // if (numEntries == -1) { -// // lilka::ui_alert(canvas, "Помилка", "Не вдалося прочитати директорію"); -// // return; -// // } -// // -// // String filenames[32]; -// // const menu_icon_t *icons[32]; -// // uint16_t colors[32]; -// // for (int i = 0; i < numEntries; i++) { -// // filenames[i] = entries[i].name; -// // icons[i] = entries[i].type == lilka::EntryType::ENT_DIRECTORY ? &folder : get_file_icon(filenames[i]); -// // colors[i] = entries[i].type == lilka::EntryType::ENT_DIRECTORY ? lilka::display.color565(255, 255, 200) -// // : get_file_color(filenames[i]); -// // } -// // filenames[numEntries++] = "<< Назад"; -// // icons[numEntries - 1] = 0; -// // colors[numEntries - 1] = 0; -// // -// // int cursor = 0; -// // while (1) { -// // cursor = lilka::ui_menu(canvas, String("SD: ") + path, filenames, numEntries, cursor, icons, colors); -// // if (cursor == numEntries - 1) { -// // return; -// // } -// // if (entries[cursor].type == lilka::EntryType::ENT_DIRECTORY) { -// // sd_browser_menu(path + entries[cursor].name + "/"); -// // } else { -// // select_file(lilka::sdcard.abspath(path + entries[cursor].name)); -// // } -// // } -// } // // void spiffs_browser_menu() { // // if (!lilka::filesystem.available()) { diff --git a/firmware/main/src/apps/launcher.h b/firmware/main/src/apps/launcher.h index 9a61db69..4a6414e9 100644 --- a/firmware/main/src/apps/launcher.h +++ b/firmware/main/src/apps/launcher.h @@ -7,4 +7,6 @@ class LauncherApp : public App { LauncherApp(); void run(); void demosMenu(); + void sdBrowserMenu(String path); + void selectFile(String path); }; diff --git a/firmware/main/src/lua/lualilka_console.cpp b/firmware/main/src/apps/lua/lualilka_console.cpp similarity index 100% rename from firmware/main/src/lua/lualilka_console.cpp rename to firmware/main/src/apps/lua/lualilka_console.cpp diff --git a/firmware/main/src/lua/lualilka_console.h b/firmware/main/src/apps/lua/lualilka_console.h similarity index 100% rename from firmware/main/src/lua/lualilka_console.h rename to firmware/main/src/apps/lua/lualilka_console.h diff --git a/firmware/main/src/lua/lualilka_controller.cpp b/firmware/main/src/apps/lua/lualilka_controller.cpp similarity index 100% rename from firmware/main/src/lua/lualilka_controller.cpp rename to firmware/main/src/apps/lua/lualilka_controller.cpp diff --git a/firmware/main/src/lua/lualilka_controller.h b/firmware/main/src/apps/lua/lualilka_controller.h similarity index 100% rename from firmware/main/src/lua/lualilka_controller.h rename to firmware/main/src/apps/lua/lualilka_controller.h diff --git a/firmware/main/src/lua/lualilka_display.cpp b/firmware/main/src/apps/lua/lualilka_display.cpp similarity index 91% rename from firmware/main/src/lua/lualilka_display.cpp rename to firmware/main/src/apps/lua/lualilka_display.cpp index 51962a63..1dea27a7 100644 --- a/firmware/main/src/lua/lualilka_display.cpp +++ b/firmware/main/src/apps/lua/lualilka_display.cpp @@ -1,4 +1,5 @@ #include "lualilka_display.h" +#include "app.h" Arduino_GFX* getDrawable(lua_State* L) { // // Check if display is buffered. @@ -18,11 +19,14 @@ Arduino_GFX* getDrawable(lua_State* L) { // return &display; // } - // TODO: Buffering is ignored for now (maybe forever, will update docs) - lua_getfield(L, LUA_REGISTRYINDEX, "canvas"); - lilka::Canvas* canvas = (lilka::Canvas*)lua_touserdata(L, -1); - lua_pop(L, 1); - return canvas; + // // TODO: Buffering is ignored for now (maybe forever, will update docs) + // lua_getfield(L, LUA_REGISTRYINDEX, "canvas"); + // lilka::Canvas* canvas = (lilka::Canvas*)lua_touserdata(L, -1); + // lua_pop(L, 1); + + lua_getfield(L, LUA_REGISTRYINDEX, "app"); + App* app = (App*)lua_touserdata(L, -1); + return app->canvas; } int lualilka_display_setBuffered(lua_State* L) { @@ -269,17 +273,18 @@ int lualilka_display_drawImage(lua_State* L) { int lualilka_display_render(lua_State* L) { // Check if display is buffered - lua_getfield(L, LUA_REGISTRYINDEX, "isBuffered"); - bool isBuffered = lua_toboolean(L, -1); - lua_pop(L, 1); - if (!isBuffered) { - // Throw error - return luaL_error(L, "Буферизація вимкнена, використовуйте display.render() тільки з буферизацією"); - } - lua_getfield(L, LUA_REGISTRYINDEX, "canvas"); - lilka::Canvas* canvas = (lilka::Canvas*)lua_touserdata(L, -1); - lua_pop(L, 1); - lilka::display.renderCanvas(canvas); + // TODO: FreeRTOS experiment + // lua_getfield(L, LUA_REGISTRYINDEX, "isBuffered"); + // bool isBuffered = lua_toboolean(L, -1); + // lua_pop(L, 1); + // if (!isBuffered) { + // // Throw error + // return luaL_error(L, "Буферизація вимкнена, використовуйте display.render() тільки з буферизацією"); + // } + // lua_getfield(L, LUA_REGISTRYINDEX, "canvas"); + // lilka::Canvas* canvas = (lilka::Canvas*)lua_touserdata(L, -1); + // lua_pop(L, 1); + // lilka::display.renderCanvas(canvas); return 0; } diff --git a/firmware/main/src/lua/lualilka_display.h b/firmware/main/src/apps/lua/lualilka_display.h similarity index 100% rename from firmware/main/src/lua/lualilka_display.h rename to firmware/main/src/apps/lua/lualilka_display.h diff --git a/firmware/main/src/lua/lualilka_geometry.cpp b/firmware/main/src/apps/lua/lualilka_geometry.cpp similarity index 100% rename from firmware/main/src/lua/lualilka_geometry.cpp rename to firmware/main/src/apps/lua/lualilka_geometry.cpp diff --git a/firmware/main/src/lua/lualilka_geometry.h b/firmware/main/src/apps/lua/lualilka_geometry.h similarity index 100% rename from firmware/main/src/lua/lualilka_geometry.h rename to firmware/main/src/apps/lua/lualilka_geometry.h diff --git a/firmware/main/src/lua/lualilka_gpio.cpp b/firmware/main/src/apps/lua/lualilka_gpio.cpp similarity index 100% rename from firmware/main/src/lua/lualilka_gpio.cpp rename to firmware/main/src/apps/lua/lualilka_gpio.cpp diff --git a/firmware/main/src/lua/lualilka_gpio.h b/firmware/main/src/apps/lua/lualilka_gpio.h similarity index 100% rename from firmware/main/src/lua/lualilka_gpio.h rename to firmware/main/src/apps/lua/lualilka_gpio.h diff --git a/firmware/main/src/lua/lualilka_math.cpp b/firmware/main/src/apps/lua/lualilka_math.cpp similarity index 100% rename from firmware/main/src/lua/lualilka_math.cpp rename to firmware/main/src/apps/lua/lualilka_math.cpp diff --git a/firmware/main/src/lua/lualilka_math.h b/firmware/main/src/apps/lua/lualilka_math.h similarity index 100% rename from firmware/main/src/lua/lualilka_math.h rename to firmware/main/src/apps/lua/lualilka_math.h diff --git a/firmware/main/src/lua/lualilka_resources.cpp b/firmware/main/src/apps/lua/lualilka_resources.cpp similarity index 100% rename from firmware/main/src/lua/lualilka_resources.cpp rename to firmware/main/src/apps/lua/lualilka_resources.cpp diff --git a/firmware/main/src/lua/lualilka_resources.h b/firmware/main/src/apps/lua/lualilka_resources.h similarity index 100% rename from firmware/main/src/lua/lualilka_resources.h rename to firmware/main/src/apps/lua/lualilka_resources.h diff --git a/firmware/main/src/lua/lualilka_state.cpp b/firmware/main/src/apps/lua/lualilka_state.cpp similarity index 100% rename from firmware/main/src/lua/lualilka_state.cpp rename to firmware/main/src/apps/lua/lualilka_state.cpp diff --git a/firmware/main/src/lua/lualilka_state.h b/firmware/main/src/apps/lua/lualilka_state.h similarity index 100% rename from firmware/main/src/lua/lualilka_state.h rename to firmware/main/src/apps/lua/lualilka_state.h diff --git a/firmware/main/src/lua/lualilka_util.cpp b/firmware/main/src/apps/lua/lualilka_util.cpp similarity index 91% rename from firmware/main/src/lua/lualilka_util.cpp rename to firmware/main/src/apps/lua/lualilka_util.cpp index d7dc1f2b..cdc49b1a 100644 --- a/firmware/main/src/lua/lualilka_util.cpp +++ b/firmware/main/src/apps/lua/lualilka_util.cpp @@ -12,7 +12,8 @@ int lualilka_util_time(lua_State* L) { int lualilka_util_sleep(lua_State* L) { float s = luaL_checknumber(L, 1); - delayMicroseconds(s * 1000000); + // delayMicroseconds(s * 1000000); + vTaskDelay(s * 1000 / portTICK_PERIOD_MS); return 0; } diff --git a/firmware/main/src/lua/lualilka_util.h b/firmware/main/src/apps/lua/lualilka_util.h similarity index 100% rename from firmware/main/src/lua/lualilka_util.h rename to firmware/main/src/apps/lua/lualilka_util.h diff --git a/firmware/main/src/lua/luarunner.cpp b/firmware/main/src/apps/lua/luarunner.cpp similarity index 70% rename from firmware/main/src/lua/luarunner.cpp rename to firmware/main/src/apps/lua/luarunner.cpp index 0fc52c4b..2a92fe07 100644 --- a/firmware/main/src/lua/luarunner.cpp +++ b/firmware/main/src/apps/lua/luarunner.cpp @@ -4,6 +4,7 @@ #include "clib/u8g2.h" #include +#include "luarunner.h" #include "lualilka_display.h" #include "lualilka_console.h" #include "lualilka_controller.h" @@ -14,8 +15,6 @@ #include "lualilka_util.h" #include "lualilka_state.h" -namespace lilka { - jmp_buf stopjmp; bool pushLilka(lua_State* L) { @@ -91,80 +90,12 @@ bool callDraw(lua_State* L) { return true; } -int execute(lua_State* L) { - // Calls Lua code that's on top of the stack (previously loaded with luaL_loadfile or luaL_loadstring) - - int jmpCode = setjmp(stopjmp); - - if (jmpCode == 0) { - // Run script - int retCode = lua_pcall(L, 0, LUA_MULTRET, 0); - if (retCode) { - longjmp(stopjmp, retCode); - } - - // Get canvas from registry - lua_getfield(L, LUA_REGISTRYINDEX, "canvas"); - Canvas* canvas = (Canvas*)lua_touserdata(L, -1); - lua_pop(L, 1); - - if (!pushLilka(L)) { - // No lilka table - we're done - lua_pop(L, 1); - longjmp(stopjmp, 32); - } - - // Check if lilka.init function exists and call it - callInit(L); - - // Check if lilka.update function exists and call it - const uint32_t perfectDelta = 1000 / 30; - uint32_t delta = perfectDelta; // Delta for first frame is always 1/30 - while (true) { - uint32_t now = millis(); - - if (!callUpdate(L, delta) || !callDraw(L)) { - // No update or draw function - we're done - longjmp(stopjmp, 32); - serial_log("lua: no update or draw function"); - } - - // Check if show_fps is true and render FPS - lua_getfield(L, -1, "show_fps"); - if (lua_toboolean(L, -1)) { - canvas->setCursor(24, 24); - canvas->setTextColor(0xFFFF, 0); - canvas->print(String("FPS: ") + (1000 / (delta > 0 ? delta : 1)) + " "); - } - lua_pop(L, 1); - - display.renderCanvas(canvas); - - // Calculate time spent in update - uint32_t elapsed = millis() - now; - // If we're too fast, delay to keep 30 FPS - if (elapsed < perfectDelta) { - delay(perfectDelta - elapsed); - delta = perfectDelta; - } else { - // If we're too slow, don't delay and set delta to elapsed time - delta = elapsed; - } - } - } +AbstractLuaRunnerApp::AbstractLuaRunnerApp(const char* appName) : App(appName) {} - int retCode = jmpCode; - if (retCode == 32) { - // Normal exit through longjmp - return 0; - } - return retCode; -} - -lua_State* lua_setup(const char* dir) { +void AbstractLuaRunnerApp::luaSetup(const char* dir) { lilka::serial_log("lua: script dir: %s", dir); - lua_State* L = luaL_newstate(); + L = luaL_newstate(); lilka::serial_log("lua: init libs"); luaL_openlibs(L); @@ -196,14 +127,14 @@ lua_State* lua_setup(const char* dir) { lualilka_gpio_register(L); lualilka_util_register(L); - lilka::serial_log("lua: init canvas"); - lilka::Canvas* canvas = new lilka::Canvas(); - lilka::display.setFont(FONT_10x20); - canvas->setFont(FONT_10x20); - canvas->begin(); + // lilka::serial_log("lua: init canvas"); + // lilka::Canvas* canvas = new lilka::Canvas(); + // lilka::display.setFont(FONT_10x20); + // canvas->setFont(FONT_10x20); + // canvas->begin(); // Store canvas in registry with "canvas" key - lua_pushlightuserdata(L, canvas); - lua_setfield(L, LUA_REGISTRYINDEX, "canvas"); + lua_pushlightuserdata(L, this); + lua_setfield(L, LUA_REGISTRYINDEX, "app"); // Initialize table for image pointers lilka::serial_log("lua: init memory for images"); lua_newtable(L); @@ -215,11 +146,9 @@ lua_State* lua_setup(const char* dir) { lua_pushboolean(L, false); lua_setfield(L, -2, "show_fps"); lua_setglobal(L, "lilka"); - - return L; } -void lua_teardown(lua_State* L) { +void AbstractLuaRunnerApp::luaTeardown() { lilka::serial_log("lua: cleanup"); // Free images from registry @@ -232,18 +161,92 @@ void lua_teardown(lua_State* L) { } // Free canvas from registry - lilka::Canvas* canvas = (lilka::Canvas*)lua_touserdata(L, lua_getfield(L, LUA_REGISTRYINDEX, "canvas")); - delete canvas; + // lilka::Canvas* canvas = (lilka::Canvas*)lua_touserdata(L, lua_getfield(L, LUA_REGISTRYINDEX, "canvas")); + // delete canvas; lua_close(L); } -int lua_runfile(Canvas *canvas, String path) { +int AbstractLuaRunnerApp::execute() { + // Calls Lua code that's on top of the stack (previously loaded with luaL_loadfile or luaL_loadstring) + + int jmpCode = setjmp(stopjmp); + + if (jmpCode == 0) { + // Run script + int retCode = lua_pcall(L, 0, LUA_MULTRET, 0); + if (retCode) { + longjmp(stopjmp, retCode); + } + + // Get canvas from registry + // lua_getfield(L, LUA_REGISTRYINDEX, "canvas"); + // lilka::Canvas* canvas = (lilka::Canvas*)lua_touserdata(L, -1); + // lua_pop(L, 1); + + if (!pushLilka(L)) { + // No lilka table - we're done + lua_pop(L, 1); + longjmp(stopjmp, 32); + } + + // Check if lilka.init function exists and call it + callInit(L); + + // Check if lilka.update function exists and call it + const uint32_t perfectDelta = 1000 / 30; + uint32_t delta = perfectDelta; // Delta for first frame is always 1/30 + while (true) { + uint32_t now = millis(); + + if (!callUpdate(L, delta) || !callDraw(L)) { + // No update or draw function - we're done + longjmp(stopjmp, 32); + lilka::serial_log("lua: no update or draw function"); + } + + // Check if show_fps is true and render FPS + lua_getfield(L, -1, "show_fps"); + if (lua_toboolean(L, -1)) { + canvas->setCursor(24, 24); + canvas->setTextColor(0xFFFF, 0); + canvas->print(String("FPS: ") + (1000 / (delta > 0 ? delta : 1)) + " "); + } + lua_pop(L, 1); + queueDraw(); + + // display.renderCanvas(canvas); + + // Calculate time spent in update + uint32_t elapsed = millis() - now; + // If we're too fast, delay to keep 30 FPS + if (elapsed < perfectDelta) { + vTaskDelay((perfectDelta - elapsed) / portTICK_PERIOD_MS); + // delay(perfectDelta - elapsed); + delta = perfectDelta; + } else { + // If we're too slow, don't delay and set delta to elapsed time + delta = elapsed; + } + } + } + + int retCode = jmpCode; + if (retCode == 32) { + // Normal exit through longjmp + return 0; + } + return retCode; +} + +LuaFileRunnerApp::LuaFileRunnerApp(String path) : AbstractLuaRunnerApp("Lua file run"), path(path) {} + +void LuaFileRunnerApp::run() { #ifndef LILKA_NO_LUA // Get dir name from path (without the trailing slash) String dir = path.substring(0, path.lastIndexOf('/')); - lua_State* L = lua_setup(dir.c_str()); + luaSetup(dir.c_str()); // Load state from file (file name is "path" with .lua replaced with .state) String statePath = path.substring(0, path.lastIndexOf('.')) + ".state"; @@ -256,11 +259,17 @@ int lua_runfile(Canvas *canvas, String path) { lilka::serial_log("lua: run file"); - int retCode = luaL_loadfile(L, path.c_str()) || execute(L); + int retCode = luaL_loadfile(L, path.c_str()) || execute(); if (retCode) { const char* err = lua_tostring(L, -1); - lilka::ui_alert(canvas, "Lua", String("Помилка: ") + err); + // lilka::ui_alert(canvas, "Lua", String("Помилка: ") + err); + lilka::Alert alert("Lua", String("Помилка: ") + err); + while (!alert.isDone()) { + alert.update(); + alert.draw(canvas); + queueDraw(); + } } // Check if state table exists and save it to file if so @@ -274,78 +283,76 @@ int lua_runfile(Canvas *canvas, String path) { lilka::serial_log("lua: no state to save"); } - lua_teardown(L); + luaTeardown(); - return retCode; -#else - ui_alert(canvas, "Помилка", "Lua не підтримується"); - return -1; + // return retCode; #endif } -int lua_runsource(Canvas* canvas, String source) { +LuaSourceRunnerApp::LuaSourceRunnerApp(String source) : AbstractLuaRunnerApp("Lua source run"), source(source) {} + +void LuaSourceRunnerApp::run() { #ifndef LILKA_NO_LUA - lua_State* L = lua_setup("/sd"); // TODO: hard-coded + luaSetup("/sd"); // TODO: hard-coded lilka::serial_log("lua: run source"); - int retCode = luaL_loadstring(L, source.c_str()) || execute(L); + int retCode = luaL_loadstring(L, source.c_str()) || execute(); if (retCode) { const char* err = lua_tostring(L, -1); - lilka::ui_alert(canvas, "Lua", String("Помилка: ") + err); - } - - lua_teardown(L); - - return retCode; -#else - ui_alert(canvas, "Помилка", "Lua не підтримується"); - return -1; -#endif -} - -lua_State* Lrepl; - -int lua_repl_start() { -#ifndef LILKA_NO_LUA - Lrepl = lua_setup("/sd"); // TODO: hard-coded - - lilka::serial_log("lua: start REPL"); - return 0; -#else - ui_alert(canvas, "Помилка", "Lua не підтримується"); - return -1; -#endif -} - -int lua_repl_input(String input) { -#ifndef LILKA_NO_LUA - // lilka::serial_log("lua: input: %s", input.c_str()); - - int retCode = luaL_loadstring(Lrepl, input.c_str()) || execute(Lrepl); - - if (retCode) { - const char* err = lua_tostring(Lrepl, -1); - serial_log("lua: error: %s", err); + // lilka::ui_alert(canvas, "Lua", String("Помилка: ") + err); + lilka::Alert alert("Lua", String("Помилка: ") + err); + while (!alert.isDone()) { + alert.update(); + alert.draw(canvas); + queueDraw(); + } } - return retCode; -#else - ui_alert(canvas, "Помилка", "Lua не підтримується"); - return -1; -#endif -} - -int lua_repl_stop() { -#ifndef LILKA_NO_LUA - lilka::serial_log("lua: stop REPL"); - lua_teardown(Lrepl); - return 0; -#else - ui_alert(canvas, "Помилка", "Lua не підтримується"); - return -1; + luaTeardown(); #endif } -} // namespace lilka +// lua_State* Lrepl; +// +// int lua_repl_start() { +// #ifndef LILKA_NO_LUA +// Lrepl = lua_setup("/sd"); // TODO: hard-coded +// +// lilka::serial_log("lua: start REPL"); +// return 0; +// #else +// ui_alert(canvas, "Помилка", "Lua не підтримується"); +// return -1; +// #endif +// } +// +// int lua_repl_input(String input) { +// #ifndef LILKA_NO_LUA +// // lilka::serial_log("lua: input: %s", input.c_str()); +// +// int retCode = luaL_loadstring(Lrepl, input.c_str()) || execute(Lrepl); +// +// if (retCode) { +// const char* err = lua_tostring(Lrepl, -1); +// serial_log("lua: error: %s", err); +// } +// +// return retCode; +// #else +// ui_alert(canvas, "Помилка", "Lua не підтримується"); +// return -1; +// #endif +// } +// +// int lua_repl_stop() { +// #ifndef LILKA_NO_LUA +// lilka::serial_log("lua: stop REPL"); +// lua_teardown(Lrepl); +// return 0; +// #else +// ui_alert(canvas, "Помилка", "Lua не підтримується"); +// return -1; +// #endif +// } diff --git a/firmware/main/src/apps/lua/luarunner.h b/firmware/main/src/apps/lua/luarunner.h new file mode 100644 index 00000000..6dff9f76 --- /dev/null +++ b/firmware/main/src/apps/lua/luarunner.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include "app.h" + +class AbstractLuaRunnerApp : public App { +public: + AbstractLuaRunnerApp(const char *name); + virtual void run() = 0; + +protected: + void luaSetup(const char *dir); + void luaTeardown(); + int execute(); + lua_State *L; +}; + +class LuaFileRunnerApp : public AbstractLuaRunnerApp { +public: + LuaFileRunnerApp(String path); + void run(); + +private: + String path; +}; + +class LuaSourceRunnerApp : public AbstractLuaRunnerApp { +public: + LuaSourceRunnerApp(String source); + void run(); + +private: + String source; +}; + +// class LuaReplApp : public AbstractLuaRunnerApp { +// LuaReplApp(); +// void run(); +// // input(String input); +// // stop(); +// }; + +// int lua_runfile(lilka::Canvas *canvas, String path); +// int lua_runsource(lilka::Canvas *canvas, String source); +// int lua_repl_start(); +// int lua_repl_input(String input); +// int lua_repl_stop(); diff --git a/firmware/main/src/apps/hud.cpp b/firmware/main/src/apps/statusbar.cpp similarity index 85% rename from firmware/main/src/apps/hud.cpp rename to firmware/main/src/apps/statusbar.cpp index bd0e0c2c..50db1143 100644 --- a/firmware/main/src/apps/hud.cpp +++ b/firmware/main/src/apps/statusbar.cpp @@ -1,10 +1,10 @@ -#include "hud.h" +#include "statusbar.h" #include "icons/battery.h" #include "icons/wifi.h" -HUDApp::HUDApp() : App("HUD", 0, 0, LILKA_DISPLAY_WIDTH, 24) {} +StatusBarApp::StatusBarApp() : App("StatusBar", 0, 0, LILKA_DISPLAY_WIDTH, 24) {} -void HUDApp::run() { +void StatusBarApp::run() { int counter = 0; while (1) { canvas->fillScreen(lilka::display.color565(0, 0, 0)); diff --git a/firmware/main/src/apps/statusbar.h b/firmware/main/src/apps/statusbar.h new file mode 100644 index 00000000..33073ee8 --- /dev/null +++ b/firmware/main/src/apps/statusbar.h @@ -0,0 +1,9 @@ +#pragma once + +#include "app.h" + +class StatusBarApp : public App { +public: + StatusBarApp(); + void run(); +}; diff --git a/firmware/main/src/lua/luarunner.h b/firmware/main/src/lua/luarunner.h deleted file mode 100644 index 3a57a365..00000000 --- a/firmware/main/src/lua/luarunner.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include "../app.h" - -class LuaRunnerApp : public App { - LuaRunnerApp(); - void run(); -}; - -class LuaFileRunnerApp : public LuaRunnerApp { - LuaFileRunnerApp(String path); -}; - -class LuaSourceRunnerApp : public LuaRunnerApp { - LuaSourceRunnerApp(String source); -}; - -class LuaReplApp : public LuaRunnerApp { - LuaReplApp(); - // input(String input); - // stop(); -}; - -// int lua_runfile(lilka::Canvas *canvas, String path); -// int lua_runsource(lilka::Canvas *canvas, String source); -// int lua_repl_start(); -// int lua_repl_input(String input); -// int lua_repl_stop(); diff --git a/firmware/main/src/main.cpp b/firmware/main/src/main.cpp index 8a929950..dccf22c5 100644 --- a/firmware/main/src/main.cpp +++ b/firmware/main/src/main.cpp @@ -15,14 +15,14 @@ #include "appmanager.h" #include "app.h" -#include "apps/hud.h" +#include "apps/statusbar.h" #include "apps/launcher.h" AppManager *appManager = AppManager::getInstance(); void setup() { lilka::begin(); - appManager->setPanel(new HUDApp()); + appManager->setPanel(new StatusBarApp()); appManager->addApp(new LauncherApp()); } diff --git a/sdk/lib/lilka/src/lilka.cpp b/sdk/lib/lilka/src/lilka.cpp index 5881f9fd..ccb7b2a4 100644 --- a/sdk/lib/lilka/src/lilka.cpp +++ b/sdk/lib/lilka/src/lilka.cpp @@ -7,7 +7,6 @@ namespace lilka { void begin() { serial_begin(); - delay(1000); multiboot.begin(); spi_begin(); buzzer.begin(); // Play notification sound diff --git a/sdk/lib/lilka/src/lilka/ui.cpp b/sdk/lib/lilka/src/lilka/ui.cpp index 69236ce9..4338e5b6 100644 --- a/sdk/lib/lilka/src/lilka/ui.cpp +++ b/sdk/lib/lilka/src/lilka/ui.cpp @@ -218,6 +218,7 @@ void Menu::draw(Arduino_GFX *canvas) { canvas->color565(255, 255, 0) ); canvas->setCursor(32, 48); + canvas->setTextBound(0, 0, canvas->width(), canvas->height()); canvas->setTextColor(canvas->color565(255, 255, 255)); canvas->setFont(FONT_6x13); canvas->setTextSize(2); @@ -294,8 +295,6 @@ void Alert::draw(Arduino_GFX *canvas) { int width = right - left; int xMargin = 4; - controller.resetState(); - canvas->fillRect(left, top, width, mid - top, canvas->color565(32, 32, 128)); canvas->setFont(FONT_6x13); canvas->setTextSize(2);