From cf5f513f102f000d514ea785821e4282166ed2db Mon Sep 17 00:00:00 2001 From: Renze Nicolai Date: Fri, 3 Jun 2022 01:30:50 +0200 Subject: [PATCH 1/8] Factory test and a couple of bugfixes --- main/CMakeLists.txt | 2 + main/appfs_wrapper.c | 13 ++- main/factory_test.c | 174 ++++++++++++++++++++++++++++++++++++ main/file_browser.c | 14 +-- main/fpga_test.c | 135 ++++++++-------------------- main/include/factory_test.h | 10 +++ main/include/fpga_test.h | 3 +- main/include/settings.h | 3 + main/include/test_common.h | 28 ++++++ main/main.c | 36 ++++---- main/menus/dev.c | 2 +- main/settings.c | 59 ++++++++++++ main/test_common.c | 65 ++++++++++++++ 13 files changed, 422 insertions(+), 122 deletions(-) create mode 100644 main/factory_test.c create mode 100644 main/include/factory_test.h create mode 100644 main/include/test_common.h create mode 100644 main/test_common.c diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index bbb07a6..f0a694e 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -20,6 +20,8 @@ idf_component_register( "menus/wifi.c" "uninstall.c" "file_browser.c" + "test_common.c" + "factory_test.c" INCLUDE_DIRS "." "include" "menus" diff --git a/main/appfs_wrapper.c b/main/appfs_wrapper.c index abcd0d5..c4c022d 100644 --- a/main/appfs_wrapper.c +++ b/main/appfs_wrapper.c @@ -72,15 +72,24 @@ void appfs_store_app(pax_buf_t* pax_buffer, ILI9341* ili9341, char* path, char* res = appfsCreateFile(label, app_size, &handle); if (res != ESP_OK) { - display_boot_screen(pax_buffer, ili9341, "Failed to create on AppFS"); + display_boot_screen(pax_buffer, ili9341, "Failed to create file"); ESP_LOGE(TAG, "Failed to create file on AppFS (%d)", res); vTaskDelay(100 / portTICK_PERIOD_MS); free(app); return; } + int roundedSize=(app_size+(SPI_FLASH_MMU_PAGE_SIZE-1))&(~(SPI_FLASH_MMU_PAGE_SIZE-1)); + res = appfsErase(handle, 0, roundedSize); + if (res != ESP_OK) { + display_boot_screen(pax_buffer, ili9341, "Failed to erase file"); + ESP_LOGE(TAG, "Failed to erase file on AppFS (%d)", res); + vTaskDelay(100 / portTICK_PERIOD_MS); + free(app); + return; + } res = appfsWrite(handle, 0, app, app_size); if (res != ESP_OK) { - display_boot_screen(pax_buffer, ili9341, "Failed to write to AppFS"); + display_boot_screen(pax_buffer, ili9341, "Failed to write file"); ESP_LOGE(TAG, "Failed to write to file on AppFS (%d)", res); vTaskDelay(100 / portTICK_PERIOD_MS); free(app); diff --git a/main/factory_test.c b/main/factory_test.c new file mode 100644 index 0000000..ad286be --- /dev/null +++ b/main/factory_test.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include +#include +#include "hardware.h" +#include "ili9341.h" +#include "ice40.h" +#include "rp2040.h" +#include "fpga_test.h" +#include "pax_gfx.h" +#include "test_common.h" +#include "settings.h" +#include "ws2812.h" +#include "audio.h" + +static const char *TAG = "factory"; + +/* Test routines */ + +bool test_rp2040_init(uint32_t* rc) { + esp_err_t res = bsp_rp2040_init(); + *rc = (uint32_t) res; + return (res == ESP_OK); +} + +bool test_ice40_init(uint32_t* rc) { + esp_err_t res = bsp_ice40_init(); + *rc = (uint32_t) res; + return (res == ESP_OK); +} + +bool test_bno055_init(uint32_t* rc) { + esp_err_t res = bsp_bno055_init(); + *rc = (uint32_t) res; + return (res == ESP_OK); +} + +bool test_bme680_init(uint32_t* rc) { + esp_err_t res = bsp_bme680_init(); + *rc = (uint32_t) res; + return (res == ESP_OK); +} + +bool test_stuck_buttons(uint32_t* rc) { + RP2040* rp2040 = get_rp2040(); + uint16_t state; + esp_err_t res = rp2040_read_buttons(rp2040, &state); + if (res != ESP_OK) { + *rc = 0xFFFFFFFF; + return false; + } + + state &= ~(1 << RP2040_INPUT_FPGA_CDONE); // Ignore FPGA CDONE + + *rc = state; + + return (state == 0x0000); +} + +bool test_sd_power(uint32_t* rc) { + *rc = 0x00000000; + // Init all GPIO pins for SD card and LED + if (gpio_reset_pin(GPIO_SD_PWR) != ESP_OK) return false; + if (gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_INPUT) != ESP_OK) return false; + if (gpio_reset_pin(GPIO_SD_CMD) != ESP_OK) return false; + if (gpio_set_direction(GPIO_SD_CMD, GPIO_MODE_INPUT) != ESP_OK) return false; + if (gpio_reset_pin(GPIO_SD_CLK) != ESP_OK) return false; + if (gpio_set_direction(GPIO_SD_CLK, GPIO_MODE_INPUT) != ESP_OK) return false; + if (gpio_reset_pin(GPIO_SD_D0) != ESP_OK) return false; + if (gpio_set_direction(GPIO_SD_D0, GPIO_MODE_INPUT) != ESP_OK) return false; + if (gpio_reset_pin(GPIO_LED_DATA) != ESP_OK) return false; + if (gpio_set_direction(GPIO_LED_DATA, GPIO_MODE_INPUT) != ESP_OK) return false; + + if (gpio_get_level(GPIO_SD_PWR)) {*rc = 0x01; return false;} // Check that power enable is pulled low + + if (gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_OUTPUT) != ESP_OK) return false; + if (gpio_set_level(GPIO_SD_PWR, 1) != ESP_OK) return false; + + vTaskDelay(10 / portTICK_PERIOD_MS); + + // SD pins should be pulled high + if (!gpio_get_level(GPIO_SD_CMD)) {*rc = 0x02; return false;} + if (!gpio_get_level(GPIO_SD_CLK)) {*rc = 0x04; return false;} + if (!gpio_get_level(GPIO_SD_D0)) {*rc = 0x08; return false;} + + return true; +} + +bool run_basic_tests(pax_buf_t* pax_buffer, ILI9341* ili9341) { + const pax_font_t *font; + int line = 0; + bool ok = true; + + /* Screen init */ + font = pax_get_font("sky mono"); + + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0x8060f0); + ili9341_write(ili9341, pax_buffer->buf); + + /* Run mandatory tests */ + RUN_TEST_MANDATORY("RP2040", test_rp2040_init); + RUN_TEST_MANDATORY("ICE40", test_ice40_init); + RUN_TEST_MANDATORY("BNO055", test_bno055_init); + RUN_TEST_MANDATORY("BME680", test_bme680_init); + RUN_TEST_MANDATORY("STUCK BUTTONS", test_stuck_buttons); + RUN_TEST_MANDATORY("SD/LED POWER", test_sd_power); + + +error: + /* Fail result on screen */ + if (!ok) pax_draw_text(pax_buffer, 0xffff0000, font, 36, 0, 20*line, "FAIL"); + ili9341_write(ili9341, pax_buffer->buf); + return ok; +} + +const uint8_t led_green[15] = {50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0}; +const uint8_t led_red[15] = {0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0}; +const uint8_t led_blue[15] = {0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50}; + +void factory_test(pax_buf_t* pax_buffer, ILI9341* ili9341) { + uint8_t factory_test_done = nvs_get_u8_default("system", "factory_test", 0); + if (!factory_test_done) { + bool result; + + ESP_LOGI(TAG, "Factory test start"); + + result = run_basic_tests(pax_buffer, ili9341); + + gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_OUTPUT); + gpio_set_level(GPIO_SD_PWR, 1); + ws2812_init(GPIO_LED_DATA); + if (result) { + ws2812_send_data(led_blue, sizeof(led_blue)); + } else { + ws2812_send_data(led_red, sizeof(led_red)); + } + + if (!result) goto test_end; + + RP2040* rp2040 = get_rp2040(); + + result = run_fpga_tests(rp2040->queue, pax_buffer, ili9341); + if (!result) { + ws2812_send_data(led_red, sizeof(led_red)); + goto test_end; + } + + ws2812_send_data(led_green, sizeof(led_green)); + + // Wait for the operator to unplug the badge + test_end: + + if (result) { + esp_err_t res = nvs_set_u8_fixed("system", "factory_test", 1); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Failed to store test result %d\n", res); + result = false; + ws2812_send_data(led_red, sizeof(led_red)); + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0xa85a32); + ili9341_write(ili9341, pax_buffer->buf); + } + } + + while (true) { + if (result) play_bootsound(); + vTaskDelay(3000 / portTICK_PERIOD_MS); + } + } +} diff --git a/main/file_browser.c b/main/file_browser.c index 0a5cf91..2a931a0 100644 --- a/main/file_browser.c +++ b/main/file_browser.c @@ -15,6 +15,7 @@ #include "menu.h" #include "rp2040.h" #include "appfs_wrapper.h" +#include "bootscreen.h" static const char *TAG = "file browser"; @@ -104,20 +105,24 @@ void find_parent_dir(char* path, char* parent) { } void file_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, const char* initial_path) { + display_boot_screen(pax_buffer, ili9341, "Please wait..."); char path[512] = {0}; strncpy(path, initial_path, sizeof(path)); while (true) { menu_t* menu = menu_alloc(path); DIR* dir = opendir(path); if (dir == NULL) { - ESP_LOGE(TAG, "Failed to open directory %s", path); + if (path[0] != 0) { + ESP_LOGE(TAG, "Failed to open directory %s", path); + display_boot_screen(pax_buffer, ili9341, "Failed to open directory"); + vTaskDelay(200 / portTICK_PERIOD_MS); + } return; } struct dirent *ent; file_browser_menu_args_t* pd_args = malloc(sizeof(file_browser_menu_args_t)); pd_args->type = 'd'; find_parent_dir(path, pd_args->path); - printf("Parent dir: %s\n", pd_args->path); menu_insert_item(menu, "../", NULL, pd_args, -1); while ((ent = readdir(dir)) != NULL) { @@ -134,8 +139,6 @@ void file_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9 args->type = 'd'; } - printf("%c %s %s\r\n", args->type, ent->d_name, args->path); - snprintf(args->label, sizeof(args->label), "%s%s", ent->d_name, (args->type == 'd') ? "/" : ""); menu_insert_item(menu, args->label, NULL, args, -1); } @@ -186,7 +189,8 @@ void file_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9 if (renderbg) { pax_background(pax_buffer, 0xFFFFFF); pax_noclip(pax_buffer); - pax_draw_text(pax_buffer, 0xFF000000, NULL, 18, 5, 240 - 19, "[A] install [B] back"); + const pax_font_t *font = pax_get_font("saira regular"); + pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 19, "[A] install [B] back"); renderbg = false; } diff --git a/main/fpga_test.c b/main/fpga_test.c index a621c76..8808cc3 100644 --- a/main/fpga_test.c +++ b/main/fpga_test.c @@ -11,6 +11,7 @@ #include "rp2040.h" #include "fpga_test.h" #include "pax_gfx.h" +#include "test_common.h" extern const uint8_t fpga_selftest_bin_start[] asm("_binary_fpga_selftest_bin_start"); extern const uint8_t fpga_selftest_bin_end[] asm("_binary_fpga_selftest_bin_end"); @@ -114,7 +115,8 @@ static bool soc_message(ICE40* ice40, uint8_t cmd, uint32_t param, uint32_t *res /* Test routines */ -static bool test_bitstream_load(ICE40* ice40, uint32_t *rc) { +static bool test_bitstream_load(uint32_t *rc) { + ICE40* ice40 = get_ice40(); esp_err_t res; res = ice40_load_bitstream(ice40, fpga_selftest_bin_start, fpga_selftest_bin_end - fpga_selftest_bin_start); @@ -127,7 +129,7 @@ static bool test_bitstream_load(ICE40* ice40, uint32_t *rc) { return true; } -static bool test_spi_loopback_one(ICE40* ice40) { +static bool _test_spi_loopback_one(ICE40* ice40) { esp_err_t res; uint8_t data_tx[257]; uint8_t data_rx[258]; @@ -219,12 +221,14 @@ static bool test_spi_loopback_one(ICE40* ice40) { return true; } -static bool test_spi_loopback(ICE40* ice40, uint32_t *rc) { +static bool test_spi_loopback(uint32_t *rc) { int i; + + ICE40* ice40 = get_ice40(); /* Run test 256 times */ for (i=0; i<256; i++) { - if (!test_spi_loopback_one(ice40)) + if (!_test_spi_loopback_one(ice40)) break; } @@ -239,7 +243,9 @@ static bool test_spi_loopback(ICE40* ice40, uint32_t *rc) { return true; } -static bool test_soc_loopback(ICE40 *ice40, uint32_t *rc) { +static bool test_soc_loopback(uint32_t *rc) { + ICE40* ice40 = get_ice40(); + /* Execute command */ if (!soc_message(ice40, SOC_CMD_PING, SOC_CMD_PING_PARAM, rc, 0)) { *rc = -1; @@ -255,7 +261,9 @@ static bool test_soc_loopback(ICE40 *ice40, uint32_t *rc) { return true; } -static bool test_uart_loopback(ICE40* ice40, uint32_t *rc) { +static bool test_uart_loopback(uint32_t *rc) { + ICE40* ice40 = get_ice40(); + /* Enable loopback mode of RP2040 */ rp2040_set_fpga_loopback(get_rp2040(), true, true); vTaskDelay(pdMS_TO_TICKS(10)); @@ -273,7 +281,9 @@ static bool test_uart_loopback(ICE40* ice40, uint32_t *rc) { return *rc == SOC_RESP_OK; } -static bool test_psram(ICE40* ice40, uint32_t *rc) { +static bool test_psram(uint32_t *rc) { + ICE40* ice40 = get_ice40(); + /* Execute command */ if (!soc_message(ice40, SOC_CMD_PSRAM_TEST, 0, rc, pdMS_TO_TICKS(1000))) { *rc = -1; @@ -284,7 +294,9 @@ static bool test_psram(ICE40* ice40, uint32_t *rc) { return *rc == SOC_RESP_OK; } -static bool test_irq_n(ICE40* ice40, uint32_t *rc) { +static bool test_irq_n(uint32_t *rc) { + ICE40* ice40 = get_ice40(); + esp_err_t res; /* Set pin as input */ @@ -327,7 +339,8 @@ static bool test_irq_n(ICE40* ice40, uint32_t *rc) { return true; } -static bool test_lcd_mode(ICE40* ice40, uint32_t *rc) { +static bool test_lcd_mode(uint32_t *rc) { + ICE40* ice40 = get_ice40(); esp_err_t res; bool ok; @@ -371,7 +384,8 @@ static bool test_lcd_mode(ICE40* ice40, uint32_t *rc) { return ok; } -static bool test_pmod_open(ICE40* ice40, uint32_t *rc) { +static bool test_pmod_open(uint32_t *rc) { + ICE40* ice40 = get_ice40(); /* Execute command */ if (!soc_message(ice40, SOC_CMD_PMOD_OPEN_TEST, 0, rc, 0)) { *rc = -1; @@ -382,7 +396,9 @@ static bool test_pmod_open(ICE40* ice40, uint32_t *rc) { return *rc == SOC_RESP_OK; } -static bool test_pmod_plug(ICE40* ice40, uint32_t *rc) { +static bool test_pmod_plug(uint32_t *rc) { + ICE40* ice40 = get_ice40(); + /* Execute command */ if (!soc_message(ice40, SOC_CMD_PMOD_PLUG_TEST, 0, rc, 0)) { *rc = -1; @@ -393,7 +409,9 @@ static bool test_pmod_plug(ICE40* ice40, uint32_t *rc) { return *rc == SOC_RESP_OK; } -static bool test_lcd_init(ICE40* ice40, uint32_t *rc) { +static bool test_lcd_init(uint32_t *rc) { + ICE40* ice40 = get_ice40(); + /* Execute command */ if (!soc_message(ice40, SOC_CMD_LCD_INIT_TEST, 0, rc, 0)) { *rc = -1; @@ -404,83 +422,8 @@ static bool test_lcd_init(ICE40* ice40, uint32_t *rc) { return *rc == SOC_RESP_OK; } - -typedef bool (*test_fn)(ICE40 *ice40, uint32_t *rc); - - -static bool wait_button(xQueueHandle buttonQueue) { - rp2040_input_message_t buttonMessage = {0}; - - while (1) { - if (xQueueReceive(buttonQueue, &buttonMessage, 0) == pdTRUE) { - if (buttonMessage.state) { - switch(buttonMessage.input) { - case RP2040_INPUT_BUTTON_HOME: - case RP2040_INPUT_BUTTON_MENU: - case RP2040_INPUT_BUTTON_BACK: - return false; - case RP2040_INPUT_BUTTON_ACCEPT: - return true; - default: - break; - } - } - } else { - vTaskDelay(pdMS_TO_TICKS(10)); - } - } -} - -static bool run_test(ICE40* ice40, pax_buf_t* pax_buffer, const pax_font_t *font, ILI9341* ili9341, int line, - const char *test_name, test_fn fn) { - bool rv; - uint32_t rc; - - /* Test name */ - pax_draw_text(pax_buffer, 0xffffffff, font, 18, 0, 20*line, test_name); - if (ili9341) - ili9341_write(ili9341, pax_buffer->buf); - - /* Run the test */ - rv = fn(ice40, &rc); - - /* Display result */ - if (!rv) { - /* Error */ - char buf[10]; - snprintf(buf, sizeof(buf), "%08x", rc); - pax_draw_text(pax_buffer, 0xffff0000, font, 18, 200, 20*line, buf); - } else { - /* OK ! */ - pax_draw_text(pax_buffer, 0xff00ff00, font, 18, 200, 20*line, " OK"); - } - - if (ili9341) - ili9341_write(ili9341, pax_buffer->buf); - - /* Pass through the 'OK' status */ - return rv; -} - -#define RUN_TEST(name, fn) do {\ - ok &= run_test(ice40, pax_buffer, font, ili9341, line++, name, fn); \ -} while (0) - -#define RUN_TEST_MANDATORY(name, fn) do {\ - if (!run_test(ice40, pax_buffer, font, ili9341, line++, name, fn)) { \ - pax_draw_text(pax_buffer, 0xffff0000, font, 18, 0, 20*line, "Aborted"); \ - ili9341_write(ili9341, pax_buffer->buf); \ - ok = false; \ - goto error; \ - } \ -} while (0) - -#define RUN_TEST_BLIND(name, fn) do {\ - ok &= run_test(ice40, pax_buffer, font, NULL, line++, name, fn); \ -} while (0) - - -bool run_fpga_tests(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341) { +bool run_fpga_tests(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { + ICE40* ice40 = get_ice40(); const pax_font_t *font; int line = 0; bool ok = true; @@ -534,9 +477,7 @@ bool run_fpga_tests(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffe soc_message(ice40, SOC_CMD_LCD_RGB_CYCLE_SET, 1, NULL, 0); /* Wait for button */ - if (!wait_button(buttonQueue)) { - ok = false; - } + RUN_TEST("LCD control", test_wait_for_response); /* Stop LCD / RGB cycling */ soc_message(ice40, SOC_CMD_LCD_RGB_CYCLE_SET, 0, NULL, 0); @@ -550,9 +491,9 @@ error: /* Pass / Fail result on screen */ if (ok) - pax_draw_text(pax_buffer, 0xff00ff00, font, 36, 0, 20*line, "FPGA PASS"); + pax_draw_text(pax_buffer, 0xff00ff00, font, 36, 0, 20*line, "PASS"); else - pax_draw_text(pax_buffer, 0xffff0000, font, 36, 0, 20*line, "FPGA FAIL"); + pax_draw_text(pax_buffer, 0xffff0000, font, 36, 0, 20*line, "FAIL"); ili9341_write(ili9341, pax_buffer->buf); @@ -562,7 +503,7 @@ error: return ok; } -void fpga_test(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341) { - run_fpga_tests(buttonQueue, ice40, pax_buffer, ili9341); - wait_button(buttonQueue); +void fpga_test(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { + run_fpga_tests(buttonQueue, pax_buffer, ili9341); + test_wait_for_response(NULL); } diff --git a/main/include/factory_test.h b/main/include/factory_test.h new file mode 100644 index 0000000..5a33254 --- /dev/null +++ b/main/include/factory_test.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include +#include +#include "ili9341.h" +#include "pax_gfx.h" + +void factory_test(pax_buf_t* pax_buffer, ILI9341* ili9341); diff --git a/main/include/fpga_test.h b/main/include/fpga_test.h index 62f9e15..d49c36e 100644 --- a/main/include/fpga_test.h +++ b/main/include/fpga_test.h @@ -10,4 +10,5 @@ #include "ice40.h" #include "pax_gfx.h" -void fpga_test(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341); +void fpga_test(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341); +bool run_fpga_tests(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341); diff --git a/main/include/settings.h b/main/include/settings.h index f032a23..7581831 100644 --- a/main/include/settings.h +++ b/main/include/settings.h @@ -6,3 +6,6 @@ #include "ice40.h" esp_err_t nvs_init(); +esp_err_t nvs_get_str_fixed(const char* nvs_namespace, const char* key, char* target, size_t target_size, size_t* size); +uint8_t nvs_get_u8_default(const char* nvs_namespace, const char* key, uint8_t default_value); +esp_err_t nvs_set_u8_fixed(const char* nvs_namespace, const char* key, uint8_t value); diff --git a/main/include/test_common.h b/main/include/test_common.h new file mode 100644 index 0000000..a679df6 --- /dev/null +++ b/main/include/test_common.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include "ili9341.h" +#include "pax_gfx.h" + +typedef bool (*test_fn)(uint32_t *rc); + +bool test_wait_for_response(uint32_t *rc); +bool run_test(pax_buf_t* pax_buffer, const pax_font_t *font, ILI9341* ili9341, int line, const char *test_name, test_fn fn); + +#define RUN_TEST(name, fn) do {\ + ok &= run_test(pax_buffer, font, ili9341, line++, name, fn); \ +} while (0) + +#define RUN_TEST_MANDATORY(name, fn) do {\ + if (!run_test(pax_buffer, font, ili9341, line++, name, fn)) { \ + pax_draw_text(pax_buffer, 0xffff0000, font, 18, 0, 20*line, "Aborted"); \ + ili9341_write(ili9341, pax_buffer->buf); \ + ok = false; \ + goto error; \ + } \ +} while (0) + +#define RUN_TEST_BLIND(name, fn) do {\ + ok &= run_test(pax_buffer, font, NULL, line++, name, fn); \ +} while (0) diff --git a/main/main.c b/main/main.c index 1b5f8a3..872d032 100644 --- a/main/main.c +++ b/main/main.c @@ -48,6 +48,8 @@ #include "menus/start.h" +#include "factory_test.h" + extern const uint8_t wallpaper_png_start[] asm("_binary_wallpaper_png_start"); extern const uint8_t wallpaper_png_end[] asm("_binary_wallpaper_png_end"); @@ -102,9 +104,19 @@ void app_main(void) { esp_restart(); } - display_boot_screen(pax_buffer, ili9341, "Starting..."); + /* Start NVS */ + res = nvs_init(); + if (res != ESP_OK) { + ESP_LOGE(TAG, "NVS init failed: %d", res); + display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "NVS failed to initialize", "Flash may be corrupted", NULL); + esp_restart(); + } audio_init(); + + factory_test(pax_buffer, ili9341); + + display_boot_screen(pax_buffer, ili9341, "Starting..."); if (bsp_rp2040_init() != ESP_OK) { ESP_LOGE(TAG, "Failed to initialize the RP2040 co-processor"); @@ -178,16 +190,6 @@ void app_main(void) { display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "AppFS failed to initialize", "Flash may be corrupted", NULL); esp_restart(); } - - //display_boot_screen(pax_buffer, ili9341, "Initializing NVS..."); - - /* Start NVS */ - res = nvs_init(); - if (res != ESP_OK) { - ESP_LOGE(TAG, "NVS init failed: %d", res); - display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "NVS failed to initialize", "Flash may be corrupted", NULL); - esp_restart(); - } //display_boot_screen(pax_buffer, ili9341, "Initializing filesystem..."); @@ -217,12 +219,14 @@ void app_main(void) { bool sdcard_ready = (res == ESP_OK); if (sdcard_ready) { ESP_LOGI(TAG, "SD card filesystem mounted"); - } - /* Start LEDs */ - ws2812_init(GPIO_LED_DATA); - uint8_t ledBuffer[15] = {50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0}; - ws2812_send_data(ledBuffer, sizeof(ledBuffer)); + /* LED power is on: start LED driver and turn LEDs off */ + ws2812_init(GPIO_LED_DATA); + const uint8_t led_off[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + ws2812_send_data(led_off, sizeof(led_off)); + } else { + gpio_set_level(GPIO_SD_PWR, 0); // Disable power to LEDs and SD card + } /* Start WiFi */ wifi_init(); diff --git a/main/menus/dev.c b/main/menus/dev.c index c654d27..58564a0 100644 --- a/main/menus/dev.c +++ b/main/menus/dev.c @@ -95,7 +95,7 @@ void menu_dev(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) if (action == ACTION_FPGA_DL) { fpga_download(buttonQueue, get_ice40(), pax_buffer, ili9341); } else if (action == ACTION_FPGA_TEST) { - fpga_test(buttonQueue, get_ice40(), pax_buffer, ili9341); + fpga_test(buttonQueue, pax_buffer, ili9341); } else if (action == ACTION_FILE_BROWSER) { file_browser(buttonQueue, pax_buffer, ili9341, "/sd"); } else if (action == ACTION_FILE_BROWSER_INT) { diff --git a/main/settings.c b/main/settings.c index 9af730f..5d2eff1 100644 --- a/main/settings.c +++ b/main/settings.c @@ -23,3 +23,62 @@ esp_err_t nvs_init() { } return ESP_OK; } + +esp_err_t nvs_get_str_fixed(const char* nvs_namespace, const char* key, char* target, size_t target_size, size_t* size) { + nvs_handle_t handle; + esp_err_t res; + + res = nvs_open(nvs_namespace, NVS_READWRITE, &handle); + if (res != ESP_OK) return res; + + size_t required_size; + res = nvs_get_str(handle, key, NULL, &required_size); + if (res != ESP_OK) { + nvs_close(handle); + return res; + } + + if (required_size > target_size) { + nvs_close(handle); + return ESP_FAIL; + } + + res = nvs_get_str(handle, key, target, &required_size); + + if (size != NULL) *size = required_size; + + nvs_close(handle); + + return res; +} + +uint8_t nvs_get_u8_default(const char* nvs_namespace, const char* key, uint8_t default_value) { + nvs_handle_t handle; + esp_err_t res; + + res = nvs_open(nvs_namespace, NVS_READWRITE, &handle); + if (res != ESP_OK) return default_value; + + uint8_t target; + res = nvs_get_u8(handle, key, &target); + if (res != ESP_OK) { + nvs_close(handle); + return default_value; + } + + nvs_close(handle); + return target; +} + +esp_err_t nvs_set_u8_fixed(const char* nvs_namespace, const char* key, uint8_t value) { + nvs_handle_t handle; + esp_err_t res; + + res = nvs_open(nvs_namespace, NVS_READWRITE, &handle); + if (res != ESP_OK) return res; + + res = nvs_set_u8(handle, key, value); + nvs_close(handle); + + return res; +} diff --git a/main/test_common.c b/main/test_common.c new file mode 100644 index 0000000..c074816 --- /dev/null +++ b/main/test_common.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include +#include "hardware.h" +#include "ili9341.h" +#include "ice40.h" +#include "rp2040.h" +#include "pax_gfx.h" + +typedef bool (*test_fn)(uint32_t *rc); + +bool test_wait_for_response(uint32_t *rc) { + RP2040* rp2040 = get_rp2040(); + rp2040_input_message_t button_message = {0}; + if (rc != NULL) *rc = 0; + while (1) { + if (xQueueReceive(rp2040->queue, &button_message, portMAX_DELAY) == pdTRUE) { + if (button_message.state) { + switch(button_message.input) { + case RP2040_INPUT_BUTTON_HOME: + case RP2040_INPUT_BUTTON_MENU: + case RP2040_INPUT_BUTTON_BACK: + return false; + case RP2040_INPUT_BUTTON_ACCEPT: + if (rc != NULL) *rc = 1; + return true; + default: + break; + } + } + } + } +} + +bool run_test(pax_buf_t* pax_buffer, const pax_font_t *font, ILI9341* ili9341, int line, const char *test_name, test_fn fn) { + bool test_result; + uint32_t rc; + + /* Test name */ + pax_draw_text(pax_buffer, 0xffffffff, font, 18, 0, 20*line, test_name); + if (ili9341) ili9341_write(ili9341, pax_buffer->buf); + + /* Run the test */ + test_result = fn(&rc); + + /* Display result */ + if (!test_result) { + /* Error */ + char buf[10]; + snprintf(buf, sizeof(buf), "%08x", rc); + pax_draw_text(pax_buffer, 0xffff0000, font, 18, 200, 20*line, buf); + } else { + /* OK ! */ + pax_draw_text(pax_buffer, 0xff00ff00, font, 18, 200, 20*line, " OK"); + } + + if (ili9341) ili9341_write(ili9341, pax_buffer->buf); + + /* Pass through the 'OK' status */ + return test_result; +} From 0ed007e3f04e904ae01ceee6e12b8fdf499d0ce3 Mon Sep 17 00:00:00 2001 From: Renze Nicolai Date: Fri, 3 Jun 2022 04:23:00 +0200 Subject: [PATCH 2/8] Theming and animation --- main/CMakeLists.txt | 33 ++++ main/animation.c | 167 +++++++++++++++++++++ main/file_browser.c | 2 +- main/include/animation.h | 4 + main/include/menu.h | 20 ++- main/menu.c | 97 +++++++++--- main/menus/dev.c | 33 +++- main/menus/launcher.c | 27 +++- main/menus/settings.c | 27 +++- main/menus/start.c | 51 ++++++- main/menus/wifi.c | 12 +- main/uninstall.c | 4 +- resources/animation/animation_frame_1.png | Bin 0 -> 4090 bytes resources/animation/animation_frame_10.png | Bin 0 -> 4390 bytes resources/animation/animation_frame_11.png | Bin 0 -> 4273 bytes resources/animation/animation_frame_12.png | Bin 0 -> 4085 bytes resources/animation/animation_frame_13.png | Bin 0 -> 3885 bytes resources/animation/animation_frame_14.png | Bin 0 -> 3860 bytes resources/animation/animation_frame_15.png | Bin 0 -> 2819 bytes resources/animation/animation_frame_16.png | Bin 0 -> 2763 bytes resources/animation/animation_frame_17.png | Bin 0 -> 2344 bytes resources/animation/animation_frame_18.png | Bin 0 -> 1892 bytes resources/animation/animation_frame_19.png | Bin 0 -> 1464 bytes resources/animation/animation_frame_2.png | Bin 0 -> 4092 bytes resources/animation/animation_frame_20.png | Bin 0 -> 1646 bytes resources/animation/animation_frame_21.png | Bin 0 -> 1740 bytes resources/animation/animation_frame_22.png | Bin 0 -> 1825 bytes resources/animation/animation_frame_23.png | Bin 0 -> 1836 bytes resources/animation/animation_frame_24.png | Bin 0 -> 1215 bytes resources/animation/animation_frame_25.png | Bin 0 -> 706 bytes resources/animation/animation_frame_26.png | Bin 0 -> 468 bytes resources/animation/animation_frame_27.png | Bin 0 -> 395 bytes resources/animation/animation_frame_28.png | Bin 0 -> 164 bytes resources/animation/animation_frame_3.png | Bin 0 -> 4090 bytes resources/animation/animation_frame_4.png | Bin 0 -> 4092 bytes resources/animation/animation_frame_5.png | Bin 0 -> 4090 bytes resources/animation/animation_frame_6.png | Bin 0 -> 4092 bytes resources/animation/animation_frame_7.png | Bin 0 -> 4083 bytes resources/animation/animation_frame_8.png | Bin 0 -> 4432 bytes resources/animation/animation_frame_9.png | Bin 0 -> 4476 bytes resources/icons/apps.png | Bin 0 -> 700 bytes resources/icons/dev.png | Bin 0 -> 857 bytes resources/icons/home.png | Bin 0 -> 439 bytes resources/icons/settings.png | Bin 0 -> 1050 bytes 44 files changed, 427 insertions(+), 50 deletions(-) create mode 100644 main/animation.c create mode 100644 main/include/animation.h create mode 100644 resources/animation/animation_frame_1.png create mode 100644 resources/animation/animation_frame_10.png create mode 100644 resources/animation/animation_frame_11.png create mode 100644 resources/animation/animation_frame_12.png create mode 100644 resources/animation/animation_frame_13.png create mode 100644 resources/animation/animation_frame_14.png create mode 100644 resources/animation/animation_frame_15.png create mode 100644 resources/animation/animation_frame_16.png create mode 100644 resources/animation/animation_frame_17.png create mode 100644 resources/animation/animation_frame_18.png create mode 100644 resources/animation/animation_frame_19.png create mode 100644 resources/animation/animation_frame_2.png create mode 100644 resources/animation/animation_frame_20.png create mode 100644 resources/animation/animation_frame_21.png create mode 100644 resources/animation/animation_frame_22.png create mode 100644 resources/animation/animation_frame_23.png create mode 100644 resources/animation/animation_frame_24.png create mode 100644 resources/animation/animation_frame_25.png create mode 100644 resources/animation/animation_frame_26.png create mode 100644 resources/animation/animation_frame_27.png create mode 100644 resources/animation/animation_frame_28.png create mode 100644 resources/animation/animation_frame_3.png create mode 100644 resources/animation/animation_frame_4.png create mode 100644 resources/animation/animation_frame_5.png create mode 100644 resources/animation/animation_frame_6.png create mode 100644 resources/animation/animation_frame_7.png create mode 100644 resources/animation/animation_frame_8.png create mode 100644 resources/animation/animation_frame_9.png create mode 100644 resources/icons/apps.png create mode 100644 resources/icons/dev.png create mode 100644 resources/icons/home.png create mode 100644 resources/icons/settings.png diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index f0a694e..209ea43 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -22,6 +22,7 @@ idf_component_register( "file_browser.c" "test_common.c" "factory_test.c" + "animation.c" INCLUDE_DIRS "." "include" "menus" @@ -32,4 +33,36 @@ idf_component_register( ${project_dir}/resources/boot.snd ${project_dir}/resources/mch2022_logo.png ${project_dir}/resources/logo_screen.png + ${project_dir}/resources/icons/dev.png + ${project_dir}/resources/icons/home.png + ${project_dir}/resources/icons/settings.png + ${project_dir}/resources/icons/apps.png + ${project_dir}/resources/animation/animation_frame_1.png + ${project_dir}/resources/animation/animation_frame_2.png + ${project_dir}/resources/animation/animation_frame_3.png + ${project_dir}/resources/animation/animation_frame_4.png + ${project_dir}/resources/animation/animation_frame_5.png + ${project_dir}/resources/animation/animation_frame_6.png + ${project_dir}/resources/animation/animation_frame_7.png + ${project_dir}/resources/animation/animation_frame_8.png + ${project_dir}/resources/animation/animation_frame_9.png + ${project_dir}/resources/animation/animation_frame_10.png + ${project_dir}/resources/animation/animation_frame_11.png + ${project_dir}/resources/animation/animation_frame_12.png + ${project_dir}/resources/animation/animation_frame_13.png + ${project_dir}/resources/animation/animation_frame_14.png + ${project_dir}/resources/animation/animation_frame_15.png + ${project_dir}/resources/animation/animation_frame_16.png + ${project_dir}/resources/animation/animation_frame_17.png + ${project_dir}/resources/animation/animation_frame_18.png + ${project_dir}/resources/animation/animation_frame_19.png + ${project_dir}/resources/animation/animation_frame_20.png + ${project_dir}/resources/animation/animation_frame_21.png + ${project_dir}/resources/animation/animation_frame_22.png + ${project_dir}/resources/animation/animation_frame_23.png + ${project_dir}/resources/animation/animation_frame_24.png + ${project_dir}/resources/animation/animation_frame_25.png + ${project_dir}/resources/animation/animation_frame_26.png + ${project_dir}/resources/animation/animation_frame_27.png + ${project_dir}/resources/animation/animation_frame_28.png ) diff --git a/main/animation.c b/main/animation.c new file mode 100644 index 0000000..a3589db --- /dev/null +++ b/main/animation.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include "pax_gfx.h" +#include "pax_codecs.h" +#include "ili9341.h" +#include "ws2812.h" +#include "hardware.h" + +extern const uint8_t animation_frame_1_start[] asm("_binary_animation_frame_1_png_start"); +extern const uint8_t animation_frame_1_end[] asm("_binary_animation_frame_1_png_end"); +extern const uint8_t animation_frame_2_start[] asm("_binary_animation_frame_2_png_start"); +extern const uint8_t animation_frame_2_end[] asm("_binary_animation_frame_2_png_end"); +extern const uint8_t animation_frame_3_start[] asm("_binary_animation_frame_3_png_start"); +extern const uint8_t animation_frame_3_end[] asm("_binary_animation_frame_3_png_end"); +extern const uint8_t animation_frame_4_start[] asm("_binary_animation_frame_4_png_start"); +extern const uint8_t animation_frame_4_end[] asm("_binary_animation_frame_4_png_end"); +extern const uint8_t animation_frame_5_start[] asm("_binary_animation_frame_5_png_start"); +extern const uint8_t animation_frame_5_end[] asm("_binary_animation_frame_5_png_end"); +extern const uint8_t animation_frame_6_start[] asm("_binary_animation_frame_6_png_start"); +extern const uint8_t animation_frame_6_end[] asm("_binary_animation_frame_6_png_end"); +extern const uint8_t animation_frame_7_start[] asm("_binary_animation_frame_7_png_start"); +extern const uint8_t animation_frame_7_end[] asm("_binary_animation_frame_7_png_end"); +extern const uint8_t animation_frame_8_start[] asm("_binary_animation_frame_8_png_start"); +extern const uint8_t animation_frame_8_end[] asm("_binary_animation_frame_8_png_end"); +extern const uint8_t animation_frame_9_start[] asm("_binary_animation_frame_9_png_start"); +extern const uint8_t animation_frame_9_end[] asm("_binary_animation_frame_9_png_end"); +extern const uint8_t animation_frame_10_start[] asm("_binary_animation_frame_10_png_start"); +extern const uint8_t animation_frame_10_end[] asm("_binary_animation_frame_10_png_end"); +extern const uint8_t animation_frame_11_start[] asm("_binary_animation_frame_11_png_start"); +extern const uint8_t animation_frame_11_end[] asm("_binary_animation_frame_11_png_end"); +extern const uint8_t animation_frame_12_start[] asm("_binary_animation_frame_12_png_start"); +extern const uint8_t animation_frame_12_end[] asm("_binary_animation_frame_12_png_end"); +extern const uint8_t animation_frame_13_start[] asm("_binary_animation_frame_13_png_start"); +extern const uint8_t animation_frame_13_end[] asm("_binary_animation_frame_13_png_end"); +extern const uint8_t animation_frame_14_start[] asm("_binary_animation_frame_14_png_start"); +extern const uint8_t animation_frame_14_end[] asm("_binary_animation_frame_14_png_end"); +extern const uint8_t animation_frame_15_start[] asm("_binary_animation_frame_15_png_start"); +extern const uint8_t animation_frame_15_end[] asm("_binary_animation_frame_15_png_end"); +extern const uint8_t animation_frame_16_start[] asm("_binary_animation_frame_16_png_start"); +extern const uint8_t animation_frame_16_end[] asm("_binary_animation_frame_16_png_end"); +extern const uint8_t animation_frame_17_start[] asm("_binary_animation_frame_17_png_start"); +extern const uint8_t animation_frame_17_end[] asm("_binary_animation_frame_17_png_end"); +extern const uint8_t animation_frame_18_start[] asm("_binary_animation_frame_18_png_start"); +extern const uint8_t animation_frame_18_end[] asm("_binary_animation_frame_18_png_end"); +extern const uint8_t animation_frame_19_start[] asm("_binary_animation_frame_19_png_start"); +extern const uint8_t animation_frame_19_end[] asm("_binary_animation_frame_19_png_end"); +extern const uint8_t animation_frame_20_start[] asm("_binary_animation_frame_20_png_start"); +extern const uint8_t animation_frame_20_end[] asm("_binary_animation_frame_20_png_end"); +extern const uint8_t animation_frame_21_start[] asm("_binary_animation_frame_21_png_start"); +extern const uint8_t animation_frame_21_end[] asm("_binary_animation_frame_21_png_end"); +extern const uint8_t animation_frame_22_start[] asm("_binary_animation_frame_22_png_start"); +extern const uint8_t animation_frame_22_end[] asm("_binary_animation_frame_22_png_end"); +extern const uint8_t animation_frame_23_start[] asm("_binary_animation_frame_23_png_start"); +extern const uint8_t animation_frame_23_end[] asm("_binary_animation_frame_23_png_end"); +extern const uint8_t animation_frame_24_start[] asm("_binary_animation_frame_24_png_start"); +extern const uint8_t animation_frame_24_end[] asm("_binary_animation_frame_24_png_end"); +extern const uint8_t animation_frame_25_start[] asm("_binary_animation_frame_25_png_start"); +extern const uint8_t animation_frame_25_end[] asm("_binary_animation_frame_25_png_end"); +extern const uint8_t animation_frame_26_start[] asm("_binary_animation_frame_26_png_start"); +extern const uint8_t animation_frame_26_end[] asm("_binary_animation_frame_26_png_end"); +extern const uint8_t animation_frame_27_start[] asm("_binary_animation_frame_27_png_start"); +extern const uint8_t animation_frame_27_end[] asm("_binary_animation_frame_27_png_end"); +extern const uint8_t animation_frame_28_start[] asm("_binary_animation_frame_28_png_start"); +extern const uint8_t animation_frame_28_end[] asm("_binary_animation_frame_28_png_end"); + +const uint8_t* animation_frames[] = { + animation_frame_1_start, + animation_frame_2_start, + animation_frame_3_start, + animation_frame_4_start, + animation_frame_5_start, + animation_frame_6_start, + animation_frame_7_start, + animation_frame_8_start, + animation_frame_9_start, + animation_frame_10_start, + animation_frame_11_start, + animation_frame_12_start, + animation_frame_13_start, + animation_frame_14_start, + animation_frame_15_start, + animation_frame_16_start, + animation_frame_17_start, + animation_frame_18_start, + animation_frame_19_start, + animation_frame_20_start, + animation_frame_21_start, + animation_frame_22_start, + animation_frame_23_start, + animation_frame_24_start, + animation_frame_25_start, + animation_frame_26_start, + animation_frame_27_start, + animation_frame_28_start +}; + +const uint8_t* animation_frames_end[] = { + animation_frame_1_end, + animation_frame_2_end, + animation_frame_3_end, + animation_frame_4_end, + animation_frame_5_end, + animation_frame_6_end, + animation_frame_7_end, + animation_frame_8_end, + animation_frame_9_end, + animation_frame_10_end, + animation_frame_11_end, + animation_frame_12_end, + animation_frame_13_end, + animation_frame_14_end, + animation_frame_15_end, + animation_frame_16_end, + animation_frame_17_end, + animation_frame_18_end, + animation_frame_19_end, + animation_frame_20_end, + animation_frame_21_end, + animation_frame_22_end, + animation_frame_23_end, + animation_frame_24_end, + animation_frame_25_end, + animation_frame_26_end, + animation_frame_27_end, + animation_frame_28_end +}; + +void display_animation(pax_buf_t* pax_buffer, ILI9341* ili9341) { + + gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_OUTPUT); + gpio_set_level(GPIO_SD_PWR, 1); + ws2812_init(GPIO_LED_DATA); + uint8_t led_data[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + ws2812_send_data(led_data, sizeof(led_data)); + + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0xFFFFFF); + + for (uint8_t frame = 0; frame < 28; frame++) { + pax_buf_t image; + pax_decode_png_buf(&image, (void*) animation_frames[frame], animation_frames_end[frame] - animation_frames[frame], PAX_BUF_16_565RGB, 0); + pax_draw_image(pax_buffer, &image, 0, 0); + pax_buf_destroy(&image); + ili9341_write(ili9341, pax_buffer->buf); + uint8_t brightness = (frame > 14) ? (frame - 14) : (0); + led_data[1] = brightness; + led_data[3] = brightness; + led_data[8] = brightness; + led_data[9] = brightness / 2; + led_data[10] = brightness / 2; + led_data[14] = brightness; + ws2812_send_data(led_data, sizeof(led_data)); + } + + for (uint8_t brightness = 14; brightness < 50; brightness++) { + led_data[1] = brightness; + led_data[3] = brightness; + led_data[8] = brightness; + led_data[9] = brightness / 2; + led_data[10] = brightness / 2; + led_data[14] = brightness; + ws2812_send_data(led_data, sizeof(led_data)); + vTaskDelay(50 / portTICK_PERIOD_MS); + } +} diff --git a/main/file_browser.c b/main/file_browser.c index 2a931a0..5adf658 100644 --- a/main/file_browser.c +++ b/main/file_browser.c @@ -109,7 +109,7 @@ void file_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9 char path[512] = {0}; strncpy(path, initial_path, sizeof(path)); while (true) { - menu_t* menu = menu_alloc(path); + menu_t* menu = menu_alloc(path, 20, 18); DIR* dir = opendir(path); if (dir == NULL) { if (path[0] != 0) { diff --git a/main/include/animation.h b/main/include/animation.h new file mode 100644 index 0000000..2f9bc39 --- /dev/null +++ b/main/include/animation.h @@ -0,0 +1,4 @@ +#include "pax_gfx.h" +#include "ili9341.h" + +void display_animation(pax_buf_t* pax_buffer, ILI9341* ili9341); diff --git a/main/include/menu.h b/main/include/menu.h index 4a57294..887bffd 100644 --- a/main/include/menu.h +++ b/main/include/menu.h @@ -16,6 +16,8 @@ typedef struct _menu_item { char* label; menu_callback_t callback; void* callbackArgs; + + pax_buf_t* icon; // Linked list struct _menu_item* previousItem; @@ -27,11 +29,27 @@ typedef struct menu { menu_item_t* firstItem; size_t length; size_t position; + float entry_height; + float text_height; + pax_buf_t* icon; + + pax_col_t fgColor; + pax_col_t bgColor; + pax_col_t selectedItemColor; + pax_col_t bgTextColor; + pax_col_t borderColor; + pax_col_t titleColor; + pax_col_t titleBgColor; + pax_col_t scrollbarBgColor; + pax_col_t scrollbarFgColor; + } menu_t; -menu_t* menu_alloc(const char* aTitle); +menu_t* menu_alloc(const char* aTitle, float arg_entry_height, float arg_text_height); void menu_free(menu_t* aMenu); +void menu_set_icon(menu_t* aMenu, pax_buf_t* icon); bool menu_insert_item(menu_t* aMenu, const char* aLabel, menu_callback_t aCallback, void* aCallbackArgs, size_t aPosition); +bool menu_insert_item_icon(menu_t* aMenu, const char* aLabel, menu_callback_t aCallback, void* aCallbackArgs, size_t aPosition, pax_buf_t* icon); bool menu_remove_item(menu_t* aMenu, size_t aPosition); bool menu_navigate_to(menu_t* aMenu, size_t aPosition); void menu_navigate_previous(menu_t* aMenu); diff --git a/main/menu.c b/main/menu.c index c6a57a3..2deba87 100644 --- a/main/menu.c +++ b/main/menu.c @@ -1,9 +1,10 @@ #include #include #include "pax_gfx.h" +#include "pax_codecs.h" #include "menu.h" -menu_t* menu_alloc(const char* aTitle) { +menu_t* menu_alloc(const char* aTitle, float arg_entry_height, float arg_text_height) { if (aTitle == NULL) return NULL; menu_t* menu = malloc(sizeof(menu_t)); if (menu == NULL) return NULL; @@ -17,6 +18,20 @@ menu_t* menu_alloc(const char* aTitle) { menu->firstItem = NULL; menu->length = 0; menu->position = 0; + menu->entry_height = (arg_entry_height > 0) ? arg_entry_height : 20; + menu->text_height = (arg_text_height > 0) ? arg_text_height : (arg_entry_height - 2); + menu->icon = NULL; + + menu->fgColor = 0xFF000000; + menu->bgColor = 0xFFFFFFFF; + menu->bgTextColor = 0xFFFFFFFF; + menu->selectedItemColor = 0xFF000000; + menu->borderColor = 0x88000000; + menu->titleColor = 0xFFFFFFFF; + menu->titleBgColor = 0xFF000000; + menu->scrollbarBgColor = 0xFFCCCCCC; + menu->scrollbarFgColor = 0xFF555555; + return menu; } @@ -37,6 +52,10 @@ void menu_free(menu_t* aMenu) { free(aMenu); } +void menu_set_icon(menu_t* aMenu, pax_buf_t* icon) { + aMenu->icon = icon; +} + menu_item_t* _menu_find_item(menu_t* aMenu, size_t aPosition) { menu_item_t* currentItem = aMenu->firstItem; if (currentItem == NULL) return NULL; @@ -66,11 +85,12 @@ bool menu_insert_item(menu_t* aMenu, const char* aLabel, menu_callback_t aCallba newItem->label = malloc(labelSize); if (newItem->label == NULL) { free(newItem); - return NULL; + return false; } memcpy(newItem->label, aLabel, labelSize); newItem->callback = aCallback; newItem->callbackArgs = aCallbackArgs; + newItem->icon = NULL; if (aMenu->firstItem == NULL) { newItem->nextItem = NULL; newItem->previousItem = NULL; @@ -91,6 +111,21 @@ bool menu_insert_item(menu_t* aMenu, const char* aLabel, menu_callback_t aCallba return true; } +bool menu_insert_item_icon(menu_t* aMenu, const char* aLabel, menu_callback_t aCallback, void* aCallbackArgs, size_t aPosition, pax_buf_t* icon) { + if (!menu_insert_item(aMenu, aLabel, aCallback, aCallbackArgs, aPosition)) { + return false; + } + menu_item_t* item; + if (aPosition >= aMenu->length - 1) { + item = _menu_find_last_item(aMenu); + } else { + item = _menu_find_item(aMenu, aPosition); + } + + item->icon = icon; + return true; +} + bool menu_remove_item(menu_t* aMenu, size_t aPosition) { if (aMenu == NULL) return false; // Can't delete an item from a menu that doesn't exist if (aMenu->length <= aPosition) return false; // Can't delete an item that doesn't exist @@ -175,17 +210,11 @@ void menu_debug(menu_t* aMenu) { } void menu_render(pax_buf_t *aBuffer, menu_t* aMenu, float aPosX, float aPosY, float aWidth, float aHeight, pax_col_t aColor) { - pax_col_t fgColor = aColor; - pax_col_t bgColor = 0xFFFFFFFF; - pax_col_t bgTextColor = 0xFFFFFFFF; - pax_col_t borderColor = 0x88000000; - pax_col_t titleColor = 0xFFFFFFFF; - pax_col_t titleBgColor = aColor; - pax_col_t scrollbarBgColor = 0xFFCCCCCC; - pax_col_t scrollbarFgColor = 0xFF555555; const pax_font_t *font = pax_get_font("saira regular"); - float entry_height = 18 + 2; + float entry_height = aMenu->entry_height;//18 + 2; + float text_height = aMenu->text_height; + float text_offset = ((entry_height - text_height) / 2) + 1; size_t maxItems = aHeight / entry_height; float posY = aPosY; @@ -193,12 +222,20 @@ void menu_render(pax_buf_t *aBuffer, menu_t* aMenu, float aPosX, float aPosY, fl pax_noclip(aBuffer); if (maxItems > 1) { + float offsetX = 0; + if (aMenu->icon != NULL) { + offsetX = aMenu->icon->width; + } + maxItems--; - pax_simple_rect(aBuffer, titleBgColor, aPosX, posY, aWidth, entry_height); - pax_simple_line(aBuffer, titleColor, aPosX + 1, aPosY + entry_height, aPosX + aWidth - 2, aPosY + entry_height - 1); - pax_clip(aBuffer, aPosX + 1, posY + 1, aWidth - 2, entry_height - 2); - pax_draw_text(aBuffer, titleColor, font, entry_height - 2, aPosX + 1, posY + 1, aMenu->title); + pax_simple_rect(aBuffer, aMenu->titleBgColor, aPosX, posY, aWidth, entry_height); + //pax_simple_line(aBuffer, aMenu->titleColor, aPosX + 1, aPosY + entry_height, aPosX + aWidth - 2, aPosY + entry_height - 1); + pax_clip(aBuffer, aPosX + 1, posY + text_offset, aWidth - 2, text_height); + pax_draw_text(aBuffer, aMenu->titleColor, font, text_height, aPosX + offsetX + 1, posY + text_offset, aMenu->title); pax_noclip(aBuffer); + if (aMenu->icon != NULL) { + pax_draw_image(aBuffer, aMenu->icon, aPosX, posY); + } posY += entry_height; } @@ -207,8 +244,8 @@ void menu_render(pax_buf_t *aBuffer, menu_t* aMenu, float aPosX, float aPosY, fl itemOffset = aMenu->position - maxItems + 1; } - pax_outline_rect(aBuffer, borderColor, aPosX, aPosY, aWidth, aHeight); - pax_simple_rect(aBuffer, bgColor, aPosX, posY, aWidth, aHeight - posY + aPosY); + pax_outline_rect(aBuffer, aMenu->borderColor, aPosX, aPosY, aWidth, aHeight); + pax_simple_rect(aBuffer, aMenu->bgColor, aPosX, posY, aWidth, aHeight - posY + aPosY); for (size_t index = itemOffset; (index < itemOffset + maxItems) && (index < aMenu->length); index++) { menu_item_t* item = _menu_find_item(aMenu, index); @@ -216,18 +253,28 @@ void menu_render(pax_buf_t *aBuffer, menu_t* aMenu, float aPosX, float aPosY, fl printf("Render error: item is NULL at %u\n", index); break; } + + float iconWidth = 0; + if (item->icon != NULL) { + iconWidth = item->icon->width + 1; + } if (index == aMenu->position) { - pax_simple_rect(aBuffer, fgColor, aPosX + 1, posY, aWidth - 2, entry_height); - pax_clip(aBuffer, aPosX + 1, posY + 1, aWidth - 4, entry_height - 2); - pax_draw_text(aBuffer, bgTextColor, font, entry_height - 2, aPosX + 1, posY + 1, item->label); + pax_simple_rect(aBuffer, aMenu->selectedItemColor, aPosX + 1, posY, aWidth - 2, entry_height); + pax_clip(aBuffer, aPosX + 1, posY + text_offset, aWidth - 4, text_height); + pax_draw_text(aBuffer, aMenu->bgTextColor, font, text_height, aPosX + iconWidth + 1, posY + text_offset, item->label); pax_noclip(aBuffer); } else { - pax_simple_rect(aBuffer, bgColor, aPosX + 1, posY, aWidth - 2, entry_height); - pax_clip(aBuffer, aPosX + 1, posY + 1, aWidth - 4, entry_height - 2); - pax_draw_text(aBuffer, fgColor, font, entry_height - 2, aPosX + 1, posY + 1, item->label); + pax_simple_rect(aBuffer, aMenu->bgColor, aPosX + 1, posY, aWidth - 2, entry_height); + pax_clip(aBuffer, aPosX + 1, posY + text_offset, aWidth - 4, text_height); + pax_draw_text(aBuffer, aMenu->fgColor, font, text_height, aPosX + iconWidth + 1, posY + text_offset, item->label); pax_noclip(aBuffer); } + + if (item->icon != NULL) { + pax_draw_image(aBuffer, item->icon, aPosX + 1, posY); + } + posY += entry_height; } @@ -242,8 +289,8 @@ void menu_render(pax_buf_t *aBuffer, menu_t* aMenu, float aPosX, float aPosY, fl float scrollbarStart = scrollbarHeight * fractionStart; float scrollbarEnd = scrollbarHeight * fractionEnd; - pax_simple_rect(aBuffer, scrollbarBgColor, aPosX + aWidth - 5, aPosY + entry_height - 1, 4, scrollbarHeight); - pax_simple_rect(aBuffer, scrollbarFgColor, aPosX + aWidth - 5, aPosY + entry_height - 1 + scrollbarStart, 4, scrollbarEnd - scrollbarStart); + pax_simple_rect(aBuffer, aMenu->scrollbarBgColor, aPosX + aWidth - 5, aPosY + entry_height - 1, 4, scrollbarHeight); + pax_simple_rect(aBuffer, aMenu->scrollbarFgColor, aPosX + aWidth - 5, aPosY + entry_height - 1 + scrollbarStart, 4, scrollbarEnd - scrollbarStart); pax_noclip(aBuffer); } diff --git a/main/menus/dev.c b/main/menus/dev.c index 58564a0..ff341e8 100644 --- a/main/menus/dev.c +++ b/main/menus/dev.c @@ -10,6 +10,7 @@ #include "appfs.h" #include "ili9341.h" #include "pax_gfx.h" +#include "pax_codecs.h" #include "menu.h" #include "rp2040.h" #include "launcher.h" @@ -19,6 +20,10 @@ #include "hardware.h" #include "file_browser.h" #include "fpga_test.h" +#include "animation.h" + +extern const uint8_t dev_png_start[] asm("_binary_dev_png_start"); +extern const uint8_t dev_png_end[] asm("_binary_dev_png_end"); typedef enum action { ACTION_NONE, @@ -27,21 +32,39 @@ typedef enum action { ACTION_FPGA_TEST, ACTION_FILE_BROWSER, ACTION_FILE_BROWSER_INT, + ACTION_ANIMATION } menu_dev_action_t; void render_dev_help(pax_buf_t* pax_buffer) { const pax_font_t *font = pax_get_font("saira regular"); pax_background(pax_buffer, 0xFFFFFF); pax_noclip(pax_buffer); - pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 19, "[A] accept [B] back"); + pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 18, "[A] accept [B] back"); } void menu_dev(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { - menu_t* menu = menu_alloc("Development tools"); + menu_t* menu = menu_alloc("Development tools", 32, 18); + + menu->fgColor = 0xFF000000; + menu->bgColor = 0xFFFFFFFF; + menu->bgTextColor = 0xFF000000; + menu->selectedItemColor = 0xFFfec859; + menu->borderColor = 0xFFfa448c; + menu->titleColor = 0xFFfec859; + menu->titleBgColor = 0xFFfa448c; + menu->scrollbarBgColor = 0xFFCCCCCC; + menu->scrollbarFgColor = 0xFF555555; + + pax_buf_t icon_dev; + pax_decode_png_buf(&icon_dev, (void*) dev_png_start, dev_png_end - dev_png_start, PAX_BUF_32_8888ARGB, 0); + + menu_set_icon(menu, &icon_dev); + menu_insert_item(menu, "FPGA download mode", NULL, (void*) ACTION_FPGA_DL, -1); menu_insert_item(menu, "FPGA selftest", NULL, (void*) ACTION_FPGA_TEST, -1); menu_insert_item(menu, "File browser (SD card)", NULL, (void*) ACTION_FILE_BROWSER, -1); menu_insert_item(menu, "File browser (internal)", NULL, (void*) ACTION_FILE_BROWSER_INT, -1); + menu_insert_item(menu, "Animation", NULL, (void*) ACTION_ANIMATION, -1); bool render = true; menu_dev_action_t action = ACTION_NONE; @@ -86,7 +109,7 @@ void menu_dev(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) } if (render) { - menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF000000); + menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF491d88); ili9341_write(ili9341, pax_buffer->buf); render = false; } @@ -100,6 +123,8 @@ void menu_dev(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) file_browser(buttonQueue, pax_buffer, ili9341, "/sd"); } else if (action == ACTION_FILE_BROWSER_INT) { file_browser(buttonQueue, pax_buffer, ili9341, "/internal"); + } else if (action == ACTION_ANIMATION) { + display_animation(pax_buffer, ili9341); } else if (action == ACTION_BACK) { break; } @@ -110,4 +135,6 @@ void menu_dev(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) } menu_free(menu); + + pax_buf_destroy(&icon_dev); } diff --git a/main/menus/launcher.c b/main/menus/launcher.c index 86d073a..25c7e44 100644 --- a/main/menus/launcher.c +++ b/main/menus/launcher.c @@ -10,10 +10,14 @@ #include "appfs.h" #include "ili9341.h" #include "pax_gfx.h" +#include "pax_codecs.h" #include "menu.h" #include "rp2040.h" #include "appfs_wrapper.h" +extern const uint8_t apps_png_start[] asm("_binary_apps_png_start"); +extern const uint8_t apps_png_end[] asm("_binary_apps_png_end"); + typedef enum { ACTION_NONE, ACTION_APPFS, @@ -26,7 +30,23 @@ typedef struct { } menu_launcher_args_t; void menu_launcher(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { - menu_t* menu = menu_alloc("Apps"); + menu_t* menu = menu_alloc("Apps", 32, 18); + + menu->fgColor = 0xFF000000; + menu->bgColor = 0xFFFFFFFF; + menu->bgTextColor = 0xFFFFFFFF; + menu->selectedItemColor = 0xFFfa448c; + menu->borderColor = 0xFF491d88; + menu->titleColor = 0xFFfa448c; + menu->titleBgColor = 0xFF491d88; + menu->scrollbarBgColor = 0xFFCCCCCC; + menu->scrollbarFgColor = 0xFF555555; + + pax_buf_t icon_apps; + pax_decode_png_buf(&icon_apps, (void*) apps_png_start, apps_png_end - apps_png_start, PAX_BUF_32_8888ARGB, 0); + + menu_set_icon(menu, &icon_apps); + const pax_font_t *font = pax_get_font("saira regular"); appfs_handle_t appfs_fd = APPFS_INVALID_FD; @@ -46,7 +66,7 @@ void menu_launcher(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili pax_background(pax_buffer, 0xFFFFFF); pax_noclip(pax_buffer); - pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 19, "[A] start app [B] back"); + pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 18, "[A] start app [B] back"); bool quit = false; @@ -88,7 +108,7 @@ void menu_launcher(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili } if (render) { - menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF000000); + menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF491d88); ili9341_write(ili9341, pax_buffer->buf); render = false; } @@ -110,4 +130,5 @@ void menu_launcher(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili } menu_free(menu); + pax_buf_destroy(&icon_apps); } diff --git a/main/menus/settings.c b/main/menus/settings.c index 71b142f..fadddc3 100644 --- a/main/menus/settings.c +++ b/main/menus/settings.c @@ -10,6 +10,7 @@ #include "appfs.h" #include "ili9341.h" #include "pax_gfx.h" +#include "pax_codecs.h" #include "menu.h" #include "rp2040.h" #include "appfs_wrapper.h" @@ -21,6 +22,9 @@ #include "wifi.h" #include "uninstall.h" +extern const uint8_t settings_png_start[] asm("_binary_settings_png_start"); +extern const uint8_t settings_png_end[] asm("_binary_settings_png_end"); + typedef enum action { ACTION_NONE, ACTION_BACK, @@ -34,11 +38,27 @@ void render_settings_help(pax_buf_t* pax_buffer) { const pax_font_t *font = pax_get_font("saira regular"); pax_background(pax_buffer, 0xFFFFFF); pax_noclip(pax_buffer); - pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 19, "[A] accept [B] back"); + pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 18, "[A] accept [B] back"); } void menu_settings(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { - menu_t* menu = menu_alloc("Settings"); + menu_t* menu = menu_alloc("Settings", 32, 18); + + menu->fgColor = 0xFF000000; + menu->bgColor = 0xFFFFFFFF; + menu->bgTextColor = 0xFFFFFFFF; + menu->selectedItemColor = 0xFF491d88; + menu->borderColor = 0xFF43b5a0; + menu->titleColor = 0xFF491d88; + menu->titleBgColor = 0xFF43b5a0; + menu->scrollbarBgColor = 0xFFCCCCCC; + menu->scrollbarFgColor = 0xFF555555; + + pax_buf_t icon_settings; + pax_decode_png_buf(&icon_settings, (void*) settings_png_start, settings_png_end - settings_png_start, PAX_BUF_32_8888ARGB, 0); + + menu_set_icon(menu, &icon_settings); + menu_insert_item(menu, "WiFi configuration", NULL, (void*) ACTION_WIFI, -1); menu_insert_item(menu, "Firmware update", NULL, (void*) ACTION_OTA, -1); menu_insert_item(menu, "Flash RP2040 firmware", NULL, (void*) ACTION_RP2040_BL, -1); @@ -87,7 +107,7 @@ void menu_settings(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili } if (render) { - menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF000000); + menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF491d88); ili9341_write(ili9341, pax_buffer->buf); render = false; } @@ -120,4 +140,5 @@ void menu_settings(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili } menu_free(menu); + pax_buf_destroy(&icon_settings); } diff --git a/main/menus/start.c b/main/menus/start.c index 7dba283..533cdf3 100644 --- a/main/menus/start.c +++ b/main/menus/start.c @@ -10,12 +10,25 @@ #include "appfs.h" #include "ili9341.h" #include "pax_gfx.h" +#include "pax_codecs.h" #include "menu.h" #include "rp2040.h" #include "launcher.h" #include "settings.h" #include "dev.h" +extern const uint8_t home_png_start[] asm("_binary_home_png_start"); +extern const uint8_t home_png_end[] asm("_binary_home_png_end"); + +extern const uint8_t apps_png_start[] asm("_binary_apps_png_start"); +extern const uint8_t apps_png_end[] asm("_binary_apps_png_end"); + +extern const uint8_t dev_png_start[] asm("_binary_dev_png_start"); +extern const uint8_t dev_png_end[] asm("_binary_dev_png_end"); + +extern const uint8_t settings_png_start[] asm("_binary_settings_png_start"); +extern const uint8_t settings_png_end[] asm("_binary_settings_png_end"); + typedef enum action { ACTION_NONE, ACTION_APPS, @@ -27,14 +40,36 @@ void render_start_help(pax_buf_t* pax_buffer) { const pax_font_t *font = pax_get_font("saira regular"); pax_background(pax_buffer, 0xFFFFFF); pax_noclip(pax_buffer); - pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 19, "[A] accept"); + pax_draw_text(pax_buffer, 0xFF491d88, font, 18, 5, 240 - 18, "[A] accept"); } void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { - menu_t* menu = menu_alloc("Main menu"); - menu_insert_item(menu, "Apps", NULL, (void*) ACTION_APPS, -1); - menu_insert_item(menu, "Development tools", NULL, (void*) ACTION_DEV, -1); - menu_insert_item(menu, "Settings", NULL, (void*) ACTION_SETTINGS, -1); + menu_t* menu = menu_alloc("Main menu", 32, 18); + + menu->fgColor = 0xFF000000; + menu->bgColor = 0xFFFFFFFF; + menu->bgTextColor = 0xFF000000; + menu->selectedItemColor = 0xFFfec859; + menu->borderColor = 0xFF491d88; + menu->titleColor = 0xFFfec859; + menu->titleBgColor = 0xFF491d88; + menu->scrollbarBgColor = 0xFFCCCCCC; + menu->scrollbarFgColor = 0xFF555555; + + pax_buf_t icon_home; + pax_decode_png_buf(&icon_home, (void*) home_png_start, home_png_end - home_png_start, PAX_BUF_32_8888ARGB, 0); + pax_buf_t icon_apps; + pax_decode_png_buf(&icon_apps, (void*) apps_png_start, apps_png_end - apps_png_start, PAX_BUF_32_8888ARGB, 0); + pax_buf_t icon_dev; + pax_decode_png_buf(&icon_dev, (void*) dev_png_start, dev_png_end - dev_png_start, PAX_BUF_32_8888ARGB, 0); + pax_buf_t icon_settings; + pax_decode_png_buf(&icon_settings, (void*) settings_png_start, settings_png_end - settings_png_start, PAX_BUF_32_8888ARGB, 0); + + menu_set_icon(menu, &icon_home); + + menu_insert_item_icon(menu, "Apps", NULL, (void*) ACTION_APPS, -1, &icon_apps); + menu_insert_item_icon(menu, "Development tools", NULL, (void*) ACTION_DEV, -1, &icon_dev); + menu_insert_item_icon(menu, "Settings", NULL, (void*) ACTION_SETTINGS, -1, &icon_settings); bool render = true; @@ -74,7 +109,7 @@ void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili934 } if (render) { - menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF000000); + menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF491d88); ili9341_write(ili9341, pax_buffer->buf); render = false; } @@ -94,4 +129,8 @@ void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili934 } menu_free(menu); + pax_buf_destroy(&icon_home); + pax_buf_destroy(&icon_apps); + pax_buf_destroy(&icon_dev); + pax_buf_destroy(&icon_settings); } diff --git a/main/menus/wifi.c b/main/menus/wifi.c index 7a1bfc3..3c8aec5 100644 --- a/main/menus/wifi.c +++ b/main/menus/wifi.c @@ -63,7 +63,7 @@ void render_wifi_help(pax_buf_t* pax_buffer) { const pax_font_t *font = pax_get_font("saira regular"); pax_background(pax_buffer, 0xFFFFFF); pax_noclip(pax_buffer); - pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 19, "[A] accept [B] back"); + pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 18, "[A] accept [B] back"); } void wifi_show(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341); @@ -73,7 +73,7 @@ int wifi_auth_menu(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili int wifi_phase2_menu(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, esp_eap_ttls_phase2_types default_mode); void menu_wifi(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { - menu_t* menu = menu_alloc("WiFi configuration"); + menu_t* menu = menu_alloc("WiFi configuration", 32, 18); menu_insert_item(menu, "Show current settings", NULL, (void*) ACTION_SHOW, -1); menu_insert_item(menu, "Scan for networks", NULL, (void*) ACTION_SCAN, -1); menu_insert_item(menu, "Configure manually", NULL, (void*) ACTION_MANUAL, -1); @@ -121,7 +121,7 @@ void menu_wifi(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341 } if (render) { - menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF2f55a8); + menu_render(pax_buffer, menu, 0, 0, 320, 220, 0xFF491d88); ili9341_write(ili9341, pax_buffer->buf); render = false; } @@ -195,7 +195,7 @@ void wifi_show(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341 } wifi_ap_record_t *wifi_scan_results(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, size_t num_aps, wifi_ap_record_t *aps) { - menu_t *menu = menu_alloc("Select network"); + menu_t *menu = menu_alloc("Select network", 20, 18); wifi_ap_record_t *picked = NULL; render_wifi_help(pax_buffer); @@ -269,7 +269,7 @@ wifi_ap_record_t *wifi_scan_results(xQueueHandle buttonQueue, pax_buf_t* pax_buf } int wifi_auth_menu(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, wifi_auth_mode_t default_mode) { - menu_t* menu = menu_alloc("Authentication mode"); + menu_t* menu = menu_alloc("Authentication mode", 20, 18); menu_insert_item(menu, "Insecure", NULL, (void*) ACTION_AUTH_OPEN, -1); menu_insert_item(menu, "WEP", NULL, (void*) ACTION_AUTH_WEP, -1); menu_insert_item(menu, "WPA PSK", NULL, (void*) ACTION_AUTH_WPA_PSK, -1); @@ -347,7 +347,7 @@ int wifi_auth_menu(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili } int wifi_phase2_menu(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, esp_eap_ttls_phase2_types default_mode) { - menu_t* menu = menu_alloc("Phase 2 authentication mode"); + menu_t* menu = menu_alloc("Phase 2 authentication mode", 20, 18); menu_insert_item(menu, "ESP", NULL, (void*) ACTION_PHASE2_EAP, -1); menu_insert_item(menu, "MSCHAPv2", NULL, (void*) ACTION_PHASE2_MSCHAPV2, -1); menu_insert_item(menu, "MSCHAP", NULL, (void*) ACTION_PHASE2_MSCHAP, -1); diff --git a/main/uninstall.c b/main/uninstall.c index 11c0e43..b678f0a 100644 --- a/main/uninstall.c +++ b/main/uninstall.c @@ -26,7 +26,7 @@ typedef struct _uninstall_menu_args { } uninstall_menu_args_t; void uninstall_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { - menu_t* menu = menu_alloc("Uninstall application"); + menu_t* menu = menu_alloc("Uninstall application", 20, 18); const pax_font_t *font = pax_get_font("saira regular"); appfs_handle_t appfs_fd = APPFS_INVALID_FD; @@ -50,7 +50,7 @@ void uninstall_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* pax_background(pax_buffer, 0xFFFFFF); pax_noclip(pax_buffer); - pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 19, "[A] uninstall app [B] back"); + pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 18, "[A] uninstall app [B] back"); bool quit = false; diff --git a/resources/animation/animation_frame_1.png b/resources/animation/animation_frame_1.png new file mode 100644 index 0000000000000000000000000000000000000000..d55e776114180bbbe35711a0bb87fa69a0da6392 GIT binary patch literal 4090 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 literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_11.png b/resources/animation/animation_frame_11.png new file mode 100644 index 0000000000000000000000000000000000000000..4e535736de0dc3d987aae4ced5f886096a213d9f GIT binary patch literal 4273 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 T*Msy&00000NkvXXu0mjf!%)08 literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_12.png b/resources/animation/animation_frame_12.png new file mode 100644 index 0000000000000000000000000000000000000000..5b9b57d1042da1488a44a7aa9d6252298757b580 GIT binary patch literal 4085 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 literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_13.png b/resources/animation/animation_frame_13.png new file mode 100644 index 0000000000000000000000000000000000000000..17b6ec435eb6b68d2664da6dc9a00108e452fb09 GIT binary patch literal 3885 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 literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_14.png b/resources/animation/animation_frame_14.png new file mode 100644 index 0000000000000000000000000000000000000000..6b72f3f7b58d39640e70e86a6fe9e2eb6683e69b GIT binary patch literal 3860 zcmW+(c|4Sl*L|Lu!7P{=>qwYv(PGV#^&uiz#*#G*k?dqG;%P!jQBk(aB#KhWlAW>S zTSCe*$eJx;Qo_jgdVhc1d(XX}bI(2h-eiiU2@iH3761UAQ>I2X002UJco~h{%O_VX zoc0PP#MCJa0C0T&AK=%~xUIdRXt=RsxNY$H@FdA)^*a*}9(%Vv(k_8>gAb-nq=DbkCfiro)lGQ#8S;^{P2B&28|Jpr- zQe>P%vfM+~;h12PHQwq?G)p36jyR%Abw$a?Dlm*;qlD!R$;lni2PLH|-()@rP@O0% z4S?x-YN|yf*Y*rDuT$cX+&(rp>w(HUdXXSTV#P1 z8fOp5Ox`v@m}*bSq@I?OAvcM&41j_$aeOW3fpqi%x((-Yu5QjrK$Ygppi;kM^i@rR z&`KTz{eq;w&eE3vK z@N{X31e`?PB}jQ3A|Wkb7#u()48Bkg@R9y1qJc*nf_K!~dz(k+0h2O#Cz-D4(O2~t zi#^cIl|{5GJgD42M^-zbTsRkfKrlIc$C69a20`I}02F38AaKNoppoaXITz2-bRC|F zeFg;!(BbMYedaE(Z*#UY#q#+-3#;=fPeQffY_yOny1-!(po%sP0Cmu7PQv+DzP0N} z#CJQ)Eh)5i$tRqle4&oBj-1dz)Gd^?HNK;QTxtyDBFwR#_DV&Y`8KhyQL@;#8Cgr6 z2}{2?NEpPAm?A$YpnhU(A5~KxX)6C^9Xa?PqyBNvnlaM)3 zXoN3(fC#EEWs;~bMDBhqz^jF++?92O-IUUs0}xgi66P{g-mjrXssVgJnSyUe9_936 zPm&F;>PgUv$IjiMpQ3yuB&E}Sqb9%60uhl7W4?XYCog`=@eurAIcctupN9{T4*$`W z;AZ!fgH}s9oXGSqo4Hu$Txt#djm%6sf=hyn=eJkDR4?FQ-4hvvA+l(>rS0Dc90C`w z=0gNC`EuS^kdt-^VvDIDgecepUunYhPN8!kfF&53$*agv5H_8hTbWYcN&jUi{{$ zQ-3>{iAET@!s+M%EP`P6qEDis(&S|X!^{8#pA^{Sj+=kcp++1?y64B&yzX$_WvQLr zo7z7ISnt&OC(S z6D7bgiGy&?9ZN1WvIsLL^`V^+iWu*t*1@yI!O3D&h#jOB%?){-&rOHTEM&zL7&0`S ztF_!swmj0g)$%Gu(Kp3#^V7-V+`B(96nb^jQfE zeSP|d6QiHDWLZ=zBu(n?2WB~A!et@ZL5``~`*AhsZnu8}e`D!^Ct|N_HL&cNn$LeR zdxr|9zy%9uG}s&U#eOY_k0@PLmMIe;LfGHKd+K8Dje+|fe@t3c5|$8J&C%wPOuc!P zMU5(1j3{ULm25ehr|-~x8OiVKO)@+6z7RI$t*DTX==2{)nT9qIwB?Cm74jSOWbWPl zHgQpgnoSZSHX9+1Q}EPugQE`FQ{PL&sXhIgo#LPt8GpcRig-W?CwEsR{|B8{j5|d7 z)>J*c=gPyI*}DnUCsko+NQu!YnM@$8q$jbaT`4#4cvI39&JC6Hp|c8!H5C`%xnJ`- zE+e2d!Ugmt4(=OH)LP=|4bK7wWRzHctmmT#uL9RT8q~dH8d+soyjXpir{`m*Lys;% zrPiMg1U8^4-x1YmjmkDq^cMRmH?{WR0Cx-q=pDd6TEshO&XdZ-#4?ET~V;ST9 zNxk!ewYUU99-2%k3(A`@gtA4>s5YMi%2f7S1!2_3DV{mn3Za%zb4M`KdsEpPy2J?$ zq$f*~%$;yYK4#~7+|k65)5z|t$9504{c)yr*rC$6F0fR2!8){UfqKq!#iuu9YyNn% zHDNhKS;~50=OXis2z|eBDal}s7oXVveGyJX=N#Xcy_znFoM{Jy-BsNbTC@1INOW<7 zFE3OcuH=rMtpGxpuat&nI_4sYB@Zs5v3i-tJrNuZ8>h@!+Q9-n6S{ey z=Q_~OfWvs{j#&`dbOx3;uwxNQxXDe1aZ&I{aHz=1W8!Yp2VWxP*0hCqk?dFn?NqB@ zi0{#G%0HFYd~JuIOLWw5QnWl(g;q5F)Opq6Jqzsm5b>6dT+DZRwE&(xqfc3kwrB%) zo(_a_W>1^~E9o^6fr8{`-q9F(fCNE_=C5_F0Iah@+`OV#!vEC8DG4koLHeVgt{aVv zd$rhw_@bE1tAg9LlsDc3b|X7}phU!a)ro#azi2{Q9ec^Ku37L5jV; z{$J!_CVS(su6ZyaWgzw%KVM5@a`DhX=I&Zi=9`*B!0y?dt@Gad{<}N7RZVZYlOI{r zwUOOQoM`$tF0L-U+wc2$;t#0&u5Mf5&+>CdUM%D!e5plGT3Q3DqXswCUqk{0MR+P@y}3#3c-*DwysUAGY6W@1 z*Jtg@%pB6OwkI+FQR97w{)MT*owJx2T;R6eV|`S?RiusUec$H=ws3->(=)b8+Ur^8 zqoYIRYT}pwu33y&pwr!drNbF7z+wgXD%mvUDl6$W>wSAd%SV!!fwI0eed8acyD{y^ z712BQz-gzm-00Sm>z5$+8>43`J@w$M;nj^lo7RvSXj7TTnNT;Gf$ZMM_6NTE8-CK@ z&KYxKi%*xwqS(+8?lSCp$nrEVe70#+tEHfcPQOi=!~e!Bm)Rj923 zD&hMcI~nzUGhX46#!_arrkhn&0pf!GW@McV=w7R^rB2S{-+wdivcQ}yf6F~H*1|hY zA@qL|+&xcKnkS%0f-5rXcUWKt7r0vW2wC9qff*l^S2+5@IAQs|O9O_=JW%pCs7kaX zZ(CJSZgE@AdQE5i=yHz0kIx{lF(cr&Oj&G*_EFXTp1(Ekmp*nKN{I1}b(a-{g7R<^ zOn}RNT^ZU33A_o)3A&NX72bQbx*u_|`TEX|1&&WbEYkKlB71H&PXgFy@U`hZT%W|v zNuA^jo6^WdD3Ek(mq$mmTtj@TsK1<@{~T_Btq{C);t5nym@lgWlXE*HtWkvPXYkD1 zWlYl&yK&_Zqb=<_7fe#-M)mHN!_&{s+r&*YJ(y8kNqDrV^?G{c45WAi)S_`fv%-k6 z2ka+F1nVlTDF?ANJQ6{oN*3ueIs=J$!#*5?X>jKI;AhQ`X^G+fQw_T0pFeyJaX<94 z^lpn%s#8xN5?+m)4QxJ5OEG(HyQ@Mq3|XeZL6lCesOsVSV-Xx3jS zYJ%R)c)(K_rGGXYz6^=-ZzvV-+7SEWX@;qzp@2&+=%Zh^TOFzAoD`R-SMC(=Q_d{d0Pr zuZy$Or<8H%rao*sOY`S&;Bq3f|GF)obj;Pyz}vH{X9_F760%vJ*K3u%Mv_c>j(tpa zXf+8fG3ITTEp+yK_u5KeGqBbq5A1t&f~4mlCdoq*v$~nrSH)mI!00`&__O7u-d<7K zsUYxB^vLpm=m*<Y$|9bW6|N)KbPiVVNo9AidX)3^ST8oF8t z8Uf{sT88F`{Myx5qB3vTCEo@(G@PHzythx>u9PW{xtwP{Ul%Xs6ejlBW~t1T7zKP&d@tv>3oxUoQ2l2l%`4!e%z$P5S=Rgi5-~!V?MtA@66VM zMyabr{$ka;zzx5{otgeGYAX9X8WNB|8RBvx=!KdMT0000^ zP)t-s7^f~7aPA(W!X(4$I4%)POEOqfXKs9Ia?+00#gt{{GMy#_z{ z5)D297`vUhefzV3*CgOo0!l&MH0b-+*Sq*8`4~H|;-3ag;E4wJ`%j-xioS~`T?HO| zeiGoef_<&K5R`;Ldlz^h@Kj?27*w#8Gwr8rU<=v&P}Z#^G=C$&6)@t{em{TsMeM|w zX_P<*xY{`^$)^BafuA;V|8pOBUHR;gtH#_PvITu40Y-YNHw*NFX&@-SU@Ay}KA#wA z0TOuLL@=I96QEzcANTvmN1D%02BTRH0Y-oyA5t*A7BNE$C9nV}+ppYW*cGx5$OzB_ z{H5A1;FSP#GZ=thP^e%dNPxvz3|#jBS3v?yrf^4)0QUgX)0k8Q32+1W_EsZEfLXv# zDfk9ZoD8}M65tk43%>0L5?~(iQ{oxHD*+w>R1thDjK^*@LVzhi6G2CK&Cg^AFbyc| z%7AuDfdJEjyBV|*B)|;dr|t&d*e%@=Ud|j35~~w0Ox9B;0xa%qtvZ3Af80)hCBHX4N==n4t&VM1Do zJUS~Rz-KO(3Gh%I6W|Df1c(+;2om6H z1c*C=rw2T{;K>2cFL-vq8v!Ek1y2un_6VLIkP#s22ofNEz;g={-~fQ97VH9$5#XZ{ zBtWzg>;v#dfM|!2P5>DJVvS%gfQ$eijo^0zL^%+51b9xtegJO-_%I+NKztGG3h+At z;;Z4F08bggt^m*23liW^fF~3rz_9?&CrE$`G!PTukbox|h`R-31c)Po{Q@!qL@|@;7Vs>B z{Q{n(7wi}CBoXWw@Eqev$AC8i#1O%r0qY{j!bX5i12JO^!uD*!t^s!hBR$Ul3&Oqu z%SEjD?#j-#rxSJ#Sc;(WyWf34WB5G6&H)P%T+8&v1Gtn&Wf&ar zA^GTEzJjD+WpwPR>f7mik0vf(Mgmvuof^1yCiv*P4G(rgwATy90nAP9| zG$~90u1t1Z_G@^a=L3W&04Gzvmz7YB&3k~GZcrMEFE$vU%AXfOHJCsF(3=`=7k9$} z-W=fCA}F6QnpAU*69GYMG(bGWXcE@{nCK*fPiVi%Z^}TF*j-g<$&7^ zsE&zwrVrGBY#2Zp!Aqkcq)ONdwrtH{0n7*%J3-K5$qZI6jZpw*B3R5IZ1z^Yez0L{ z0S(|3%2fdBm59DF6CIQY!g;6!#t{U&tji0^pL=N0qPTuGjR!Cmg#CVyX9QJ0I8+Iz z1z}Gx?E9iB9xH)~A{G$4lbLY*9Ud?iR2hV&s)+pzHXXPl0t^N95G%hgP`RKJJ)i!r zrqZJU#t}4kdUXV$u%O!l@*xu#uZZnJLjW$VfGvk~cmPwwh?0oTg!fCh>6{%G;HDF_ zf|(44%ec`2qX@2m=BjUMF+za(2>SQ0%;v#XFb~KF1T+yeYmo5bK{A98)G&mAm4es+ zdvw!6>^sDRz(E+NP9&@z8BA00! zK?OLkdXZmc1aSf0m;>b7t@>ppG=PgrT_Szs0)hifoEL?H25{aE9|HrL`@Ts)?sbIr zj@!EWP5mbXm^e5$gzAp!;!5{;4Wk2OW<=n8KZT3?7w4qlh=3-8S-{{?5iPCHkdhbo z#|4BuV03;t+l#3{2@E4xNof3L>I0()Ruk$BF6rO0fdQcckh=^Dz=Z(g2y$N869SLV zIIAD!L=0FdD6YYX0eA~od9s8Vu)2sPAYYC>j*V~{!vLVMh~@XqG5er^j&Ev+pz`Az zKt3kmG)+@m4eLKFso`a~K_u|25_D_0_{JL_7ZAW9?h6KM;3X>IeL;lVSq4xOJ^;w8 zGKdn;wAuw-Am|ZRb%WReE9Ww~A_h)?YN2reP}lHoH;8c_=mAd65nQ~%87hv4u>($a z!|FgU2sH=n7CcK z?JNs7MeDk`#;-Pl`h{m$_XF)1;sobp3ZbeO* zSE@h#TtkR{5LrL9%AZnOK(K(>xpn#-d8^<6fPM|HfMES*`()zW0w~wU%uJI;0laNC zw`Ol;CkN=_-M1v6nr03F2r0mMEoD7E^F}~>IT$^lSHqjlEpNX8_%T5~Ea2?bulUj2 zT*F+yMHD}vHS_Kq&+WaS5*z@~yw5lUoFdHpT9^RW z8qWI_m5RR{0k9ZB*nDt7O9V#&EJV=k(Rgx8O9HzbM4CVPs9eK3fHe<7-~iMsD0ZlZ zbAFXilJpPA>~ZDS^CAcpo2mAd-JJt!yf4+-BIu@qhy1vCohWnzLj9{%FN0UIaa%~oLfK#-qRr~Y^a5R8_-H%KLJ;6Z%wV-+T-yuNQ)^H0T?;lVm z57yO4+hmYwz-e4SBY1Q&$ZP_Sl`w|2a6+&(g2($nq=2mvbkA@$Ph*g5XTfhvCw7oq z7j(?svOJPNKmz#t5x~jbvOH44H~{?!9@$bD0FYHiFw+N?&P&$EIH6%X6&WCu0ok`w zC>ey05@Q5pyMUhH?^vts=iW%+U$=bB4>|*kBDeyw8o%|G9%eRJ@!VW?95x3{{{r2f V*21o_BG>=`002ovPDHLkV1f##(K7%5 literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_16.png b/resources/animation/animation_frame_16.png new file mode 100644 index 0000000000000000000000000000000000000000..41d3952a0992e252b0e44322bd3753d41a305e28 GIT binary patch literal 2763 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 RzpDTM002ovPDHLkV1h=1z7hZc literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_17.png b/resources/animation/animation_frame_17.png new file mode 100644 index 0000000000000000000000000000000000000000..47f6323e56fcc19dbde077f61a1973ad0c382136 GIT binary patch literal 2344 zcmZ8jc{r4N8~(ksFf$plQ?j=>(M(A=mNB;Mqv$lUO`{vJUez8(by1R7jiN#B~IHb zJ|Q?PO4~as>STM>C`&SC>FMU}pn3DLe#f-gE_u4wiEvhUY{Q;YsYN;DyScfA93iFB z@^$^)Hl8}Y?ek~BZNvG`lV7xA7Ta#D^_#4(ueZCVE(3szaJHvM3I}=-Owh_w^ z4ygsX+umqhf3&MbJ02_Jo5j5l9#P41r>4JuOxy1*(wo~2*v*lK924fWEH{r^ z*6t}5mjrmFfGixx>zsM~9@hSs;nS#-aWGT7ICN!Gwvj3-x_v-N8725$bv-q~%S$?P zL&iJB+mzMSRk!`yfZsIVLvZLJkC#!Yw;l9@@VX1%$2)c^J|Ku|3KcI^9;DD_%C!Jx z$H~Ai4coWTJ1gL0G?Q67)7)a&ka_(Xr6GiXo`|vY3^sRVArmD-; zu4XE}g1~QU>4U60a47P%tUt7@%4T$NdSoW~F*XHx4H4khRvIloE@>AzK|Hw`&O%f*?e1ZI_d*hn+9li=N{>iytbtz{f$21R^aT2!j z+1kLXeQdd@JAOQIg`ft$`V7DOX{=vI6a<6eLp`yC5xyjACO;=4= z6Z2GySw)UjAI+xg&Jz5zFHM08g!lsKZ- zN=AO=&hWChmko4)5qAfAA)P{mEo_rR_;dF`_FS=|AMHnsFTpmRKKM*S2b^bDN?TB! z^-a%+r{TR?{573cOD-Mboh`-e4UT2<*$hqcaFJW~hT?ZK=;Ja${D7$UV>YSP#%E=~ zpns$kxp&f>P>?@ZHqgsA1WpA-Bej@5z@`vl6=bEh&_id|0Ch2YP;?&;sYod)uCk&i!sFYue^sW zUW`%2V{n-+Iuv&Ty(2^?bIX0qgkBS??G{!ca`_u%u*ODZKMDSvsT zwjyvie`)Z{vbqsE1yrWfNuF+)u#DYXAeTb?^V;CXr&*5``%K`^I$PxU!o4@Qu9#D= zNy`kpS_c0r+N70~3(lR#2jkTo1ke%cGo~Y;bGS+C;kALc*ni1Z%$rN@#=W*W`>-}{ z%>?bV)rOX@i1?R87Bue|7a2C3Zke{tFt}pQ<=qmKUsP9EBa6oxA~rW%l|M`5Q;pcn=cAmF z`i41B3#s3smQx>RlonHF%s4>SZ%kfJBUnO*@hv^U?=D%jh+9&Eb!BvmGlr)2He_t|KLM4j8Li&f9;pc_BAJ@?C zD0#y@hvX_@j_A9i)=*moblDOTB^^@m+d>^6b#qnQPuGmN*n~lPlpfXmPvj`@LBo?T*Iq=E5l<|PbAJy<&Q&B`_U@#FF{RTv-i--$MF&Nrj$R6iQ0)8(>+ zLz$Y0)hUM0Hkoac_x|Opi$6W<7cB-QHJ|o%c)bvSUYhVOxD$SF=Lgz&ne?#Hogf#u uU|8AvtxQAEVQId3ds@?7--TY(vvr_uUF2)HeY#Qdz5r(j5BoY>2Kztp>>Jep literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_18.png b/resources/animation/animation_frame_18.png new file mode 100644 index 0000000000000000000000000000000000000000..5db8ed80401c8e618a845959d6b052d61e9e0457 GIT binary patch literal 1892 zcma)+dpOkF8pqdfE||e_8@cChyA(sa$81{|w?l@AI{%a{Udu9J$rg#od%-kZo+qoipw8oag*|*7LmU`>yZ1)@QAM-j(I)@poywDjonJ z?dsxq6adIhlnW)$qS<-b-Cra)x{H4z0Fv_G1A0B4z9ll%lAQdKylD|hDb$2;;1C|0 zn3|~Nopkn8G)>EgMvLXuemF1MX6s4uaoT&UQ?F}|WGY8-v+(ndOt^26kyesPx2yPdEG-S**|)qj^HuZAYDf8Y|H0ke-4vx~l>ms{c6B8CFh&-} zM}6-SWnrq7*DR!XcX9Wgy}VYHYt(Ga#m^NvZC{dSpEgBa{} z%Qw#q(F`$R2yzS;FX`E+2`55NQEXSb_tm5saegeaoEpb`TC~+42^N=58+8?Sp_bO+c;+hnY8LX|%! zVQC?$WJNL4?d^Qv%d_f9h-0atV=)i4RTyO_qN^3HOU(%IdvwwCE0&zKM`RUh&<&Y- zrDR>&Kx{}BBNoEWo*9SVo5T5-Mx!WPbcbklz(iMP!&Dvm{Afw8yH!A}J z;eS493;k)NU}*=kI3a1Wz7-8@oqAp*GgZ{b2|MqrH_8C-&oB_17?yezRvz!0G* ztb?Uu&(4GmOtpKZ^e)#Uh>Ia&>#(}6Llvcf@kGD6OgYinet?P7h?R8&mS2X zGH-1uG3-YRMdvCzH)yjdd84xfspGSc-I3r;>r4$e%YIK8Yi|B@AuA4rA_c#=aQ?!& z|Io(Dk=sC6B%nn=q>Q9OhzsZw0>i##!vGtwsPp5IuYew+;kN2X0Fer%KxkvXft(9a z1&b9F(E8vSGaF+=v%L7XHb1Ya6EH$N&m@q;$T%xWE`#7BkJhrH#!< zwvTLPD#GBizNq70;!zU(`%Hj(d{2ZB!$4ym_;x+5yz~*z|5P~mI?!4yi^Ng7Ig%wS z2Yf6GnHaK@tKPFZAl}FV9*q{#p~3IF03%KwHIz}yE?~-a_JS$qo1;xC0m|{04khoHYLGl(@2-Iut;rQ7vSfb3W~ZrW2||rH1fUUAMenZ|e+i zF{PkMdo?VCXjM$rC1{}4kfx@kmMv3o8h14-es6@#+6R1ET<+vx$)d}U*>NP#d9ixC z+GI+rGFWXltAf>USa-h<5nFV)qECUc9SyGgnxC^P%hX0L{@!jFmcO0OIT2s3%?}Lh zthxNN{rDIK3X!^0Pc4FJ&*A_8OjmVngz~h>|-w!dC72VfoQloc{sA|Lz({ua`G=* literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_19.png b/resources/animation/animation_frame_19.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd9465eefdec035ad34589ed38147bb140ec23d GIT binary patch literal 1464 zcma)+X;70_6o&7Y1;V0+SY?x~z$g$ylqhQ;APA_1un1vMVp|Y}nn(a60>&W4;Gk_a z1Pdry77GGeN(lQ0SXvbnL=g;>AbV7nK}v=M=x1m8r_;{#$Gzvd?|siXGk4}50o~7g zgQkHd0APc!k7pnN5DnpZ92(j8FMbw=1P!)N7zY3?!p8+YA5K>xqG6&JBQb~-lX&J> zLNwSH9nU$-F%3#g`tk(JG?>MTZz~&3MQnTNhl0I~@3dQV$Q?*JhYs#e_-S`0xjOp? z(Ul>$QkT~aIj5TDW?S?btqTi1<;2B>sShTnms<-~pYCD!yUGB-LcX5v!Q9vHhX+5; zCx7A zX()tHJ^&%0fe_9VLI|?>r}=+a{-Yib|8L#ko#s*?=^^%Sexo7ta5FnHBiQQ?bTt7^!;$#UluUH+ZU@laN8%-DU0SPqC=(#Tj-M`dj4Cg*; zKkE)eE13jJYg(xl*BbVmH}~Rl;RY>6qg6n)B?0!r>WPZXt^<-4E#k~VIT4D)#D>0^ z?r0Q!Cyz`&X2VhT1Saf-XDNycCB;sXD3kk#-%9!?VJrQ|6*=$Z@z~gt_x;4L+6$y% z&PhCV+4ZP0iZH6L>E3N8Rb^E#(!DmvJHLe62dJaAeHcs%8y+y3TTQcSa_p=1_BM^BZOFiaspGFkQYb z8H94x2YxvxA1+Yz>rZi}I7aaF`8&mFMX9CraT$+R-sX(5UF~}!sg6ZHirL;H_oQFN zXtd1nIO`my_q?%Euzq#a08Z{&QU%+4X*$+wZntFYX6C6SnfNzIuO)|7zD?;QG8~F< z>66#uF3ArkCD<^~u{VcPO^(Lx*X2&c>huR_pkklCSmV-kwET>pLDQIMy{Coya(Zu& zokoa8j=Ta<3AR!i+nQdZri#8 zC67vQ94b&_&p+T!C$zz2@zpNyM|ea>on4?uW!YM;5&W~QU2=1Y^OY?lCB4;XKz<)5 z2xz0-1%}|-&Mr!kBSp+_Z}Ep!0P-XPK9vV?=)oQj$4{`KxJ-Des0Q njv1qY$_wLzm|WzD1k@TDp6UFM{o9~7GERZ7m!D^q2b2FNljvqg literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_2.png b/resources/animation/animation_frame_2.png new file mode 100644 index 0000000000000000000000000000000000000000..ab8f15f90b6758a194c05c2f12311d5313f6a2a4 GIT binary patch literal 4092 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 literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_21.png b/resources/animation/animation_frame_21.png new file mode 100644 index 0000000000000000000000000000000000000000..81a593788aac0170381e634c96cfac6e5458ae43 GIT binary patch literal 1740 zcmY*ac~q0v62D*ak;FjQmjnbt5Zn-uP4H1kKtv=U5~^&t6%++6kr&$2^UnKYW`1|h%^Fj8U&^NnT4hdW)qv#w_<(9?n)<&%Hi=Tq+% z=}<_0@PO(Z??+lf*ZjP;$>Y`h#DepY#V)!2k^jod%Cc(aFBGo|c7u5P85SMC@C;kMojn7-v#x6gk{7NL6?6f-Xy?pn9>eD96k zgUCMW4zSIOH9%uOp1X3r^>aM3rO`d<;%US-CoL~o*ZiL2UeP`D1|QX5b>a%WvR`_$ zwxg;K9O9Ti*nCpjLhzIHTf?7u=sl34XvJ9rsPUJhNQF#ijXAI=7#k!rT}jxUE(lSo zphSH8))StRevyPYvuu|*&NuIVyQz;@?j2q))?CDnX3-yuzVEk{Ea>b@*VL%pop z>hW?oH?(1(B#GHEoBjTK@km4kqcz|0VCp3b_Amar!_r}Av$5ur|Hfa{c>WFY4bb6bzI5YgxVQ6y^`ulR@2QUrR&Q1(E?LB_NK^@ickS~6-q|}S6Qja`HZ)YgU zJgMeIT~FKndVA&*&r>VKJ~O46+L(Ez4hIce=T~e8oK?kH{Z2zt9ZDoy|d)$vkY(7)H?(Dl?$+$(=eN}#DG8fb_12)BPa=BF@Dn;bs< zE4W1a=SFUaui`7HrH&*ujhIQ=1tX%94^3-k?PtEjEEW1r#)bS&T6qV%S*3?xyGzD` zk);b%WShmW`t2!G1ZXD1yK}GaLinXD z+PBYgl7N_gSSPvOk_9cDR2(D!yn|?XfMJpANmzqR$vbG$>dtP>57pS*gG9k!M7Ogs=cqzPa%G7k$AgbHdb^ z;+u37+_MQDSv1Z81!pl@*T}orZjLy-o#6d72}eRw{z`~bj}ksIq$c-d8U=eu0FK&L zy4Scc9)@6V9tBTAhU|CvVlEc2xw_rogpMA`p)vvVKxOrR4m%@={edCbFm?cARjr~H zbPv8#_%{(9O%(k_|37A1Kn+;ULi(x|uv)RYi4D4v1vs;CI4HfE_Geu_0KY4ij9ep; zArrgRqLX{#z^QP0xpi}Jmzy)tac?Yi9zyV%*V4Yt>$n5ScHin_Yg z@-cesd@EOd&}W0AHaRu`Gj%N!FjqDav<3SZPFm4EoMPfK%+!bt_hf>b4625-H_D*} zYgl{371kD{n_x`AAFrH%)VC1|zIhf}cwjgk$)!+|qJfTbeo;{*pxD$66q9j*2kzOc z!`|2ex!Fk58W*(21r`LI2Bq%sTple4gvG2`A&!j-C&kT?mknPGhzyU}*&K>U{tLCV B+qwV% literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_22.png b/resources/animation/animation_frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..59cfd17bc9e62bd348ff6feacaf81cac8e425e03 GIT binary patch literal 1825 zcmbVN`#;kQAOFsGGqc!;Hh1QBI0)_FI4;A)+#O}K6pm}T%w@!@oQ+YTQw~WU$?_z5 zdTb>Lu~~A+B@eR2mP?^$%%vWeGH+m^?uu}2!wM~CwTWHPxL*&}vG}Un} zrCTG6yRk9aWbtKV=|6gD+uWMHk)IBI=4=3fJe5RrJ8@x3D&}6Fwo`*sKkTB2(kWI> zsd7rO2=cMPz$5LGo<4p3j9RUuPGka?f{*WH`L^e*gurZq|lQ7F8n2oN11GMWpQJK|tAb!^Lq z!nO=0Xf7`q36*xTD)qNL1ON(DgDK6{!zPF87a`M~eQHoTX841D6y3x{i1Zh%%W+Zp z0jr@NOPfmCer*$;9KJKO(eM!xwrkkivNdx=NEq+)zckpgl9BcIf}&z-e@xHuozH{Y z?=XKIN4tzFA4bYnu+~9hU3}ESXwuoZ@T@+UlSYr%NJGHD6HNT&dSQUR0wB=F4#l;! zyb!SqV(30<@(S%13`9O*FRI~ohM6%iQ@HaCaZ&*ewnOseCFpFatg=u4F+uugtQ96y zK^*gpK~W{ZN}5iQ#QlrDxh%7Sj9+=GANh#e8jc+CKcSa1d1h26Cu)fh{IGnKw%T1+?^vf9_* z8Cr&Yx7Mus$L^n+Z|0tkr2g{vMr8_;=BOB8Q%}Ga_y&v;SL{ z@+Z^gt+S!Vn(J%#VBKHOe}QZ7bl6!zkc}7<%OLm;=iSOSuZiN_^jcL(s4)Gl`k#>Q zU;H@dFn``q&E(br-_YM43@^S}B5)N|&sE5N8I`&B|z zG1x`ymuioe-kfYCw{_d1<~1zqR<9O+yR2gDA33cdj-J!-uQMyx7PVRtO>1I0i&R&i z6-O0%k1yi`WRRY*~ zCoqPwoM?CmsL85f3a8DmX|vUsUPd%O+`mfNS>s6MD_Jw_2!Ywo2xv|71ar#WC7j8k z7duj)FtiUfnoOO?tYSe=-To_}Am5Q%J&tqNz3-;$Kw`R~3=L2<_r{no#d)3?PIebG z70Pt6jg4R+8_!~xdSPV~Ke*{!ILDlOBUYVYm>$PM4qO6Xa7zFuOFs7UI6}fJFCtZZ z*n{TGBFd{B;6~{Z-r>nTLmiu+UHIO-jaKVPgFE$xr7-QTRXu4{NPv!m9sdIS)q$)G zwT{zm9efD>6%i1v8|q}vui^#YDsSZ31Y+xJy@n5nr)i?YU0y^i#sSCgkH3eyZfzSB z3|HoY*OV=;fN*3z_%m&G^fpaKK1!AF7_T(v7Svzjh=6hl`opGP0j?KNE=DiF%sp_u z5Zig4fxW52TE7O`K$%KldZ%hg^bAxv`;)jw+xRpI@BJ%HsXzIWAraiVhDHf9s%E=} z9>w490k=xg8#L-3t{1vhj8+k*{%E# F|1Yu9K12Wj literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_23.png b/resources/animation/animation_frame_23.png new file mode 100644 index 0000000000000000000000000000000000000000..9d25bf6f4f2528d34f0e96c2ed47140e2a24b50e GIT binary patch literal 1836 zcmZ9Ndpwl+9>;&r%riV@TxRSvqKij}%B>w@6U_{!nUcJ6IU}2NL~@tgi1_UjNg?M{ zn_^u`vQ~IGX;s!dbh1Q9F}W3+!%{SEWpmjx=XGAM^T+w)^ZmSE-_Pg!_cznsZJ!2R zj}8FP5R04-1Ay?9tiY(sDV#s(qcpWBkxvW&4DJ62=^0L0Q7$*cx=3O@B7+b65viVlC(UU1V3zqAFuOpH}PS<{&ZdIK# zw6|=}z@zMlMqP<)cDB31Y<_lPK|lJltYEd%##@gz3xJAL?9BI+4onYC$Nz>iRYtH~ zQv>CnQ|sFIiO9TC`c7^~dv#&m(PTn%Rjceh_{42 zTh`?{VPqGwspJ6_MpFGO`0j}=AO#IZwIV$CUT;UHtiQ`}k|T;=zC2a8Q3s8()FEo2 zn*Y0#QB*PkJgI@%#$jLyDIPqRpW=DUQwMGM6*`m@^?+omRRv`@*tf1w(6Y^3eW~Ll z;#+sqiN=$u>~=hB6Xvw^sy{1ULvafU4&kL@502HvOY8gSg+EL-tKAG&xkUMtaqQla zY7WC9y?c?oFD1KEUNm*oW)nG4K`MirbEKr{At$hm35hdXVVGgPjlXqf5`T3!{FeJV z?8C&?=vpJUD`f)4K57&_&-J(KLVo;y-9EyEtySJ@ z{oRv`3AOJ`RICZZznGTap*ngcy-1mTClrJ^X8pOVixK_Y`@qNrd1~+Sk0&A@*6Qa2 zVckW`qF?cf`;KglJlFb0^O2e!#X}zldKo-oRh;v2cIx+U&J5yi1#jlLyH(4P*VMIT zopD9t@jVCs+7Dl3d#VPXKSC{VX2MZnpdyJ&fDa$<8|zTys4ox_YDImaMB72yBn-Lm z)+OB!`t@hL#OB_*obGIH)SAkl+dkc9Mi;WKYOL2ZtjZEUCU(@}AfPFUF4Z`i-p`tT zbqg70Nli;%9^%k$W6x+=3oF7s;ToOV!l(EBdDjZZ%E)~bO>$%-zjWoo!O>@Ngq&FAx=lj4Y_)3PYIkGbxyiu19++DAK0Nj@ z?0xI*F1n7R{+%HhnJfU~5hfjPE$eOZW#+EsKd}ay8kpmhg4QzzMo93Xt7^5hAkqk zTpx5BrS>Au=lQWpvDW=e2v9tf54_b{MNAk+nPZ{;cVHNbNOs^ld_ZBsN*W@u#I^XK zPrY??6bs~Wl$GajBYKMJcNtVV5TJ5H?%KvOO_8G#H|93rAXK%~0wbr8Oa`SUg*QL| zHV_?QLT#{(F$avyFrF@qr6HzhZY92fSD}olBG25f@V3XoEO1JwBr)F`vT2EkHXAKA zgfS99^nw{mw1 zh664Q$=imJu6z(gr&xI~;f-}RfXl^5wqp7d=l|lE9V-93S<)|Ecca($_H3l`g#odP Kn{$=GpZGTb^AQXH literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_24.png b/resources/animation/animation_frame_24.png new file mode 100644 index 0000000000000000000000000000000000000000..47cecee08b7185386be3cfa583a9d4c9c8968e30 GIT binary patch literal 1215 zcmV;w1VHYAX9X8WNB|8RBvx=!KdMT0000R zP)t-s9-_h|!|FIL5ll-mZhUHmFt4UO+Q0Gf^YQ02>vhEd00bjRL_t(|+U?vsj@v*C z24Ev6h*G5tC^!eu6h1(T^a11p7+4=*X@GTZo?>@_B!J~1hmRkE)ou5Iui?mEALQR> zAzH5=&r0X(%PghO;lICmq2u~Da0LSjFSLFLUw$3&fWkz_^E;5}J04KlLhGB5i0Ft1 z6n<}%3r*962$V*2zFw~v`uck!MBuydLK7-bn2zVS6o0`4$}1h$3r*AX2^1)=3#~Lw zpJ9Q*M07YDaDmbm`gS-FE>L(OY@i4cVFMW*C=DGb3p$X|fwG_j867Cg2^`2z;NF4K zp1?ui4INlpgnIzWPjp~=0tbONbYN+h>;hQY#$CYn1P%f#i*P5v85~$ygnhspIVmz(HWypxF(uEXVr+R^@mV0xQqf$2Hc0@E8= z4{YGT><}7&GdM80kqtmb2j;#Hwg6MB&;-n^LJKf8gf?Je2u(oY`GgHjoP|bUUI>jq zX)`!5Ey-qJ)~GQ9Woh8Rq!8MHNg*5nGM%u2(i%80RFcRo8uS0psQL!Sf#$1lpkKv_nG(Lf%3 zu^9~2j5;N0LN~53Ba*CR0eQpT&Dm>PP`Q0$Xb&FWOU$wFbinzlO&+M zPqKjKQIiI=hA<6i4PhS8c;Mv$jUh|~+Nv-SXbWK`&{Tz)K-0IHQ-PKfITdKB!d#%? zhL;O8-0+ft`Ws#{P#?lQ3YepsJB8 zfU3Q)1gO~yOMn6oRQy~>5pV_vDke=4us?)F!2S?c0T~_GJ!#5-y*Vxe_O?kGus6qb zz|J-)1kT{V&Ne9o_Jyzz*w-eNz^)HzD}h}(t^{_qNhz==gtfqqmkg!Ao*dT#JMN#g zz>WjC82ETtR|6kbVKtD^fe&+B4tx~CCvY^!^}q)qtOq^_ApmfD6aoMl9k~7D#|S`% z2hQNY%{GAmZiNs6xHSqffEzi+0B(HY2LrefLJ;8nHUR>_aXoQ literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_25.png b/resources/animation/animation_frame_25.png new file mode 100644 index 0000000000000000000000000000000000000000..494a3cb84391a384a19771fc0a6dc5f2c77a7b22 GIT binary patch literal 706 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoKQJ)^Ngn6WA3%z;z$3Dlfq`2Hgc&d0t@{HM zlrC|NC<)F_D=AMbN@Z|P%_*)dRtPRBP07qx2+7aSIk@~`4akT9pAc6IJwYFDqr94| z{U1L{@2^Qq8vDv+mQ&vf`9V2gCoyelfo!H+Lw9jtN z&8hcWtz1+ELNZnroZY`iDuholME-nD*<~f}-M_54P92)w&=eH0-~R1v$I`RAZ{Fe4 zTEAMtGaz=wDwn39j8`p9r*_X`O?_?4y{h`L)7Q*^-KsqM0x#6ag+w2I@HB?YL00c+ z5Yw}-lUT2wOT3^LQs}zi(`)BNJRzH#4(@4n+$zWxKlSy}1?RFKCVlwRxkzG*F3&50 zL<9YV0BNZlVV+w;Y+p?eKImq*W!hrNv&z$;!YZ9*3oFm7Cqf4s+C_QzxFu{V|E3+7 z{FkS3Ta)A6iw`EU6*oD$O1;Va;L+^dbnyAqj>hvV3m(Yw%4L4g6cva$xZvW0V69}f zev=uQ7cSj6_<#3Imfshf`GMRI6WMfoWkY0aZ}iL6EI8=X+;5V>$@Vq+JOot-b&xH|`UlsPN;9{;a(+xVZ`w=<73v;3chP1+LGBbF{a{ZrMd4?nKm+}7@O zS9C%9^+3Pxnft?nUvgde`Sgp=StHq3+DlJ6)_-Z=tz_%re&9p)f%paG_mke)yzr~C zs!h!6KU%z?e3FXk)OlC?w@*p_sFU&5_-@dx)OyPbPcQ5DPL9);y>fPZUUsj&N$;(V zSV?5*#s%&3^Ld!omUgk)>NGoUuQiA;JGkI}u)&P{CdYgr^*Z5#*TIB!&5rqt4ld+p zJFO(~#f-IiCm-A1PPW|{AB?3;6et4^?AiA-SRPCIYj(xS0vOZ`p00i_>zopr0Jxp6 AKmY&$ literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_27.png b/resources/animation/animation_frame_27.png new file mode 100644 index 0000000000000000000000000000000000000000..4ba0b0f962c53b7f580d18b998c7b7bcb14d80f5 GIT binary patch literal 395 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoKQJ)^Ngn6WA3%z;z$3Dlfq`2Hgc&d0t@{HM zlrC|NC<)F_D=AMbN@Z|P%_*)dRtPRBP07qx2+7aSIk@~`4akT9pAc6IJwYFDqr94| z{U1L{@2^P!ivRO;aSW-r_4cwN-=P2j*NakzgeJstuM)eguzSjWu0y*#eqHX}Qe@O2 zrZG!fp!`Y4Lpdfh!lmY96f8WbVPbJ0Axugp)8L<+k0S*j aYu?2Rb1fV*gp7e9!QkoY=d#Wzp$PymdWCNQ literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_28.png b/resources/animation/animation_frame_28.png new file mode 100644 index 0000000000000000000000000000000000000000..e132200e7757d362f98fbd472b8e7b6fde2b7753 GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoKQJ-_N$zopr0C#mQ4gdfE literal 0 HcmV?d00001 diff --git a/resources/animation/animation_frame_3.png b/resources/animation/animation_frame_3.png new file mode 100644 index 0000000000000000000000000000000000000000..d55e776114180bbbe35711a0bb87fa69a0da6392 GIT binary patch literal 4090 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 literal 0 HcmV?d00001 diff --git a/resources/icons/home.png b/resources/icons/home.png new file mode 100644 index 0000000000000000000000000000000000000000..03d2f3df15bbd4cef0f5df0d5b065af0e5f0db65 GIT binary patch literal 439 zcmV;o0Z9IdP)H(6_XG@JC(9P9}7x5|gr%CYX@Bu`SolZpme79(v-8&EuJM`4e zf;j*Pbm^&kJRh%TK*8MUj&7tmz+&u0+Mr6KZwLdK0VJW%H5vV4K7;s3ua^f<7Q0Rs z&z1w2#r`Mh0ajqH2e8|@nUc^q18~{7fpS+=3s7zBg;z8Oz_z@q$b25+QVRf}SE2b- zTV((%R|C)hIDL`{a)9lNyIk?{Xh1Nso#mFmPjdk1pWV8Qs(l3wKm+ja044@Kamkr= hzuT|b5>@*Oz5w1Rbm)4hgh2oR002ovPDHLkV1k`DwGjXS literal 0 HcmV?d00001 diff --git a/resources/icons/settings.png b/resources/icons/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..2f4a61ce837f599d1f461d8668eeac1296572ed3 GIT binary patch literal 1050 zcmV+#1m*jQP)Ki|{B=r%*Pgc&bdekI1}cExV=)<7)NT@B)r0bYpn(nW=4i9@yv zTfNuGVp1K^1vDnp!JHTV2PVepForQ&6AfgWilHg2=kcQaXis}eOXzd?ect!^e$V^9 z&yOQBqgFe9e4^p;rH2WF!91d5CY_1>U}KMJjTNk!9XPV|V9e0DZN%OTh4-xj7ix{= z)ViTx(*e+Hr5?SW_w9iI2}RrEZQY%TX0LB0JU;-ml-bs@5?MIo1-m;F&C&LFn}o`8 zdEdZ^aN?MWFM%h4HQ*X@CuD)F;hqG=fCirrT1UL+VAox&3DYp>G|(txPRjLtn>v5n zSAc|~;l$hq`JS7MxtTvkpEhHQwCD<=;SW0MMGj!%sS}ZSdm&aNU|*o&KKWy>m#t-k zy6-FC>xDP|2K%Cp0PGMCBQI1^i1M$!a&?UPg1mjh5&*T)SlOAjvy&QiX@0r_K}O^rk= zk#AaWB=H@UuVWePaweU<7~JS;=PE1)z%jd84LN`rSEY&qT0rlF!c8G(yQS_F)%mqr z0SeeJRW2Q>xf|}O8RmeJRjrjhQ*B(CbF`|34oN4I&cwhj0n0l?t?sG-Y{#|Bl8qls zb(}9w!^pxJnO(sr4NcZRyBM3U6ge76ye#l5=WCK)J&3X3Ah$GTt{+%Mkg0ITnH2im`%TZ7x7yi1>Cu{_|`pN0cMzE zxl=OcHlQrnr?`67b(}8A#)0(;1Z$FwyN=UkuKOJD8QU}F9ar?3XWA*L1CSrjUucWB zDwusR^ZVGpg}!L(`$IBImSdMRcw=#-sB3n2CYpn;9lCihw&*4I6;R3gH68dbeD_aE zXNTp3>5eBhD_pf!b2rQr*kvy+*@v}~!!G}hGwJNbP`D`s3}F}!$pfj*OS_5nKNh8R UEz2+6WB>pF07*qoM6N<$f?Hkk>;M1& literal 0 HcmV?d00001 From 2782665ad8242d224be5be83f661a293ae7dad25 Mon Sep 17 00:00:00 2001 From: Renze Nicolai Date: Fri, 3 Jun 2022 04:39:49 +0200 Subject: [PATCH 3/8] Add menu item and placeholder action for hatchery --- main/CMakeLists.txt | 1 + main/menus/dev.c | 2 +- main/menus/launcher.c | 2 +- main/menus/settings.c | 2 +- main/menus/start.c | 14 +++++++++++++- main/menus/wifi.c | 2 +- resources/icons/hatchery.png | Bin 0 -> 725 bytes 7 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 resources/icons/hatchery.png diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 209ea43..2c6644e 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -37,6 +37,7 @@ idf_component_register( ${project_dir}/resources/icons/home.png ${project_dir}/resources/icons/settings.png ${project_dir}/resources/icons/apps.png + ${project_dir}/resources/icons/hatchery.png ${project_dir}/resources/animation/animation_frame_1.png ${project_dir}/resources/animation/animation_frame_2.png ${project_dir}/resources/animation/animation_frame_3.png diff --git a/main/menus/dev.c b/main/menus/dev.c index ff341e8..016dc06 100644 --- a/main/menus/dev.c +++ b/main/menus/dev.c @@ -43,7 +43,7 @@ void render_dev_help(pax_buf_t* pax_buffer) { } void menu_dev(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { - menu_t* menu = menu_alloc("Development tools", 32, 18); + menu_t* menu = menu_alloc("Development tools", 34, 18); menu->fgColor = 0xFF000000; menu->bgColor = 0xFFFFFFFF; diff --git a/main/menus/launcher.c b/main/menus/launcher.c index 25c7e44..b9f204e 100644 --- a/main/menus/launcher.c +++ b/main/menus/launcher.c @@ -30,7 +30,7 @@ typedef struct { } menu_launcher_args_t; void menu_launcher(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { - menu_t* menu = menu_alloc("Apps", 32, 18); + menu_t* menu = menu_alloc("Apps", 34, 18); menu->fgColor = 0xFF000000; menu->bgColor = 0xFFFFFFFF; diff --git a/main/menus/settings.c b/main/menus/settings.c index fadddc3..2057e65 100644 --- a/main/menus/settings.c +++ b/main/menus/settings.c @@ -42,7 +42,7 @@ void render_settings_help(pax_buf_t* pax_buffer) { } void menu_settings(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { - menu_t* menu = menu_alloc("Settings", 32, 18); + menu_t* menu = menu_alloc("Settings", 34, 18); menu->fgColor = 0xFF000000; menu->bgColor = 0xFFFFFFFF; diff --git a/main/menus/start.c b/main/menus/start.c index 533cdf3..5de0a34 100644 --- a/main/menus/start.c +++ b/main/menus/start.c @@ -16,6 +16,7 @@ #include "launcher.h" #include "settings.h" #include "dev.h" +#include "bootscreen.h" extern const uint8_t home_png_start[] asm("_binary_home_png_start"); extern const uint8_t home_png_end[] asm("_binary_home_png_end"); @@ -23,6 +24,9 @@ extern const uint8_t home_png_end[] asm("_binary_home_png_end"); extern const uint8_t apps_png_start[] asm("_binary_apps_png_start"); extern const uint8_t apps_png_end[] asm("_binary_apps_png_end"); +extern const uint8_t hatchery_png_start[] asm("_binary_hatchery_png_start"); +extern const uint8_t hatchery_png_end[] asm("_binary_hatchery_png_end"); + extern const uint8_t dev_png_start[] asm("_binary_dev_png_start"); extern const uint8_t dev_png_end[] asm("_binary_dev_png_end"); @@ -32,6 +36,7 @@ extern const uint8_t settings_png_end[] asm("_binary_settings_png_end"); typedef enum action { ACTION_NONE, ACTION_APPS, + ACTION_HATCHERY, ACTION_DEV, ACTION_SETTINGS } menu_start_action_t; @@ -44,7 +49,7 @@ void render_start_help(pax_buf_t* pax_buffer) { } void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { - menu_t* menu = menu_alloc("Main menu", 32, 18); + menu_t* menu = menu_alloc("Main menu", 34, 18); menu->fgColor = 0xFF000000; menu->bgColor = 0xFFFFFFFF; @@ -60,6 +65,8 @@ void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili934 pax_decode_png_buf(&icon_home, (void*) home_png_start, home_png_end - home_png_start, PAX_BUF_32_8888ARGB, 0); pax_buf_t icon_apps; pax_decode_png_buf(&icon_apps, (void*) apps_png_start, apps_png_end - apps_png_start, PAX_BUF_32_8888ARGB, 0); + pax_buf_t icon_hatchery; + pax_decode_png_buf(&icon_hatchery, (void*) hatchery_png_start, hatchery_png_end - hatchery_png_start, PAX_BUF_32_8888ARGB, 0); pax_buf_t icon_dev; pax_decode_png_buf(&icon_dev, (void*) dev_png_start, dev_png_end - dev_png_start, PAX_BUF_32_8888ARGB, 0); pax_buf_t icon_settings; @@ -68,6 +75,7 @@ void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili934 menu_set_icon(menu, &icon_home); menu_insert_item_icon(menu, "Apps", NULL, (void*) ACTION_APPS, -1, &icon_apps); + menu_insert_item_icon(menu, "Hatchery", NULL, (void*) ACTION_HATCHERY, -1, &icon_hatchery); menu_insert_item_icon(menu, "Development tools", NULL, (void*) ACTION_DEV, -1, &icon_dev); menu_insert_item_icon(menu, "Settings", NULL, (void*) ACTION_SETTINGS, -1, &icon_settings); @@ -117,6 +125,9 @@ void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili934 if (action != ACTION_NONE) { if (action == ACTION_APPS) { menu_launcher(buttonQueue, pax_buffer, ili9341); + } else if (action == ACTION_HATCHERY) { + // Not implemented + display_boot_screen(pax_buffer, ili9341, "Not implemented"); } else if (action == ACTION_SETTINGS) { menu_settings(buttonQueue, pax_buffer, ili9341); } else if (action == ACTION_DEV) { @@ -131,6 +142,7 @@ void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili934 menu_free(menu); pax_buf_destroy(&icon_home); pax_buf_destroy(&icon_apps); + pax_buf_destroy(&icon_hatchery); pax_buf_destroy(&icon_dev); pax_buf_destroy(&icon_settings); } diff --git a/main/menus/wifi.c b/main/menus/wifi.c index 3c8aec5..d612074 100644 --- a/main/menus/wifi.c +++ b/main/menus/wifi.c @@ -73,7 +73,7 @@ int wifi_auth_menu(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili int wifi_phase2_menu(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, esp_eap_ttls_phase2_types default_mode); void menu_wifi(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { - menu_t* menu = menu_alloc("WiFi configuration", 32, 18); + menu_t* menu = menu_alloc("WiFi configuration", 34, 18); menu_insert_item(menu, "Show current settings", NULL, (void*) ACTION_SHOW, -1); menu_insert_item(menu, "Scan for networks", NULL, (void*) ACTION_SCAN, -1); menu_insert_item(menu, "Configure manually", NULL, (void*) ACTION_MANUAL, -1); diff --git a/resources/icons/hatchery.png b/resources/icons/hatchery.png new file mode 100644 index 0000000000000000000000000000000000000000..7cf5a6a5a5a973af43f9f0c98612ba60439796fb GIT binary patch literal 725 zcmV;`0xJE9P)&sPGI<#o1hyNqc-F<+2|$ES0AsmUT&!1fjdqp| z0N4dEpWQNAOc-47Qu#JgPJj@=`>B_6#e_rCL+N~5=%)Np%ZD&p-|p{0SYxFok^(-i z;d<33z$U zTMa@)%vjx*>#o)!b2c)GGuaW@r7<;fJbG>XU)nyF;0F)fOR4dOwx}d83^>oJq00000NkvXX Hu0mjf^j$uu literal 0 HcmV?d00001 From 5ff7e84997dca973132bb8b07a93d70f8657e00d Mon Sep 17 00:00:00 2001 From: Renze Nicolai Date: Fri, 3 Jun 2022 05:35:42 +0200 Subject: [PATCH 4/8] Move factory test to after RP2040 init and RP2040 firmware update steps --- main/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/main.c b/main/main.c index 872d032..26d3841 100644 --- a/main/main.c +++ b/main/main.c @@ -113,9 +113,7 @@ void app_main(void) { } audio_init(); - - factory_test(pax_buffer, ili9341); - + display_boot_screen(pax_buffer, ili9341, "Starting..."); if (bsp_rp2040_init() != ESP_OK) { @@ -131,6 +129,8 @@ void app_main(void) { } rp2040_updater(rp2040, pax_buffer, ili9341); // Handle RP2040 firmware update & bootloader mode + + factory_test(pax_buffer, ili9341); /*uint8_t rp2040_uid[8]; if (rp2040_get_uid(rp2040, rp2040_uid) != ESP_OK) { From e2fc23eed3673c1f33ec06d8aad0d5ad96010ac4 Mon Sep 17 00:00:00 2001 From: Renze Nicolai Date: Fri, 3 Jun 2022 05:59:42 +0200 Subject: [PATCH 5/8] Add button test --- main/CMakeLists.txt | 1 + main/button_test.c | 106 +++++++++++++++++++++++++++++++++++++ main/include/button_test.h | 9 ++++ main/menus/dev.c | 7 ++- 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 main/button_test.c create mode 100644 main/include/button_test.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 2c6644e..02468fc 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -23,6 +23,7 @@ idf_component_register( "test_common.c" "factory_test.c" "animation.c" + "button_test.c" INCLUDE_DIRS "." "include" "menus" diff --git a/main/button_test.c b/main/button_test.c new file mode 100644 index 0000000..3f67524 --- /dev/null +++ b/main/button_test.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include "ili9341.h" +#include "pax_gfx.h" +#include "rp2040.h" + +void test_buttons(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { + bool render = true; + bool quit = false; + + bool btn_joy_down = false; + bool btn_joy_up = false; + bool btn_joy_left = false; + bool btn_joy_right = false; + bool btn_joy_press = false; + bool btn_home = false; + bool btn_menu = false; + bool btn_select = false; + bool btn_start = false; + bool btn_accept = false; + bool btn_back = false; + + while (!quit) { + rp2040_input_message_t buttonMessage = {0}; + if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) { + uint8_t pin = buttonMessage.input; + bool value = buttonMessage.state; + render = true; + switch(pin) { + case RP2040_INPUT_JOYSTICK_DOWN: + btn_joy_down = value; + break; + case RP2040_INPUT_JOYSTICK_UP: + btn_joy_up = value; + break; + case RP2040_INPUT_JOYSTICK_LEFT: + btn_joy_left = value; + break; + case RP2040_INPUT_JOYSTICK_RIGHT: + btn_joy_right = value; + break; + case RP2040_INPUT_JOYSTICK_PRESS: + btn_joy_press = value; + break; + case RP2040_INPUT_BUTTON_HOME: + btn_home = value; + break; + case RP2040_INPUT_BUTTON_MENU: + btn_menu = value; + break; + case RP2040_INPUT_BUTTON_SELECT: + btn_select = value; + break; + case RP2040_INPUT_BUTTON_START: + btn_start = value; + break; + case RP2040_INPUT_BUTTON_ACCEPT: + btn_accept = value; + break; + case RP2040_INPUT_BUTTON_BACK: + btn_back = value; + default: + break; + } + } + + if (render) { + pax_noclip(pax_buffer); + pax_background(pax_buffer, 0x325aa8); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*0, "Press HOME + START to exit"); + char buffer[64]; + snprintf(buffer, sizeof(buffer), "JOY DOWN %s", btn_joy_down ? "PRESSED" : "released"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*1, buffer); + snprintf(buffer, sizeof(buffer), "JOY UP %s", btn_joy_up ? "PRESSED" : "released"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*2, buffer); + snprintf(buffer, sizeof(buffer), "JOY LEFT %s", btn_joy_left ? "PRESSED" : "released"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*3, buffer); + snprintf(buffer, sizeof(buffer), "JOY RIGHT %s", btn_joy_right ? "PRESSED" : "released"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*4, buffer); + snprintf(buffer, sizeof(buffer), "JOY PRESS %s", btn_joy_press ? "PRESSED" : "released"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*5, buffer); + snprintf(buffer, sizeof(buffer), "BTN HOME %s", btn_home ? "PRESSED" : "released"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*6, buffer); + snprintf(buffer, sizeof(buffer), "BTN MENU %s", btn_menu ? "PRESSED" : "released"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*7, buffer); + snprintf(buffer, sizeof(buffer), "BTN SELECT %s", btn_select ? "PRESSED" : "released"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*8, buffer); + snprintf(buffer, sizeof(buffer), "BTN START %s", btn_start ? "PRESSED" : "released"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*9, buffer); + snprintf(buffer, sizeof(buffer), "BTN A %s", btn_accept ? "PRESSED" : "released"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*10, buffer); + snprintf(buffer, sizeof(buffer), "BTN B %s", btn_back ? "PRESSED" : "released"); + pax_draw_text(pax_buffer, 0xFFFFFFFF, NULL, 18, 0, 20*11, buffer); + ili9341_write(ili9341, pax_buffer->buf); + render = false; + } + + if (btn_home && btn_start) { + quit = true; + } + } +} + diff --git a/main/include/button_test.h b/main/include/button_test.h new file mode 100644 index 0000000..6b16506 --- /dev/null +++ b/main/include/button_test.h @@ -0,0 +1,9 @@ +#include +#include +#include +#include +#include +#include "ili9341.h" +#include "pax_gfx.h" + +void test_buttons(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341); diff --git a/main/menus/dev.c b/main/menus/dev.c index 016dc06..2db34b2 100644 --- a/main/menus/dev.c +++ b/main/menus/dev.c @@ -21,6 +21,7 @@ #include "file_browser.h" #include "fpga_test.h" #include "animation.h" +#include "button_test.h" extern const uint8_t dev_png_start[] asm("_binary_dev_png_start"); extern const uint8_t dev_png_end[] asm("_binary_dev_png_end"); @@ -32,7 +33,8 @@ typedef enum action { ACTION_FPGA_TEST, ACTION_FILE_BROWSER, ACTION_FILE_BROWSER_INT, - ACTION_ANIMATION + ACTION_ANIMATION, + ACTION_BUTTON_TEST } menu_dev_action_t; void render_dev_help(pax_buf_t* pax_buffer) { @@ -65,6 +67,7 @@ void menu_dev(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) menu_insert_item(menu, "File browser (SD card)", NULL, (void*) ACTION_FILE_BROWSER, -1); menu_insert_item(menu, "File browser (internal)", NULL, (void*) ACTION_FILE_BROWSER_INT, -1); menu_insert_item(menu, "Animation", NULL, (void*) ACTION_ANIMATION, -1); + menu_insert_item(menu, "Button test", NULL, (void*) ACTION_BUTTON_TEST, -1); bool render = true; menu_dev_action_t action = ACTION_NONE; @@ -125,6 +128,8 @@ void menu_dev(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) file_browser(buttonQueue, pax_buffer, ili9341, "/internal"); } else if (action == ACTION_ANIMATION) { display_animation(pax_buffer, ili9341); + } else if (action == ACTION_BUTTON_TEST) { + test_buttons(buttonQueue, pax_buffer, ili9341); } else if (action == ACTION_BACK) { break; } From f476ceb3a93c6eca9b4097987462443d5007efd6 Mon Sep 17 00:00:00 2001 From: Renze Nicolai Date: Fri, 3 Jun 2022 06:15:26 +0200 Subject: [PATCH 6/8] Put OTA update in separate task because of stack overflow --- main/wifi_ota.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/main/wifi_ota.c b/main/wifi_ota.c index e31383a..2403ba1 100644 --- a/main/wifi_ota.c +++ b/main/wifi_ota.c @@ -115,8 +115,10 @@ void display_ota_state(pax_buf_t* pax_buffer, ILI9341* ili9341, const char* text ili9341_write(ili9341, pax_buffer->buf); } +pax_buf_t* ota_pax_buffer; +ILI9341* ota_ili9341; -void ota_update(pax_buf_t* pax_buffer, ILI9341* ili9341) { +void ota_task(void *pvParameter) { esp_wifi_set_ps(WIFI_PS_NONE); // Disable any WiFi power save mode ESP_LOGI(TAG, "Starting OTA update"); @@ -142,13 +144,13 @@ void ota_update(pax_buf_t* pax_buffer, ILI9341* ili9341) { ESP_LOGI(TAG, "Attempting to download update from %s", config.url); - display_ota_state(pax_buffer, ili9341, "Starting download..."); + display_ota_state(ota_pax_buffer, ota_ili9341, "Starting download..."); esp_https_ota_handle_t https_ota_handle = NULL; esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle); if (err != ESP_OK) { ESP_LOGE(TAG, "ESP HTTPS OTA Begin failed"); - display_ota_state(pax_buffer, ili9341, "Failed to start download"); + display_ota_state(ota_pax_buffer, ota_ili9341, "Failed to start download"); vTaskDelay(5000 / portTICK_PERIOD_MS); esp_restart(); } @@ -158,7 +160,7 @@ void ota_update(pax_buf_t* pax_buffer, ILI9341* ili9341) { if (err != ESP_OK) { ESP_LOGE(TAG, "esp_https_ota_read_img_desc failed"); esp_https_ota_abort(https_ota_handle); - display_ota_state(pax_buffer, ili9341, "Failed to read image desc"); + display_ota_state(ota_pax_buffer, ota_ili9341, "Failed to read image desc"); vTaskDelay(5000 / portTICK_PERIOD_MS); esp_restart(); } @@ -166,7 +168,7 @@ void ota_update(pax_buf_t* pax_buffer, ILI9341* ili9341) { if (err != ESP_OK) { ESP_LOGE(TAG, "image header verification failed"); esp_https_ota_abort(https_ota_handle); - display_ota_state(pax_buffer, ili9341, "Image header verification failed"); + display_ota_state(ota_pax_buffer, ota_ili9341, "Image header verification failed"); vTaskDelay(5000 / portTICK_PERIOD_MS); esp_restart(); } @@ -188,29 +190,29 @@ void ota_update(pax_buf_t* pax_buffer, ILI9341* ili9341) { percent_shown = percent; char buffer[128]; snprintf(buffer, sizeof(buffer), "Updating... %d%%", percent); - display_ota_state(pax_buffer, ili9341, buffer); + display_ota_state(ota_pax_buffer, ota_ili9341, buffer); } } if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) { // the OTA image was not completely received and user can customise the response to this situation. ESP_LOGE(TAG, "Complete data was not received."); - display_ota_state(pax_buffer, ili9341, "Download failed"); + display_ota_state(ota_pax_buffer, ota_ili9341, "Download failed"); vTaskDelay(5000 / portTICK_PERIOD_MS); esp_restart(); } else { ota_finish_err = esp_https_ota_finish(https_ota_handle); if ((err == ESP_OK) && (ota_finish_err == ESP_OK)) { ESP_LOGI(TAG, "ESP_HTTPS_OTA upgrade successful. Rebooting ..."); - display_ota_state(pax_buffer, ili9341, "Update installed"); + display_ota_state(ota_pax_buffer, ota_ili9341, "Update installed"); vTaskDelay(1000 / portTICK_PERIOD_MS); esp_restart(); } else { if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) { ESP_LOGE(TAG, "Image validation failed, image is corrupted"); - display_ota_state(pax_buffer, ili9341, "Image validation failed"); + display_ota_state(ota_pax_buffer, ota_ili9341, "Image validation failed"); } else { - display_ota_state(pax_buffer, ili9341, "Update failed"); + display_ota_state(ota_pax_buffer, ota_ili9341, "Update failed"); } ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed 0x%x", ota_finish_err); vTaskDelay(5000 / portTICK_PERIOD_MS); @@ -220,6 +222,13 @@ void ota_update(pax_buf_t* pax_buffer, ILI9341* ili9341) { esp_https_ota_abort(https_ota_handle); esp_restart(); +} + +void ota_update(pax_buf_t* pax_buffer, ILI9341* ili9341) { + + ota_pax_buffer = pax_buffer; + ota_ili9341 = ili9341; + xTaskCreate(&ota_task, "OTA update", 8192, NULL, 5, NULL); while (1) { vTaskDelay(1000 / portTICK_PERIOD_MS); From a123f04ecf07cf4b8d3e84ebb6da88ca7720915b Mon Sep 17 00:00:00 2001 From: Renze Nicolai Date: Fri, 3 Jun 2022 07:00:06 +0200 Subject: [PATCH 7/8] Increase stack size for main task and undo changes to ota --- main/wifi_ota.c | 29 ++++++++++------------------- sdkconfig | 4 ++-- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/main/wifi_ota.c b/main/wifi_ota.c index 2403ba1..e31383a 100644 --- a/main/wifi_ota.c +++ b/main/wifi_ota.c @@ -115,10 +115,8 @@ void display_ota_state(pax_buf_t* pax_buffer, ILI9341* ili9341, const char* text ili9341_write(ili9341, pax_buffer->buf); } -pax_buf_t* ota_pax_buffer; -ILI9341* ota_ili9341; -void ota_task(void *pvParameter) { +void ota_update(pax_buf_t* pax_buffer, ILI9341* ili9341) { esp_wifi_set_ps(WIFI_PS_NONE); // Disable any WiFi power save mode ESP_LOGI(TAG, "Starting OTA update"); @@ -144,13 +142,13 @@ void ota_task(void *pvParameter) { ESP_LOGI(TAG, "Attempting to download update from %s", config.url); - display_ota_state(ota_pax_buffer, ota_ili9341, "Starting download..."); + display_ota_state(pax_buffer, ili9341, "Starting download..."); esp_https_ota_handle_t https_ota_handle = NULL; esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle); if (err != ESP_OK) { ESP_LOGE(TAG, "ESP HTTPS OTA Begin failed"); - display_ota_state(ota_pax_buffer, ota_ili9341, "Failed to start download"); + display_ota_state(pax_buffer, ili9341, "Failed to start download"); vTaskDelay(5000 / portTICK_PERIOD_MS); esp_restart(); } @@ -160,7 +158,7 @@ void ota_task(void *pvParameter) { if (err != ESP_OK) { ESP_LOGE(TAG, "esp_https_ota_read_img_desc failed"); esp_https_ota_abort(https_ota_handle); - display_ota_state(ota_pax_buffer, ota_ili9341, "Failed to read image desc"); + display_ota_state(pax_buffer, ili9341, "Failed to read image desc"); vTaskDelay(5000 / portTICK_PERIOD_MS); esp_restart(); } @@ -168,7 +166,7 @@ void ota_task(void *pvParameter) { if (err != ESP_OK) { ESP_LOGE(TAG, "image header verification failed"); esp_https_ota_abort(https_ota_handle); - display_ota_state(ota_pax_buffer, ota_ili9341, "Image header verification failed"); + display_ota_state(pax_buffer, ili9341, "Image header verification failed"); vTaskDelay(5000 / portTICK_PERIOD_MS); esp_restart(); } @@ -190,29 +188,29 @@ void ota_task(void *pvParameter) { percent_shown = percent; char buffer[128]; snprintf(buffer, sizeof(buffer), "Updating... %d%%", percent); - display_ota_state(ota_pax_buffer, ota_ili9341, buffer); + display_ota_state(pax_buffer, ili9341, buffer); } } if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) { // the OTA image was not completely received and user can customise the response to this situation. ESP_LOGE(TAG, "Complete data was not received."); - display_ota_state(ota_pax_buffer, ota_ili9341, "Download failed"); + display_ota_state(pax_buffer, ili9341, "Download failed"); vTaskDelay(5000 / portTICK_PERIOD_MS); esp_restart(); } else { ota_finish_err = esp_https_ota_finish(https_ota_handle); if ((err == ESP_OK) && (ota_finish_err == ESP_OK)) { ESP_LOGI(TAG, "ESP_HTTPS_OTA upgrade successful. Rebooting ..."); - display_ota_state(ota_pax_buffer, ota_ili9341, "Update installed"); + display_ota_state(pax_buffer, ili9341, "Update installed"); vTaskDelay(1000 / portTICK_PERIOD_MS); esp_restart(); } else { if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) { ESP_LOGE(TAG, "Image validation failed, image is corrupted"); - display_ota_state(ota_pax_buffer, ota_ili9341, "Image validation failed"); + display_ota_state(pax_buffer, ili9341, "Image validation failed"); } else { - display_ota_state(ota_pax_buffer, ota_ili9341, "Update failed"); + display_ota_state(pax_buffer, ili9341, "Update failed"); } ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed 0x%x", ota_finish_err); vTaskDelay(5000 / portTICK_PERIOD_MS); @@ -222,13 +220,6 @@ void ota_task(void *pvParameter) { esp_https_ota_abort(https_ota_handle); esp_restart(); -} - -void ota_update(pax_buf_t* pax_buffer, ILI9341* ili9341) { - - ota_pax_buffer = pax_buffer; - ota_ili9341 = ili9341; - xTaskCreate(&ota_task, "OTA update", 8192, NULL, 5, NULL); while (1) { vTaskDelay(1000 / portTICK_PERIOD_MS); diff --git a/sdkconfig b/sdkconfig index f8b80b7..753c246 100644 --- a/sdkconfig +++ b/sdkconfig @@ -675,7 +675,7 @@ CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 -CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y # CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set # CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set @@ -1475,7 +1475,7 @@ CONFIG_ESP32S2_PANIC_PRINT_HALT=y # CONFIG_ESP32S2_PANIC_GDBSTUB is not set CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 -CONFIG_MAIN_TASK_STACK_SIZE=3584 +CONFIG_MAIN_TASK_STACK_SIZE=8192 CONFIG_CONSOLE_UART_DEFAULT=y # CONFIG_CONSOLE_UART_CUSTOM is not set # CONFIG_ESP_CONSOLE_UART_NONE is not set From 9f36d027a2be7c60efc582c09913c26f82151f86 Mon Sep 17 00:00:00 2001 From: Renze Nicolai Date: Fri, 3 Jun 2022 07:17:22 +0200 Subject: [PATCH 8/8] Use PROJECT_VER variable for version, display version on main menu --- CMakeLists.txt | 4 ++++ main/main.c | 8 ++++++-- main/menus/start.c | 14 ++++++++++---- main/menus/start.h | 2 +- sdkconfig | 6 +++--- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a4b2cc..a18b31a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,8 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +set(PROJECT_NAME "MCH2022 launcher") +set(PROJECT_VER "0.1") + project(main) diff --git a/main/main.c b/main/main.c index 26d3841..3c01c49 100644 --- a/main/main.c +++ b/main/main.c @@ -15,7 +15,7 @@ #include "pax_gfx.h" #include "sdcard.h" #include "appfs.h" - +#include "esp_ota_ops.h" #include "rp2040.h" #include "rp2040bl.h" @@ -71,6 +71,10 @@ void display_fatal_error(pax_buf_t* pax_buffer, ILI9341* ili9341, const char* li void app_main(void) { esp_err_t res; + + const esp_app_desc_t *app_description = esp_ota_get_app_description(); + ESP_LOGI(TAG, "App version: %s", app_description->version); + //ESP_LOGI(TAG, "Project name: %s", app_description->project_name); /* Initialize memory */ uint8_t* framebuffer = heap_caps_malloc(ILI9341_BUFFER_SIZE, MALLOC_CAP_8BIT); @@ -236,7 +240,7 @@ void app_main(void) { /* Launcher menu */ while (true) { - menu_start(rp2040->queue, pax_buffer, ili9341); + menu_start(rp2040->queue, pax_buffer, ili9341, app_description->version); } free(framebuffer); diff --git a/main/menus/start.c b/main/menus/start.c index 5de0a34..4f1854b 100644 --- a/main/menus/start.c +++ b/main/menus/start.c @@ -41,14 +41,20 @@ typedef enum action { ACTION_SETTINGS } menu_start_action_t; -void render_start_help(pax_buf_t* pax_buffer) { +void render_start_help(pax_buf_t* pax_buffer, const char* version) { const pax_font_t *font = pax_get_font("saira regular"); pax_background(pax_buffer, 0xFFFFFF); pax_noclip(pax_buffer); pax_draw_text(pax_buffer, 0xFF491d88, font, 18, 5, 240 - 18, "[A] accept"); + + char version_text[64]; + snprintf(version_text, sizeof(version_text), "v%s", version); + + pax_vec1_t version_size = pax_text_size(font, 18, version_text); + pax_draw_text(pax_buffer, 0xFF491d88, font, 18, 320 - 5 - version_size.x, 240 - 18, version_text); } -void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) { +void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, const char* version) { menu_t* menu = menu_alloc("Main menu", 34, 18); menu->fgColor = 0xFF000000; @@ -83,7 +89,7 @@ void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili934 bool render = true; menu_start_action_t action = ACTION_NONE; - render_start_help(pax_buffer); + render_start_help(pax_buffer, version); while (1) { rp2040_input_message_t buttonMessage = {0}; @@ -135,7 +141,7 @@ void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili934 } action = ACTION_NONE; render = true; - render_start_help(pax_buffer); + render_start_help(pax_buffer, version); } } diff --git a/main/menus/start.h b/main/menus/start.h index f3de243..88c8ab7 100644 --- a/main/menus/start.h +++ b/main/menus/start.h @@ -5,4 +5,4 @@ #include "pax_gfx.h" #include "ili9341.h" -void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341); +void menu_start(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, const char* version); diff --git a/sdkconfig b/sdkconfig index 753c246..7461ec7 100644 --- a/sdkconfig +++ b/sdkconfig @@ -157,15 +157,15 @@ CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y CONFIG_APP_BUILD_GENERATE_BINARIES=y CONFIG_APP_BUILD_BOOTLOADER=y CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y -CONFIG_APP_REPRODUCIBLE_BUILD=y +# CONFIG_APP_REPRODUCIBLE_BUILD is not set # end of Build type # # Application manager # # CONFIG_APP_COMPILE_TIME_DATE is not set -CONFIG_APP_EXCLUDE_PROJECT_VER_VAR=y -CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR=y +# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set +# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set # CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set CONFIG_APP_RETRIEVE_LEN_ELF_SHA=16 # end of Application manager