Rust decode nfc payload #18

Merged
rhetenor merged 13 commits from rust-decode-nfc-payload into main 2026-08-02 15:45:05 +02:00
3 changed files with 143 additions and 107 deletions
Showing only changes of commit 10dc9b1b83 - Show all commits
+3
View File
@@ -24,3 +24,6 @@ target/
# Ignore .DS_Store file in mac # Ignore .DS_Store file in mac
**/.DS_Store **/.DS_Store
# Ignore checked out wiki
creaturedex.wiki/
+14 -22
View File
@@ -3,30 +3,23 @@
"author": "CreatureDex", "author": "CreatureDex",
"editor": "wokwi", "editor": "wokwi",
"parts": [ "parts": [
{ "type": "board-esp32-s3-devkitc-1", "id": "esp1", "top": 67.2, "left": -263.96, "attrs": {} },
{ {
"type": "wokwi-ili9341", "type": "board-esp32-s3-devkitc-1",
"id": "lcd1", "id": "esp1",
"top": -191.24, "top": 67.2,
"left": -0.38, "left": -263.96,
"attrs": {} "attrs": {}
}, },
{ "type": "wokwi-ili9341", "id": "lcd1", "top": -191.24, "left": -0.38, "attrs": {} },
{ {
"type": "wokwi-led", "type": "wokwi-pushbutton",
"id": "led1", "id": "btn1",
"top": -82.89, "top": 131,
"left": 196.26, "left": -124.8,
"attrs": { "color": "red" } "attrs": { "color": "green", "xray": "1", "key": "1" }
},
{
"type": "wokwi-resistor",
"id": "r1",
"top": 183.86,
"left": 154.06,
"attrs": { "value": "220" }
} }
], ],
"connections": [ "connections": [
[ "esp1:TX", "$serialMonitor:RX", "", [] ], [ "esp1:TX", "$serialMonitor:RX", "", [] ],
[ "esp1:RX", "$serialMonitor:TX", "", [] ], [ "esp1:RX", "$serialMonitor:TX", "", [] ],
[ "lcd1:VCC", "esp1:3V3", "red", [ "v19.2", "h-57.6", "v-105.6", "h-124.95" ] ], [ "lcd1:VCC", "esp1:3V3", "red", [ "v19.2", "h-57.6", "v-105.6", "h-124.95" ] ],
@@ -39,9 +32,8 @@
[ "esp1:37", "lcd1:MISO", "magenta", [ "h57.6", "v105.6", "h172.8" ] ], [ "esp1:37", "lcd1:MISO", "magenta", [ "h57.6", "v105.6", "h172.8" ] ],
[ "esp1:22", "lcd1:SCL", "yellow", [ "h48", "v163.2", "h192" ] ], [ "esp1:22", "lcd1:SCL", "yellow", [ "h48", "v163.2", "h192" ] ],
[ "esp1:21", "lcd1:SDA", "cyan", [ "h38.4", "v144", "h211.2" ] ], [ "esp1:21", "lcd1:SDA", "cyan", [ "h38.4", "v144", "h211.2" ] ],
[ "r1:2", "led1:A", "green", [ "h8.4", "v-230.4" ] ], [ "btn1:1.l", "esp1:GND.2", "green", [ "h-9.6", "v-48" ] ],
[ "esp1:GND.2", "led1:C", "black", [ "v0", "h86.4", "v76.8", "h278.4" ] ], [ "btn1:2.l", "esp1:38", "green", [ "h-9.6", "v19.4" ] ]
[ "esp1:17", "r1:1", "green", [ "h0" ] ] ],
],
"dependencies": {} "dependencies": {}
} }
+123 -82
View File
@@ -27,8 +27,8 @@ use embedded_hal_bus::spi::ExclusiveDevice;
use esp_hal::clock::CpuClock; use esp_hal::clock::CpuClock;
use esp_hal::delay::Delay; use esp_hal::delay::Delay;
use esp_hal::gpio::Level; use esp_hal::gpio::Level;
use esp_hal::gpio::Output; use esp_hal::gpio::{Input, Output, Pull};
use esp_hal::gpio::OutputConfig; use esp_hal::gpio::{InputConfig, OutputConfig};
use esp_hal::spi::{ use esp_hal::spi::{
Mode, Mode,
master::{Config, Spi}, master::{Config, Spi},
@@ -169,6 +169,12 @@ const MOCK_PAYLOAD: [u8; 858] = [
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Rgb(pub u8, pub u8, pub u8); struct Rgb(pub u8, pub u8, pub u8);
impl Rgb {
fn into_rgb565(self) -> Rgb565 {
Rgb565::new(self.0, self.1, self.2)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct TypeStyle { struct TypeStyle {
pub name: &'static str, pub name: &'static str,
@@ -515,6 +521,101 @@ fn decode_packed_card_text(bytes: &[u8]) -> (String, String, String, String) {
(name, trait1, trait2, trait3) (name, trait1, trait2, trait3)
} }
fn render_nfc_payload<D>(display: &mut D, payload: &[u8; 858])
where
D: DrawTarget<Color = Rgb565>,
D::Error: core::fmt::Debug,
{
let nfc_payload: Option<NfcPayload<'_>> = split_nfc_hex(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(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);
let background = Rectangle::new(
Point::new(0, 0),
Size::new(240, 320),
);
let bg_color: Rgb565 = TYPE_STYLES[(nfc_payload.card_type - 1) as usize]
.primary
.into_rgb565();
let _ = display.fill_solid(&background, bg_color);
let style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
Text::new(&format!("Name: {}", name), Point::new(20, 240), style)
.draw(display)
.unwrap();
Text::new(
&format!(
"Type: {}",
TYPE_STYLES[(nfc_payload.card_type - 1) as usize].name
),
Point::new(20, 250),
style,
)
.draw(display)
.unwrap();
Text::new(
&format!(
"Event: {}",
decode_known_event(nfc_payload.event_encoding).unwrap_or("Unknown")
),
Point::new(20, 260),
style,
)
.draw(display)
.unwrap();
Text::new(
&format!(
"UUID: {}",
nfc_payload
.card_uuid
.iter()
.map(|b| format!("{:02X}", b))
.collect::<String>()
),
Point::new(20, 270),
style,
)
.draw(display)
.unwrap();
Text::new(&format!("Trait1: {}", trait1), Point::new(20, 280), style)
.draw(display)
.unwrap();
Text::new(&format!("Trait2: {}", trait2), Point::new(20, 290), style)
.draw(display)
.unwrap();
Text::new(&format!("Trait3: {}", trait3), Point::new(20, 300), style)
.draw(display)
.unwrap();
Text::new(&format!("Secret: {}", secret), Point::new(20, 310), style)
.draw(display)
.unwrap();
render_sprite_onto_ili9341(
display,
nfc_payload.sprite,
&PALETTES[(nfc_payload.card_type - 1) as usize],
);
} else {
log::error!(
"NFC payload too short to decode packed text: {} bytes",
payload.len()
);
}
}
#[cfg(not(feature = "wokwi"))] #[cfg(not(feature = "wokwi"))]
async fn setup_wifi( async fn setup_wifi(
wifi: esp_hal::peripherals::WIFI<'static>, wifi: esp_hal::peripherals::WIFI<'static>,
@@ -616,6 +717,9 @@ async fn main(spawner: Spawner) {
esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 73744); esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 73744);
let button_config = InputConfig::default().with_pull(Pull::Up);
let button1 = Input::new(peripherals.GPIO38, button_config);
let timg0 = TimerGroup::new(peripherals.TIMG0); let timg0 = TimerGroup::new(peripherals.TIMG0);
let sw_interrupt = let sw_interrupt =
esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
@@ -652,92 +756,29 @@ async fn main(spawner: Spawner) {
display.clear(Rgb565::BLACK).unwrap(); display.clear(Rgb565::BLACK).unwrap();
let nfc_payload: Option<NfcPayload<'_>> = 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); //setup_wifi(peripherals.WIFI, peripherals.FLASH, &spawner).await;
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);
let style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
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::<String>()
),
Point::new(20, 270),
style,
)
.draw(&mut display)
.unwrap();
Text::new(&format!("Trait1: {}", trait1), Point::new(20, 280), style)
.draw(&mut display)
.unwrap();
Text::new(&format!("Trait2: {}", trait2), Point::new(20, 290), style)
.draw(&mut display)
.unwrap();
Text::new(&format!("Trait3: {}", trait3), Point::new(20, 300), style)
.draw(&mut display)
.unwrap();
Text::new(&format!("Secret: {}", secret), Point::new(20, 310), style)
.draw(&mut display)
.unwrap();
render_sprite_onto_ili9341(
&mut display,
nfc_payload.sprite,
&PALETTES[(nfc_payload.card_type - 1) as usize],
);
} else {
log::error!(
"NFC payload too short to decode packed text: {} bytes",
MOCK_PAYLOAD.len()
);
}
setup_wifi(peripherals.WIFI, peripherals.FLASH, &spawner).await;
log::info!("Setup complete, entering main loop"); log::info!("Setup complete, entering main loop");
let mut use_first_payload = true;
loop { loop {
info!("Hello world!"); if button1.is_low() {
Timer::after_millis(500).await; log::info!("Button pressed!");
display.clear(Rgb565::BLACK).unwrap();
let payload = if use_first_payload {
&MOCK_PAYLOAD
} else {
&MOCK_PAYLOAD2
};
render_nfc_payload(&mut display, payload);
use_first_payload = !use_first_payload;
}
// info!("Hello world!");
Timer::after_millis(10).await;
} }
// for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.1.0/examples // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.1.0/examples