Вывод всей памяти
До сих пор вы научились проходить аутентификацию с сектором и читать блоки внутри него. В предыдущей главе мы ограничивались сектором 0. В этой главе мы расширим этот подход и выведем всю память метки.
Мы пройдём по всем секторам метки, аутентифицируем каждый сектор и прочитаем каждый блок внутри него. Поскольку аутентификация действительна только для одного сектора, при переходе к следующему сектору её нужно будет пройти заново.
Для каждого сектора мы прочитаем все четыре блока и выведем их содержимое в виде 16 байтов. Чтобы вывод было проще понять, добавим подписи, которые показывают:
- Номер сектора
- Абсолютный номер блока
- Номер блока относительно сектора
- Является ли блок данными производителя, служебным блоком сектора (trailer) или обычными данными
Чтобы сохранить фокус этой главы, мы не будем повторять шаги создания проекта и настройки зависимостей. К этому моменту вы уже должны уверенно работать с нужными крейтами и структурой проекта.
Выберите способ вывода
Как и раньше, основная логика одинакова для обеих схем. Отличается только вид логирования.
Выберите вкладку, которая соответствует тому, как вы выводите результаты.
В этой схеме для вывода данных блоков мы используем defmt через RTT.
Перебор секторов
Начнём с создания вспомогательной функции, которая перебирает все секторы метки. Для метки MIFARE Classic 1K это секторы с 0 по 15.
Для каждого сектора мы выводим заголовок, а затем вызываем read_sector, чтобы пройти аутентификацию и прочитать его блоки.
#![allow(unused)]
fn main() {
fn dump_memory<E, COMM>(
uid: &mfrc522::Uid,
rfid: &mut Mfrc522<COMM, mfrc522::Initialized>,
) -> Result<(), &'static str>
where
COMM: mfrc522::comm::Interface<Error = E>,
{
let mut buff: String<64> = String::new();
for sector in 0..16 {
// Выводим номер сектора
write!(buff, "-----------SECTOR {}-----------", sector)
.expect("failed to write into heapless buff");
defmt::println!("{}", buff);
buff.clear();
read_sector(uid, sector, rfid)?;
}
Ok(())
}
}
Увеличиваем буфер логов
Вывод всей памяти даёт много данных. Увеличьте размер буфера логов, чтобы вывод не обрезался.
#![allow(unused)]
fn main() {
#[embassy_executor::task]
async fn logger_task(usb: embassy_rp::Peri<'static, embassy_rp::peripherals::USB>) {
let driver = embassy_rp::usb::Driver::new(usb, Irqs);
embassy_usb_logger::run!(8192, log::LevelFilter::Info, driver);
}
}
Определяем тип блока
Далее добавим небольшую вспомогательную функцию, которая определяет тип блока по его положению внутри сектора.
#![allow(unused)]
fn main() {
fn get_block_type(sector: u8, rel_block: u8) -> &'static str {
match rel_block {
0 if sector == 0 => "MFD",
3 => "TRAILER",
_ => "DATA",
}
}
}
Чтение сектора с подписями
Функция read_sector похожа на ту, что вы использовали раньше, но теперь она добавляет к выводу больше контекста.
#![allow(unused)]
fn main() {
fn read_sector<E, COMM>(
uid: &mfrc522::Uid,
sector: u8,
rfid: &mut Mfrc522<COMM, mfrc522::Initialized>,
) -> Result<(), &'static str>
where
COMM: mfrc522::comm::Interface<Error = E>,
{
const AUTH_KEY: [u8; 6] = [0xFF; 6];
let mut buff: String<64> = String::new();
let block_offset = sector * 4;
rfid.mf_authenticate(uid, block_offset, &AUTH_KEY)
.map_err(|_| "Auth failed")?;
for abs_block in block_offset..block_offset + 4 {
let rel_block = abs_block - block_offset;
let data = rfid.mf_read(abs_block).map_err(|_| "Read failed")?;
// Выводим данные блока
for &d in data.iter() {
write!(buff, "{:02x} ", d).expect("failed to write byte into buffer");
}
// Выводим тип блока
let block_type = get_block_type(sector, rel_block);
defmt::println!(
"BLOCK {} (REL: {}) | {} | {}",
abs_block,
rel_block,
buff,
block_type
);
buff.clear();
}
defmt::println!("");
Ok(())
}
}
Главный цикл
Главный цикл почти не меняется. Единственное отличие в том, что теперь мы вызываем dump_memory вместо чтения одного сектора.
#![allow(unused)]
fn main() {
loop {
if let Ok(atqa) = rfid.reqa() {
if let Ok(uid) = rfid.select(&atqa) {
if let Err(e) = dump_memory(&uid, &mut rfid) {
defmt::error!("Error reading sector: {:?}", e);
}
let _ = rfid.hlta();
let _ = rfid.stop_crypto1();
Timer::after_millis(500).await;
}
}
Timer::after_millis(200).await;
}
}
Клонируйте существующий проект
Можно клонировать проект, который я создал, и перейти в папку memory-dump.
git clone https://github.com/ImplFerris/pico2-embassy-projects
cd pico2-embassy-projects/rfid/memory-dump/
Дамп
Когда вы запустите программу и поднесёте метку или брелок, вы увидите примерно такой вывод. Если вы заметили байты 0x40..0x43 в блоке 18 (это блок 2 сектора 4) и задались вопросом, откуда они там, — отличное наблюдение! Это пользовательские данные, которые я записал на метку.