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; +}