Add bounded OLED boot identity animation
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
/* 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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user