/* SPDX-License-Identifier: GPL-3.0-only */ /* Compact monochrome derivative of images/logo.png for the 128x48 content panel. */ #include "local_boot_animation.h" #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "local_display.h" #define BOOT_ANIMATION_FRAMES 20U #define BOOT_ANIMATION_FRAME_MS 250U #define BOOT_ANIMATION_NAME_SPEED 16U static const char s_device_name[] = "ESP32 SERIAL SWISS ARMY KNIFE"; static void pixel(int x, int y) { if (x >= 0 && x < (int)LOCAL_DISPLAY_WIDTH && y >= 0 && y < (int)LOCAL_DISPLAY_CONTENT_HEIGHT) { local_display_frame_set_pixel(LOCAL_DISPLAY_PANEL_CONTENT, (uint8_t)x, (uint8_t)y, true); } } static void line(int x0, int y0, int x1, int y1) { int dx = x1 >= x0 ? x1 - x0 : x0 - x1; int sx = x0 < x1 ? 1 : -1; int dy = y1 >= y0 ? y0 - y1 : y1 - y0; int sy = y0 < y1 ? 1 : -1; int error = dx + dy; for (;;) { pixel(x0, y0); if (x0 == x1 && y0 == y1) { return; } int twice_error = 2 * error; if (twice_error >= dy) { error += dy; x0 += sx; } if (twice_error <= dx) { error += dx; y0 += sy; } } } static void rectangle(int x, int y, int width, int height) { line(x, y, x + width - 1, y); line(x, y, x, y + height - 1); line(x + width - 1, y, x + width - 1, y + height - 1); line(x, y + height - 1, x + width - 1, y + height - 1); } static void draw_serial_connector(void) { rectangle(2, 8, 16, 10); rectangle(4, 10, 12, 6); for (int column = 0; column < 5; ++column) { pixel(6 + column * 2, 12); pixel(6 + column * 2, 14); } line(18, 13, 30, 22); line(18, 16, 28, 25); } static void draw_usb_connector(void) { rectangle(2, 31, 14, 11); rectangle(4, 33, 10, 7); line(16, 35, 29, 30); line(16, 39, 30, 34); pixel(7, 35); pixel(10, 38); } static void draw_terminal(bool cursor_on) { rectangle(28, 21, 42, 22); rectangle(30, 23, 38, 18); /* Keep the prompt upright even though the source logo is reoriented. */ line(38, 28, 45, 33); line(45, 33, 38, 38); if (cursor_on) { line(51, 37, 59, 37); } line(31, 27, 31, 39); line(67, 27, 67, 39); line(34, 22, 34, 20); line(63, 22, 63, 20); } static void draw_board(void) { rectangle(72, 7, 28, 36); rectangle(78, 14, 16, 17); for (int row = 0; row < 5; ++row) { pixel(74, 11 + row * 6); pixel(76, 11 + row * 6); pixel(96, 11 + row * 6); pixel(98, 11 + row * 6); } for (int column = 0; column < 6; ++column) { pixel(79 + column * 3, 35); pixel(79 + column * 3, 38); } line(70, 28, 72, 28); line(70, 34, 72, 34); } static void draw_wifi(uint8_t frame) { bool outer = (frame % 2U) == 0U; if (outer) { line(103, 8, 109, 3); line(109, 3, 115, 8); } line(105, 12, 109, 8); line(109, 8, 113, 12); line(107, 16, 109, 14); line(109, 14, 111, 16); pixel(109, 19); } static void draw_logo(uint8_t frame) { draw_serial_connector(); draw_usb_connector(); draw_terminal((frame % 2U) == 0U); draw_board(); draw_wifi(frame); } static void draw_scrolling_name(uint8_t frame) { int text_width = ((int)sizeof(s_device_name) - 1) * 6; int x = (int)LOCAL_DISPLAY_WIDTH - (int)frame * BOOT_ANIMATION_NAME_SPEED; if (x < -text_width) { x += text_width + (int)LOCAL_DISPLAY_WIDTH; } const char *text = s_device_name; while (x < 0 && *text != '\0') { x += 6; ++text; } if (x < (int)LOCAL_DISPLAY_WIDTH && *text != '\0') { local_display_frame_draw_text(LOCAL_DISPLAY_PANEL_STATUS, (uint8_t)x, 4U, text); } } esp_err_t local_boot_animation_play(void) { for (uint8_t frame = 0U; frame < BOOT_ANIMATION_FRAMES; ++frame) { esp_err_t error = local_display_frame_begin(); if (error != ESP_OK) { return error; } local_display_frame_clear_all(); draw_scrolling_name(frame); draw_logo(frame); error = local_display_frame_end(); if (error != ESP_OK) { return error; } vTaskDelay(pdMS_TO_TICKS(BOOT_ANIMATION_FRAME_MS)); } return ESP_OK; }