From e77fcbc5d0a325aebdd15dfbb7d00763d96a5d1c Mon Sep 17 00:00:00 2001 From: lukas Date: Sat, 1 Aug 2026 10:13:29 +0200 Subject: [PATCH] Improved sprite rendering --- Cargo.lock | 9 +- Cargo.toml | 1 + src/bin/main.rs | 436 ++++++++++++++++++++++++++++++++++++------------ 3 files changed, 337 insertions(+), 109 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e99a9fc..cb1baf7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64-simd" version = "0.7.0" @@ -296,6 +302,7 @@ dependencies = [ name = "creaturedex" version = "0.1.0" dependencies = [ + "base64 0.22.1", "critical-section", "display-interface-spi", "embassy-executor", @@ -3137,7 +3144,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" dependencies = [ - "base64", + "base64 0.13.1", "proc-macro2", "quote", "syn 1.0.109", diff --git a/Cargo.toml b/Cargo.toml index 5b8e1f7..2ecb546 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ embedded-hal = "1.0.0" embedded-hal-bus = "0.3.0" embedded-graphics = "0.8.2" embedded-graphics-core = "0.4.1" +base64 = { version = "0.22", default-features = false, features = ["alloc"] } # For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html diff --git a/src/bin/main.rs b/src/bin/main.rs index a877e68..ef6be69 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -7,18 +7,25 @@ )] #![deny(clippy::large_stack_frames)] -use alloc::{format, string::{String, ToString}}; +use alloc::{ + format, + string::{String, ToString}, +}; +use base64::{Engine as _, engine::general_purpose}; +use display_interface_spi::SPIInterface; use embassy_executor::Spawner; use embassy_time::Timer; -use display_interface_spi::SPIInterface; -use embedded_graphics::Drawable; -use embedded_graphics::Pixel; use embedded_graphics::mono_font::MonoTextStyle; use embedded_graphics::mono_font::ascii::FONT_6X10; use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::Point; use embedded_graphics::prelude::RgbColor; use embedded_graphics::text::Text; +use embedded_graphics::{ + Drawable, + prelude::*, + primitives::{PrimitiveStyle, Rectangle}, +}; use embedded_graphics_core::draw_target::DrawTarget; use embedded_hal_bus::spi::ExclusiveDevice; use esp_hal::clock::CpuClock; @@ -48,8 +55,64 @@ extern crate alloc; // For more information see: esp_bootloader_esp_idf::esp_app_desc!(); - // The mock payload acting as our simulated NFC card read +const MOCK_PAYLOAD2: [u8; 858] = [ + 0xE9, 0x24, 0x03, 0xDA, 0x45, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0xAA, 0xA0, 0xF0, 0xAA, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0xAA, 0xA0, 0xF0, 0xAA, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xAA, 0xAA, 0x00, 0xAA, + 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0xAA, + 0xAA, 0x0A, 0xAA, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xAA, 0xAA, 0xAA, 0x0A, 0xAA, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF0, 0xAA, 0xAA, 0xA0, 0xAA, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xAA, 0xAA, 0xA0, 0xAA, 0x0A, 0x0F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xAA, 0xAA, 0xA0, 0x0A, 0x0A, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xAA, 0xAA, 0xAA, + 0x00, 0xAA, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x0A, 0xAA, 0xAA, 0x6A, 0xAA, 0xAA, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xAA, 0xFA, 0xAA, 0xA6, 0xAA, 0xAA, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xAF, 0xFF, 0xFA, 0xAA, 0xAA, 0xAA, 0x0F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xAA, 0xFA, 0xAA, 0xAA, 0xAA, + 0xFA, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0xAA, 0xAA, + 0xAA, 0xAA, 0xFF, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xDD, 0xDD, 0xDD, 0x0F, + 0x0A, 0xAA, 0xAA, 0xA6, 0xAA, 0xAA, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0D, 0xDF, + 0xFF, 0xFF, 0x00, 0xAA, 0xAA, 0xA0, 0xAA, 0xA0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x0D, 0x00, 0x0F, 0xF0, 0xAA, 0xAA, 0xAA, 0x0F, 0x0A, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0D, 0x0A, 0xAF, 0xF0, 0xAA, 0xAA, 0x0A, 0x0F, 0xFF, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0D, 0x00, 0x0F, 0xF0, 0xAA, 0xAA, 0xF0, 0xFF, 0xF0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0D, 0x0E, 0x0F, 0xF0, 0xAA, 0xAA, + 0xDD, 0xDD, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0D, 0x00, 0x0F, + 0xF0, 0xAF, 0xAF, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0D, 0x0A, 0xAF, 0xF0, 0xAA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0D, 0x00, 0x0F, 0xF0, 0xAF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0D, 0xDF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xDD, 0xDD, 0xDF, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xB3, 0x11, 0xCF, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x3B, 0x9A, + 0x26, 0x10, 0x98, 0x23, 0xFC, 0x01, 0x00, 0x00, 0x00, 0xCE, 0xF7, 0xB2, 0xA9, 0x9B, 0xCA, 0x23, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0xD7, 0x86, 0xBA, 0xC0, 0x9C, 0xBE, 0x40, 0x98, 0xA2, 0x1F, 0x00, + 0x00, 0x54, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6B, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x6C, + 0x69, 0x65, 0x20, 0xF0, 0x9F, 0x8E, 0x82, 0x00, 0x00, 0x87, 0xF7, 0xB1, 0x44, 0xB0, 0x55, 0x5E, + 0xF9, 0x22, 0xD6, 0x71, 0x45, 0xE9, 0xD5, 0xA7, 0xF8, 0x0F, 0xBE, 0x8F, 0xF6, 0xB9, 0x77, 0x62, + 0x13, 0xF3, 0x84, 0x55, 0xD2, 0xBC, 0x4E, 0xCB, 0xDF, 0xDB, 0xC0, 0x91, 0x5F, 0x39, 0xE0, 0xF5, + 0x95, 0x2D, 0x14, 0xBD, 0x43, 0xB2, 0x40, 0x76, 0x78, 0x00, +]; + const MOCK_PAYLOAD: [u8; 858] = [ 0xE9, 0x24, 0x01, 0x47, 0xF2, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -104,7 +167,7 @@ const MOCK_PAYLOAD: [u8; 858] = [ 0x65, 0x73, 0x20, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x23, 0xC0, 0xD6, 0x5A, 0x9D, 0xA2, 0xAB, 0xC4, 0x80, 0xC3, 0x06, 0xDB, 0xE8, 0x88, 0x7A, 0x7E, 0xB2, 0x40, 0x10, 0xA2, 0xDF, 0xC0, 0x89, 0x03, 0x63, 0x8F, 0x4C, 0x2F, 0xBA, 0xCC, 0x17, 0x23, 0x51, 0x60, 0x27, 0x3B, 0x00, 0x1C, 0x6D, - 0x1F, 0x8C, 0x6B, 0x3E, 0x32, 0xE2, 0x53, 0xD9, 0x8F, 0x00 + 0x1F, 0x8C, 0x6B, 0x3E, 0x32, 0xE2, 0x53, 0xD9, 0x8F, 0x00, ]; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -120,71 +183,199 @@ struct TypeStyle { const PALETTES: [[Rgb; 16]; 8] = [ // Palette Default [ - Rgb(31, 23, 35), Rgb(60, 47, 82), Rgb(178, 39, 65), Rgb(70, 86, 165), - Rgb(245, 70, 76), Rgb(216, 104, 48), Rgb(140, 127, 144), Rgb(73, 149, 243), - Rgb(62, 197, 75), Rgb(240, 149, 72), Rgb(188, 176, 179), Rgb(247, 156, 136), - Rgb(114, 222, 235), Rgb(180, 230, 86), Rgb(239, 208, 129), Rgb(255, 255, 255), + Rgb(31, 23, 35), + Rgb(60, 47, 82), + Rgb(178, 39, 65), + Rgb(70, 86, 165), + Rgb(245, 70, 76), + Rgb(216, 104, 48), + Rgb(140, 127, 144), + Rgb(73, 149, 243), + Rgb(62, 197, 75), + Rgb(240, 149, 72), + Rgb(188, 176, 179), + Rgb(247, 156, 136), + Rgb(114, 222, 235), + Rgb(180, 230, 86), + Rgb(239, 208, 129), + Rgb(255, 255, 255), ], // Palette Blueprint [ - Rgb(83, 67, 127), Rgb(44, 111, 153), Rgb(142, 88, 111), Rgb(0, 188, 170), - Rgb(153, 104, 226), Rgb(109, 129, 255), Rgb(255, 84, 112), Rgb(196, 143, 158), - Rgb(168, 159, 204), Rgb(190, 155, 255), Rgb(255, 155, 113), Rgb(127, 206, 255), - Rgb(255, 155, 182), Rgb(255, 217, 174), Rgb(255, 217, 232), Rgb(255, 255, 255), + Rgb(83, 67, 127), + Rgb(44, 111, 153), + Rgb(142, 88, 111), + Rgb(0, 188, 170), + Rgb(153, 104, 226), + Rgb(109, 129, 255), + Rgb(255, 84, 112), + Rgb(196, 143, 158), + Rgb(168, 159, 204), + Rgb(190, 155, 255), + Rgb(255, 155, 113), + Rgb(127, 206, 255), + Rgb(255, 155, 182), + Rgb(255, 217, 174), + Rgb(255, 217, 232), + Rgb(255, 255, 255), ], // Palette CYBER [ - Rgb(0, 0, 0), Rgb(0, 0, 126), Rgb(0, 0, 255), Rgb(126, 0, 0), - Rgb(126, 0, 126), Rgb(4, 126, 0), Rgb(254, 0, 0), Rgb(4, 126, 126), - Rgb(254, 0, 255), Rgb(126, 126, 0), Rgb(126, 126, 126), Rgb(6, 255, 4), - Rgb(6, 255, 255), Rgb(190, 190, 190), Rgb(255, 255, 4), Rgb(255, 255, 255), + Rgb(0, 0, 0), + Rgb(0, 0, 126), + Rgb(0, 0, 255), + Rgb(126, 0, 0), + Rgb(126, 0, 126), + Rgb(4, 126, 0), + Rgb(254, 0, 0), + Rgb(4, 126, 126), + Rgb(254, 0, 255), + Rgb(126, 126, 0), + Rgb(126, 126, 126), + Rgb(6, 255, 4), + Rgb(6, 255, 255), + Rgb(190, 190, 190), + Rgb(255, 255, 4), + Rgb(255, 255, 255), ], // Palette Nature [ - Rgb(35, 34, 40), Rgb(40, 66, 97), Rgb(30, 116, 83), Rgb(117, 77, 69), - Rgb(95, 88, 84), Rgb(36, 133, 166), Rgb(198, 80, 70), Rgb(85, 160, 88), - Rgb(135, 133, 115), Rgb(84, 186, 210), Rgb(161, 191, 65), Rgb(230, 146, 138), - Rgb(184, 176, 149), Rgb(227, 192, 84), Rgb(195, 213, 199), Rgb(255, 255, 255), + Rgb(35, 34, 40), + Rgb(40, 66, 97), + Rgb(30, 116, 83), + Rgb(117, 77, 69), + Rgb(95, 88, 84), + Rgb(36, 133, 166), + Rgb(198, 80, 70), + Rgb(85, 160, 88), + Rgb(135, 133, 115), + Rgb(84, 186, 210), + Rgb(161, 191, 65), + Rgb(230, 146, 138), + Rgb(184, 176, 149), + Rgb(227, 192, 84), + Rgb(195, 213, 199), + Rgb(255, 255, 255), ], // Palette Fossil [ - Rgb(75, 61, 68), Rgb(119, 66, 81), Rgb(78, 84, 99), Rgb(100, 83, 85), - Rgb(156, 82, 78), Rgb(132, 109, 89), Rgb(91, 125, 115), Rgb(125, 123, 98), - Rgb(140, 124, 121), Rgb(185, 122, 96), Rgb(168, 138, 94), Rgb(142, 159, 125), - Rgb(170, 162, 93), Rgb(169, 156, 141), Rgb(204, 168, 123), Rgb(255, 255, 255), + Rgb(75, 61, 68), + Rgb(119, 66, 81), + Rgb(78, 84, 99), + Rgb(100, 83, 85), + Rgb(156, 82, 78), + Rgb(132, 109, 89), + Rgb(91, 125, 115), + Rgb(125, 123, 98), + Rgb(140, 124, 121), + Rgb(185, 122, 96), + Rgb(168, 138, 94), + Rgb(142, 159, 125), + Rgb(170, 162, 93), + Rgb(169, 156, 141), + Rgb(204, 168, 123), + Rgb(255, 255, 255), ], // Palette Ghost [ - Rgb(0, 0, 0), Rgb(24, 24, 24), Rgb(40, 40, 40), Rgb(56, 56, 56), - Rgb(71, 71, 71), Rgb(86, 86, 86), Rgb(100, 100, 100), Rgb(113, 113, 113), - Rgb(126, 126, 126), Rgb(140, 140, 140), Rgb(155, 155, 155), Rgb(171, 171, 171), - Rgb(189, 189, 189), Rgb(209, 209, 209), Rgb(231, 231, 231), Rgb(255, 255, 255), + Rgb(0, 0, 0), + Rgb(24, 24, 24), + Rgb(40, 40, 40), + Rgb(56, 56, 56), + Rgb(71, 71, 71), + Rgb(86, 86, 86), + Rgb(100, 100, 100), + Rgb(113, 113, 113), + Rgb(126, 126, 126), + Rgb(140, 140, 140), + Rgb(155, 155, 155), + Rgb(171, 171, 171), + Rgb(189, 189, 189), + Rgb(209, 209, 209), + Rgb(231, 231, 231), + Rgb(255, 255, 255), ], // Palette Daemon [ - Rgb(0, 3, 60), Rgb(96, 0, 136), Rgb(0, 56, 132), Rgb(42, 46, 121), - Rgb(0, 82, 96), Rgb(177, 5, 133), Rgb(255, 0, 78), Rgb(172, 41, 206), - Rgb(0, 157, 74), Rgb(0, 138, 197), Rgb(78, 110, 168), Rgb(255, 92, 255), - Rgb(10, 255, 82), Rgb(0, 247, 255), Rgb(173, 212, 250), Rgb(255, 255, 255), + Rgb(0, 3, 60), + Rgb(96, 0, 136), + Rgb(0, 56, 132), + Rgb(42, 46, 121), + Rgb(0, 82, 96), + Rgb(177, 5, 133), + Rgb(255, 0, 78), + Rgb(172, 41, 206), + Rgb(0, 157, 74), + Rgb(0, 138, 197), + Rgb(78, 110, 168), + Rgb(255, 92, 255), + Rgb(10, 255, 82), + Rgb(0, 247, 255), + Rgb(173, 212, 250), + Rgb(255, 255, 255), ], // Palette Fairy [ - Rgb(40, 40, 46), Rgb(108, 86, 113), Rgb(135, 168, 137), Rgb(249, 130, 132), - Rgb(176, 169, 228), Rgb(222, 163, 139), Rgb(172, 204, 228), Rgb(254, 170, 228), - Rgb(217, 200, 191), Rgb(255, 195, 132), Rgb(176, 235, 147), Rgb(179, 227, 218), - Rgb(233, 245, 157), Rgb(255, 230, 198), Rgb(255, 247, 160), Rgb(255, 255, 255), + Rgb(40, 40, 46), + Rgb(108, 86, 113), + Rgb(135, 168, 137), + Rgb(249, 130, 132), + Rgb(176, 169, 228), + Rgb(222, 163, 139), + Rgb(172, 204, 228), + Rgb(254, 170, 228), + Rgb(217, 200, 191), + Rgb(255, 195, 132), + Rgb(176, 235, 147), + Rgb(179, 227, 218), + Rgb(233, 245, 157), + Rgb(255, 230, 198), + Rgb(255, 247, 160), + Rgb(255, 255, 255), ], ]; const TYPE_STYLES: [TypeStyle; 8] = [ - TypeStyle { name: "Default", primary: Rgb(0xD8, 0x68, 0x30), text: Rgb(0xFF, 0xFF, 0xFF) }, - TypeStyle { name: "Blueprint", primary: Rgb(0x6D, 0x81, 0xFF), text: Rgb(0xFF, 0xFF, 0xFF) }, - TypeStyle { name: "CYBER", primary: Rgb(0xFF, 0xFF, 0x04), text: Rgb(0x00, 0x00, 0x00) }, - TypeStyle { name: "Nature", primary: Rgb(0x55, 0xA0, 0x58), text: Rgb(0xFF, 0xFF, 0xFF) }, - TypeStyle { name: "Fossil", primary: Rgb(0xB9, 0x7A, 0x60), text: Rgb(0xFF, 0xFF, 0xFF) }, - TypeStyle { name: "Ghost", primary: Rgb(0xAB, 0xAB, 0xAB), text: Rgb(0xFF, 0xFF, 0xFF) }, - TypeStyle { name: "Daemon", primary: Rgb(0x60, 0x00, 0x88), text: Rgb(0xFF, 0xFF, 0xFF) }, - TypeStyle { name: "Fairy", primary: Rgb(0xB0, 0xA9, 0xE4), text: Rgb(0xFF, 0xFF, 0xFF) }, + TypeStyle { + name: "Default", + primary: Rgb(0xD8, 0x68, 0x30), + text: Rgb(0xFF, 0xFF, 0xFF), + }, + TypeStyle { + name: "Blueprint", + primary: Rgb(0x6D, 0x81, 0xFF), + text: Rgb(0xFF, 0xFF, 0xFF), + }, + TypeStyle { + name: "CYBER", + primary: Rgb(0xFF, 0xFF, 0x04), + text: Rgb(0x00, 0x00, 0x00), + }, + TypeStyle { + name: "Nature", + primary: Rgb(0x55, 0xA0, 0x58), + text: Rgb(0xFF, 0xFF, 0xFF), + }, + TypeStyle { + name: "Fossil", + primary: Rgb(0xB9, 0x7A, 0x60), + text: Rgb(0xFF, 0xFF, 0xFF), + }, + TypeStyle { + name: "Ghost", + primary: Rgb(0xAB, 0xAB, 0xAB), + text: Rgb(0xFF, 0xFF, 0xFF), + }, + TypeStyle { + name: "Daemon", + primary: Rgb(0x60, 0x00, 0x88), + text: Rgb(0xFF, 0xFF, 0xFF), + }, + TypeStyle { + name: "Fairy", + primary: Rgb(0xB0, 0xA9, 0xE4), + text: Rgb(0xFF, 0xFF, 0xFF), + }, ]; #[derive(Debug, Clone, Copy)] @@ -216,11 +407,8 @@ fn split_nfc_hex(payload: &[u8]) -> Option> { }) } -fn render_sprite_onto_ili9341( - ili9341: &mut D, - sprite: &[u8], - palette: &[Rgb; 16], -) where +fn render_sprite_onto_ili9341(ili9341: &mut D, sprite: &[u8], palette: &[Rgb; 16]) +where D: DrawTarget, { let pixel_count = sprite.len() * 2; @@ -228,6 +416,12 @@ fn render_sprite_onto_ili9341( return; } + // OPTIMIZATION 1: Pre-compute the 16-color palette to Rgb565 once. + let mut palette565 = [Rgb565::new(0, 0, 0); 16]; + for (i, Rgb(r, g, b)) in palette.iter().enumerate() { + palette565[i] = Rgb565::new(*r >> 3, *g >> 2, *b >> 3); + } + let mut src_width = 1usize; while src_width * src_width < pixel_count { src_width += 1; @@ -243,29 +437,36 @@ fn render_sprite_onto_ili9341( break; } - let byte_index = pixel_index / 2; + // Bitwise operations are slightly faster than division/modulo + let byte_index = pixel_index >> 1; let pixel_byte = sprite[byte_index]; - let color_index = if pixel_index % 2 == 0 { + + let color_index = if pixel_index & 1 == 0 { (pixel_byte >> 4) & 0x0F } else { pixel_byte & 0x0F }; - let Rgb(r, g, b) = palette[color_index as usize]; - let color = Rgb565::new(r >> 3, g >> 2, b >> 3); - let start_x = x * scale; - let start_y = y * scale; - let end_x = core::cmp::min(start_x + scale, target_width); - let end_y = core::cmp::min(start_y + scale, target_width); + let color = palette565[color_index as usize]; - for draw_y in start_y..end_y { - for draw_x in start_x..end_x { - let _ = ili9341.draw_iter(core::iter::once(Pixel( - Point::new(draw_x as i32, draw_y as i32), - color, - ))); - } + let start_x = (x * scale) as i32; + let start_y = (y * scale) as i32; + let end_x = core::cmp::min(start_x + scale as i32, target_width as i32); + let end_y = core::cmp::min(start_y + scale as i32, target_width as i32); + + let width = (end_x - start_x) as u32; + let height = (end_y - start_y) as u32; + + if width == 0 || height == 0 { + continue; } + + // OPTIMIZATION 2: Draw a rectangle block. + // The display driver sets the SPI window ONCE per block. + let rect = Rectangle::new(Point::new(start_x, start_y), Size::new(width, height)); + let style = PrimitiveStyle::with_fill(color); + + let _ = rect.into_styled(style).draw(ili9341); } } } @@ -287,7 +488,6 @@ fn decode_secret(hex: &[u8]) -> alloc::string::String { break; } - // Keep the output readable on the display by excluding control characters. if byte.is_ascii_graphic() || byte == b' ' { ascii_string.push(byte as char); } @@ -298,19 +498,19 @@ fn decode_secret(hex: &[u8]) -> alloc::string::String { fn decode_packed_card_text(bytes: &[u8]) -> (String, String, String, String) { let alphabet = b" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-"; let mut full_text = String::with_capacity((bytes.len() * 8) / 6 + 1); - + let mut accumulator: u32 = 0; let mut bits_in_acc: u32 = 0; - + for &byte in bytes { accumulator |= (byte as u32) << bits_in_acc; bits_in_acc += 8; while bits_in_acc >= 6 { let char_index = (accumulator & 0x3F) as usize; - + full_text.push(alphabet[char_index] as char); - + accumulator >>= 6; bits_in_acc -= 6; } @@ -324,11 +524,10 @@ fn decode_packed_card_text(bytes: &[u8]) -> (String, String, String, String) { (name, trait1, trait2, trait3) } - #[cfg(not(feature = "wokwi"))] async fn setup_wifi( - wifi: esp_hal::peripherals::WIFI<'static>, - flash: esp_hal::peripherals::FLASH<'static>, + wifi: esp_hal::peripherals::WIFI<'static>, + flash: esp_hal::peripherals::FLASH<'static>, spawner: &Spawner, ) { let nvs = esp_hal_wifimanager::Nvs::new(0x9000, 0x6000, flash).unwrap(); @@ -344,22 +543,15 @@ async fn setup_wifi( wm_settings.wifi_conn_timeout = 30000; wm_settings.esp_reset_timeout = Some(300000); // 5min - let wifi_res = esp_hal_wifimanager::init_wm( - wm_settings, - spawner, - Some(&nvs), - wifi, - None, - ) - .await; + let wifi_res = esp_hal_wifimanager::init_wm(wm_settings, spawner, Some(&nvs), wifi, None).await; log::info!("wifi_res: {wifi_res:?}"); } #[cfg(feature = "wokwi")] async fn setup_wifi( - wifi: esp_hal::peripherals::WIFI<'static>, - flash: esp_hal::peripherals::FLASH<'static>, + wifi: esp_hal::peripherals::WIFI<'static>, + flash: esp_hal::peripherals::FLASH<'static>, spawner: &Spawner, ) { log::info!("Wokwi mode active: pre-seeding WiFi Manager with Wokwi credentials"); @@ -383,24 +575,17 @@ async fn setup_wifi( log::error!("Failed to store Wokwi WiFi config in NVS: {e:?}"); return; } - + let mut wm_settings = esp_hal_wifimanager::WmSettings::default(); - + // Wokwi simulation uses a default open network called "Wokwi-GUEST" wm_settings.ssid.clear(); _ = core::fmt::write(&mut wm_settings.ssid, format_args!("Wokwi-GUEST")); - + wm_settings.wifi_conn_timeout = 30000; - + // Initialize Wi-Fi connection through WiFi Manager using the pre-seeded NVS data. - let wifi_res = esp_hal_wifimanager::init_wm( - wm_settings, - spawner, - Some(&nvs), - wifi, - None, - ) - .await; + let wifi_res = esp_hal_wifimanager::init_wm(wm_settings, spawner, Some(&nvs), wifi, None).await; log::info!("Wokwi wifi_res: {wifi_res:?}"); } @@ -413,7 +598,7 @@ async fn setup_wifi( async fn main(spawner: Spawner) { // generator version: 1.3.0 // generator parameters: --chip esp32s3 -o esp32s3-wroom-1-octal-psram -o unstable-hal -o alloc -o wifi -o log -o wokwi -o neovim -o vscode -o esp - + esp_println::logger::init_logger_from_env(); let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); @@ -444,12 +629,11 @@ async fn main(spawner: Spawner) { let sw_interrupt = esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); - let spi_bus = Spi::new( peripherals.SPI2, Config::default() - .with_frequency(Rate::from_khz(100)) + .with_frequency(Rate::from_mhz(40)) .with_mode(Mode::_0), ) .unwrap() @@ -477,6 +661,17 @@ async fn main(spawner: Spawner) { display.clear(Rgb565::BLACK).unwrap(); let nfc_payload: Option> = split_nfc_hex(&MOCK_PAYLOAD); + let simulated_time: u64 = 1780757711176; // Simulated time in milliseconds since epoch (2023-06-05 12:15:11 UTC) + let mut final_payload = [0u8; 867]; + + final_payload[..858].copy_from_slice(&MOCK_PAYLOAD); + final_payload[858..866].copy_from_slice(&simulated_time.to_be_bytes()); + final_payload[866] = 0x00; + + let b64_string = general_purpose::STANDARD.encode(&final_payload); + log::info!("Base64 String:"); + log::info!("{}", b64_string); + if let Some(nfc_payload) = nfc_payload { let (name, trait1, trait2, trait3) = decode_packed_card_text(nfc_payload.packed_card_text); let secret = decode_secret(nfc_payload.secret); @@ -485,15 +680,40 @@ async fn main(spawner: Spawner) { Text::new(&format!("Name: {}", name), Point::new(20, 240), style) .draw(&mut display) .unwrap(); - Text::new(&format!("Type: {}", TYPE_STYLES[(nfc_payload.card_type - 1) as usize].name), Point::new(20, 250), style) - .draw(&mut display) - .unwrap(); - Text::new(&format!("Event: {}", decode_known_event(nfc_payload.event_encoding).unwrap_or("Unknown")), Point::new(20, 260), style) - .draw(&mut display) - .unwrap(); - Text::new(&format!("UUID: {}", nfc_payload.card_uuid.iter().map(|b| format!("{:02X}", b)).collect::()), Point::new(20, 270), style) - .draw(&mut display) - .unwrap(); + Text::new( + &format!( + "Type: {}", + TYPE_STYLES[(nfc_payload.card_type - 1) as usize].name + ), + Point::new(20, 250), + style, + ) + .draw(&mut display) + .unwrap(); + Text::new( + &format!( + "Event: {}", + decode_known_event(nfc_payload.event_encoding).unwrap_or("Unknown") + ), + Point::new(20, 260), + style, + ) + .draw(&mut display) + .unwrap(); + Text::new( + &format!( + "UUID: {}", + nfc_payload + .card_uuid + .iter() + .map(|b| format!("{:02X}", b)) + .collect::() + ), + Point::new(20, 270), + style, + ) + .draw(&mut display) + .unwrap(); Text::new(&format!("Trait1: {}", trait1), Point::new(20, 280), style) .draw(&mut display) .unwrap();