2025年12月25日 星期四

WOKWI 模擬 MFRC522 RFID Reader + 5個 Tag 發行到MQTT 上

WOKWI 模擬 MFRC522 RFID Reader + 5個 Tag  發行到MQTT 上











MQTTgo.io

# MQTT 設定
MQTT_BROKER = "mqttgo.io"
MQTT_PORT = 1883
MQTT_TOPIC = "alex9ufo/wokwiRFID/UID"


Python +TKinter



import tkinter as tk

from tkinter import messagebox

import paho.mqtt.client as mqtt


# MQTT 設定

MQTT_BROKER = "mqttgo.io"

MQTT_PORT = 1883

MQTT_TOPIC = "alex9ufo/wokwiRFID/UID"


class RFIDApp:

    def __init__(self, root):

        self.root = root

        self.root.title("MQTT RFID 監控器")

        self.root.geometry("400x250")


        # 連線狀態標籤

        self.status_label = tk.Label(root, text="連線狀態: 嘗試中...", font=("Arial", 12))

        self.status_label.pack(pady=10)


        # UID 顯示區

        tk.Label(root, text="接收到的 UID:", font=("Arial", 10)).pack()

        self.uid_var = tk.StringVar(value="等待感應...")

        self.uid_display = tk.Label(root, textvariable=self.uid_var, font=("Courier", 18, "bold"), fg="blue")

        self.uid_display.pack(pady=20)


        # 初始化 MQTT

        self.client = mqtt.Client()

        self.client.on_connect = self.on_connect

        self.client.on_message = self.on_message


        try:

            self.client.connect(MQTT_BROKER, MQTT_PORT, 60)

            self.client.loop_start()

        except Exception as e:

            self.status_label.config(text=f"連線失敗: {e}", fg="red")


    def on_connect(self, client, userdata, flags, rc):

        if rc == 0:

            self.status_label.config(text="連線狀態: 已連接至 mqttgo.io", fg="green")

            self.client.subscribe(MQTT_TOPIC)

        else:

            self.status_label.config(text=f"連線錯誤 代碼: {rc}", fg="red")


    def on_message(self, client, userdata, msg):

        payload = msg.payload.decode("utf-8")

        # 更新 UI 需要回到主線程,但 tkinter 的 StringVar 是線程安全的

        self.uid_var.set(payload)


if __name__ == "__main__":

    root = tk.Tk()

    app = RFIDApp(root)

    root.mainloop()


將 WOKWI的下面網址

https://wokwi.com/projects/451372804952965121

內容補齊   相關內容如下:




WOKWI 程式

#include <SPI.h>
#include <MFRC522.h>
#include <WiFi.h>
#include <PubSubClient.h>

// ESP32 Pins
#define SS_PIN  5
#define RST_PIN 22

// WiFi & MQTT 設定
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* mqtt_server = "mqttgo.io";
const char* topic = "alex9ufo/wokwiRFID/UID";

WiFiClient espClient;
PubSubClient client(espClient);
MFRC522 mfrc522(SS_PIN, RST_PIN);

void setup_wifi() {
  delay(10);
  Serial.print("Connecting to WiFi...");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected");
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // 建立隨機 Client ID
    String clientId = "ESP32Client-" + String(random(0xffff), HEX);
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  SPI.begin();
  mfrc522.PCD_Init();
 
  setup_wifi();
  client.setServer(mqtt_server, 1883);

  Serial.println(F("System Ready. Waiting for RFID card..."));
  Serial.println(F("系統已準備就緒。請使用滑桿選擇卡片 (1-5)..."));
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // 偵測 RFID 卡片
  if (!mfrc522.PICC_IsNewCardPresent()) return;
  if (!mfrc522.PICC_ReadCardSerial()) return;

  // 格式化 UID
  String currentUID = "";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    currentUID.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? "0" : ""));
    currentUID.concat(String(mfrc522.uid.uidByte[i], HEX));
    if (i < mfrc522.uid.size - 1) currentUID.concat(" ");
  }
  currentUID.toUpperCase();

  Serial.print("UID detected: ");
  Serial.println(currentUID);

  // 發送至 MQTT
  client.publish(topic, currentUID.c_str());
  Serial.println("Sent to MQTT!");

  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
  delay(2000);
}


diagran.json 

{
  "version": 1,
  "author": "Student",
  "editor": "wokwi",
  "parts": [
    { "type": "board-esp32-devkit-c-v4", "id": "esp", "top": -48, "left": -71.96, "attrs": {} },
    { "type": "chip-rfid-rc522", "id": "chip1", "top": -18.18, "left": 216, "attrs": {} }
  ],
  "connections": [
  ],
  "dependencies": {}
}

rfid-rc522.chip.json

{
  "name": "RC522 RFID Reader",
  "author": "Anton Shumeyko",
  "pins": [
    "CS",
    "SCK",
    "MOSI",
    "MISO",
    "IRQ",    
    "GND",
    "RST",
    "VCC",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    ""
  ],
  "controls": [
    {
      "id": "selectedCard",
      "label": "Select Card \n (0 - card not selected, 1-5 - card UID index)",
      "type": "range",
      "min": 0,
      "max": 5,
      "step": 1
    }
  ]
}

rfid-rc522.chip.c

// This file implements an emulation of the MFRC522 RFID chip for Wokwi projects.
// It allows you to simulate SPI communication with the MFRC522, including basic MIFARE commands.
// For more information and source code, see: https://github.com/anton21m
// Author: Anton21m blackdark20@mail.ru

#include "wokwi-api.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define VERSION_REG 0x37
#define NUM_REGISTERS 64
#define FIFO_SIZE 64

// MIFARE commands
#define CMD_REQA 0x26
#define CMD_WUPA 0x52
#define CMD_ANTICOLL 0x93
#define CMD_SEL_CL1 0x93
#define CMD_SEL_CL2 0x95
#define CMD_SEL_CL3 0x97
#define CMD_CT 0x88
#define CMD_AUTH_A 0x60
#define CMD_AUTH_B 0x61
#define CMD_READ 0x30
#define CMD_WRITE 0xA0
#define REG_VERSION 0x92

// Добавьте недостающие:
#define CMD_CALC_CRC      0x03
#define CMD_IDLE          0x00
#define CMD_MEM           0x01
#define CMD_GEN_RANDOM_ID 0x02
#define CMD_TRANSMIT      0x04
#define CMD_RECEIVE       0x08
#define CMD_DECREMENT     0xC0 // MIFARE Decrement
#define CMD_INCREMENT     0xC1 // MIFARE Increment
#define CMD_RESTORE       0xC2 // MIFARE Restore
#define CMD_TRANSFER      0xB0 // MIFARE Transfer
#define CMD_UL_WRITE      0xA2 // MIFARE Ultralight Write

// Pre-defined UIDs for 5 different cards
static const uint8_t CARD_UIDS[5][4] = {
    {0x50, 0x9D, 0x39, 0x23}, // UId1
    {0x77, 0x18, 0x40, 0x05}, // Uid2
    {0x9F, 0xD6, 0xB1, 0xBD}, // Uid3
    {0x0A, 0x1B, 0x2C, 0x3D}, // Uid4
    {0xF1, 0xE2, 0xD3, 0xC4}  // Uid5
};

typedef enum {
  SPI_STATE_IDLE,
  SPI_STATE_WAIT_DATA,
} spi_transaction_state_t;

typedef struct {
  pin_t cs_pin;
  uint32_t spi;

  uint8_t registers[NUM_REGISTERS];
  uint8_t fifo[FIFO_SIZE];
  uint8_t fifo_len;

  uint8_t spi_buffer[18];
  spi_transaction_state_t spi_transaction_state;
  uint8_t current_address;
  bool is_read;
  uint8_t read_count;

  // Emulated card data (MIFARE Classic 1K = 16 sectors * 4 blocks * 16 bytes)
  uint8_t card_data[16 * 4 * 16];
  uint8_t uid[4];

  // NEW: Selected card index and Wokwi attribute ID
  uint32_t selected_card_attr_id;
  uint8_t selected_card_index; // 0 = no card, 1-5 = CARD_UIDS index + 1

  // New internal data register for MIFARE Value Block operations (Restore/Transfer)
  uint8_t internal_data_register[16];

  // State variables
  bool card_selected;
  bool authenticated;

  // Флаг: карта уже была обнаружена (для IsNewCardPresent)
  bool card_was_present;

  // Internal variables for anticollision, auth, etc.
  uint8_t anticoll_step;
  bool uid_read_completed;
  uint8_t cascade_level;
  uint8_t current_level_known_bits;
 
  // New variables for proper SELECT handling
  bool select_completed;
  uint8_t select_response_sent;

  // Streaming write state: true while CS is low and we are writing consecutive bytes into FIFO
  bool stream_write_to_fifo;

  // Backdoor variables
  bool uid_backdoor_step1;
  bool uid_backdoor_open;

  // MIFARE write command state
  int8_t pending_write_block; // -1 if no pending write, otherwise block address
  uint8_t pending_write_len;  // Expected length of data for pending write

  // NEW: MIFARE two-step command state
  int8_t pending_mifare_twostep_command; // -1 if no pending, otherwise the command (CMD_DECREMENT, CMD_INCREMENT, etc.)
  uint8_t pending_mifare_twostep_block_addr; // The block address for the pending two-step command
} chip_state_t;

// Forward declarations
static void chip_pin_change(void *user_data, pin_t pin, uint32_t value);
static void chip_spi_done(void *user_data, uint8_t *buffer, uint32_t count);

// MIFARE command processing functions
static void handle_reqa_wupa_command(chip_state_t *chip);
static void handle_anticoll_command(chip_state_t *chip);
static void handle_select_command(chip_state_t *chip);

// SPI read/write functions
static void handle_spi_read_command(chip_state_t *chip);
static void handle_spi_write_command(chip_state_t *chip, uint8_t val);
static void read_version_register(chip_state_t *chip);
static void read_comirq_register(chip_state_t *chip);
static void read_fifo_level_register(chip_state_t *chip);
static void read_fifo_data_register(chip_state_t *chip);
static void write_fifo_register(chip_state_t *chip, uint8_t val);
static void write_command_register(chip_state_t *chip, uint8_t val);

// FIFO management functions
static void fifo_push(chip_state_t *chip, uint8_t val);
static void fifo_remove_bytes(chip_state_t *chip, int bytes_to_remove);
static void update_fifo_level_register(chip_state_t *chip);

// State management functions
static void reset_chip_state(chip_state_t *chip);
static void set_irq_flag(chip_state_t *chip);
static void clear_irq_flag(chip_state_t *chip, uint8_t flag);
static void set_specific_irq_flag(chip_state_t *chip, uint8_t flag);
static void log_chip_state(chip_state_t *chip);
void send_ack_response(chip_state_t *chip);

// CRC_A для ISO14443A (полином 0x8408, начальное значение 0x6363)
static void calc_crc_a(const uint8_t *data, size_t len, uint8_t *crc) {
    uint16_t crcval = 0x6363;
    for (size_t i = 0; i < len; i++) {
        crcval ^= data[i];
        for (int j = 0; j < 8; j++) {
            if (crcval & 0x0001)
                crcval = (crcval >> 1) ^ 0x8408;
            else
                crcval = (crcval >> 1);
        }
    }
    crc[0] = crcval & 0xFF;
    crc[1] = (crcval >> 8) & 0xFF;
}

static void perform_crc_calculation(chip_state_t *chip) {
  // Выполнить CRC_A для текущего содержимого FIFO
  if (chip->fifo_len == 0) {
    // Нечего считать - возвращаем 0
    chip->registers[0x22] = 0x00; // CRCResultRegL
    chip->registers[0x21] = 0x00; // CRCResultRegH
  } else {
    uint8_t crc[2];
    calc_crc_a(chip->fifo, chip->fifo_len, crc);
    chip->registers[0x22] = crc[0]; // CRCResultRegL
    chip->registers[0x21] = crc[1]; // CRCResultRegH
  }
  // Установить бит CRCIRq (0x04) в DivIrqReg (адрес 0x05)
  chip->registers[0x05] |= 0x04;
//   printf("CRC calculated for %d bytes -> %02X %02X, DivIrqReg: 0x%02X\n", chip->fifo_len, chip->registers[0x22], chip->registers[0x21], chip->registers[0x05]);
}

// Helper to decode a 4-byte value from a MIFARE value block
static int32_t decode_mifare_value(const uint8_t *buffer) {
    return (int32_t)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24));
}

// Helper to encode a 4-byte value into a MIFARE value block format
static void encode_mifare_value(uint8_t *buffer, int32_t value, uint8_t block_address) {
    buffer[0] = (uint8_t)(value & 0xFF);
    buffer[1] = (uint8_t)((value >> 8) & 0xFF);
    buffer[2] = (uint8_t)((value >> 16) & 0xFF);
    buffer[3] = (uint8_t)((value >> 24) & 0xFF);

    buffer[4] = ~buffer[0];
    buffer[5] = ~buffer[1];
    buffer[6] = ~buffer[2];
    buffer[7] = ~buffer[3];

    buffer[8] = buffer[0];
    buffer[9] = buffer[1];
    buffer[10] = buffer[2];
    buffer[11] = buffer[3];

    buffer[12] = block_address;
    buffer[13] = block_address;
    buffer[14] = block_address;
    buffer[15] = ~block_address;
}

void chip_init(void) {
  chip_state_t *chip = calloc(1, sizeof(chip_state_t));
  chip->cs_pin = pin_init("CS", INPUT_PULLUP);

  // Initialize Wokwi control for card selection
  chip->selected_card_attr_id = attr_init("selectedCard", 0); // Default to 0 (no card)
  chip->selected_card_index = attr_read(chip->selected_card_attr_id);

  // Initialize UID (will be updated when card is selected)
  if (chip->selected_card_index > 0 && chip->selected_card_index <= 5) {
      memcpy(chip->uid, CARD_UIDS[chip->selected_card_index - 1], 4);
  } else {
      // Default UID if no card selected or invalid index
      memset(chip->uid, 0, 4);
  }

  // Initialize registers, set version reg to typical MFRC522 version
  chip->registers[VERSION_REG] = 0x92;

  printf("INIT, UID %02X %02X %02X %02X\n",
    chip->uid[0], chip->uid[1], chip->uid[2], chip->uid[3]);

  // Initialize card data with default MIFARE Classic 1K structure
  memset(chip->card_data, 0, sizeof(chip->card_data));

  // Populate Block 0 (Manufacturer Block) with UID and BCC
  chip->card_data[0] = chip->uid[0];
  chip->card_data[1] = chip->uid[1];
  chip->card_data[2] = chip->uid[2];
  chip->card_data[3] = chip->uid[3];
  chip->card_data[4] = chip->uid[0] ^ chip->uid[1] ^ chip->uid[2] ^ chip->uid[3]; // BCC
  // The rest of block 0 is manufacturer data, can be left as 0.

  // Populate all sector trailers with default keys and access bits.
  // This mimics a fresh MIFARE Classic card.
  const uint8_t default_trailer[16] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Key A
    0xFF, 0x07, 0x80,                   // Access Bits (default configuration)
    0x69,                               // User Data Byte (GPB)
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // Key B
  };

  // MIFARE Classic 1K has 16 sectors.
  for (int sector = 0; sector < 16; sector++) {
    // The trailer is the last block of the sector (block 3).
    int trailer_block_index = sector * 4 + 3;
    memcpy(&chip->card_data[trailer_block_index * 16], default_trailer, 16);
  }

  // Setup pin watching and SPI
  pin_watch_config_t watch_cfg = {
    .edge = BOTH,
    .pin_change = chip_pin_change,
    .user_data = chip,
  };
  pin_watch(chip->cs_pin, &watch_cfg);

  spi_config_t spi_cfg = {
    .sck = pin_init("SCK", INPUT),
    .miso = pin_init("MISO", INPUT),
    .mosi = pin_init("MOSI", INPUT),
    .done = chip_spi_done,
    .user_data = chip,
  };
  chip->spi = spi_init(&spi_cfg);

  // Initialize important registers
  memset(chip->registers, 0, NUM_REGISTERS);
  chip->registers[VERSION_REG] = 0x92;  // Version
  chip->registers[0x04] = 0x00;        // ComIrqReg - все флаги сброшены
  chip->registers[0x06] = 0x00;        // ErrorReg - нет ошибок
  chip->registers[0x0A] = 0x00;        // FIFOLevelReg
  chip->registers[0x0C] = 0x80;        // ControlReg (PowerOn=1)
  chip->registers[0x26] = 0x70;        // RFCfgReg (default to 48dB gain)
 
  // Clear FIFO
  chip->fifo_len = 0;
  memset(chip->fifo, 0, FIFO_SIZE);
 
  // Initialize state
  chip->card_selected = false;
  chip->authenticated = false;
  chip->anticoll_step = 0;
  chip->uid_read_completed = false;
  chip->cascade_level = 1;
  chip->current_level_known_bits = 0;
  chip->select_completed = false;
  chip->select_response_sent = 0;
  chip->stream_write_to_fifo = false;
  chip->spi_transaction_state = SPI_STATE_IDLE;
  chip->pending_write_block = -1;
  chip->pending_write_len = 0;
  chip->pending_mifare_twostep_command = -1; // NEW
  chip->pending_mifare_twostep_block_addr = 0; // NEW
  chip->card_was_present = false; // Инициализируем флаг
 
  // Initialize internal data register to all zeros
  memset(chip->internal_data_register, 0, sizeof(chip->internal_data_register));
 
  printf("Chip initialized - ComIrqReg: 0x%02X\n", chip->registers[0x04]);
 
  // Проверяем состояние всех важных регистров
  printf("Initial register state:\n");
  printf("ComIrqReg (0x04): 0x%02X\n", chip->registers[0x04]);
  printf("FIFOLevelReg (0x0A): 0x%02X\n", chip->registers[0x0A]);
  printf("ControlReg (0x0C): 0x%02X\n", chip->registers[0x0C]);
  printf("VersionReg (0x37): 0x%02X\n", chip->registers[VERSION_REG]);
}

void chip_pin_change(void *user_data, pin_t pin, uint32_t value) {
  chip_state_t *chip = (chip_state_t *)user_data;
  if (pin == chip->cs_pin) {
    if (value == LOW) {
      chip->spi_transaction_state = SPI_STATE_IDLE;
      spi_start(chip->spi, chip->spi_buffer, 1);
    } else {
      spi_stop(chip->spi);
      // Do NOT reset anticoll_step here. It must be preserved across transactions
      // during card selection sequence. It will be reset by REQA/WUPA or SELECT completion.
      // chip->anticoll_step = 0;
      chip->stream_write_to_fifo = false;
    }
  }
}

void chip_spi_done(void *user_data, uint8_t *buffer, uint32_t count) {
  chip_state_t *chip = (chip_state_t*)user_data;

  // NEW: Read selected card from Wokwi control and update UID if changed
  uint8_t new_selected_card_index = attr_read(chip->selected_card_attr_id);
  if (new_selected_card_index != chip->selected_card_index) {
      chip->selected_card_index = new_selected_card_index;
      if (chip->selected_card_index > 0 && chip->selected_card_index <= 5) {
          memcpy(chip->uid, CARD_UIDS[chip->selected_card_index - 1], 4);
          chip->card_was_present = false; // Карта убрана или новая — сбросить флаг
      } else {
          memset(chip->uid, 0, 4);
          chip->card_was_present = false; // Карта убрана — сбросить флаг
      }
      printf("Selected card changed to: %d\n", chip->selected_card_index);
      // Reinitialize card data for new card
      memset(chip->card_data, 0, sizeof(chip->card_data));
      chip->card_data[0] = chip->uid[0];
      chip->card_data[1] = chip->uid[1];
      chip->card_data[2] = chip->uid[2];
      chip->card_data[3] = chip->uid[3];
      chip->card_data[4] = chip->uid[0] ^ chip->uid[1] ^ chip->uid[2] ^ chip->uid[3]; // BCC
      const uint8_t default_trailer[16] = {
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Key A
        0xFF, 0x07, 0x80,                   // Access Bits (default configuration)
        0x69,                               // User Data Byte (GPB)
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // Key B
      };
      for (int sector = 0; sector < 16; sector++) {
        int trailer_block_index = sector * 4 + 3;
        memcpy(&chip->card_data[trailer_block_index * 16], default_trailer, 16);
      }
  }

  if (pin_read(chip->cs_pin) == HIGH) {
    return; // CS is high, transaction is over.
  }

  switch (chip->spi_transaction_state) {
    case SPI_STATE_IDLE: {
      uint8_t cmd_byte = buffer[0];
      chip->current_address = (cmd_byte >> 1) & 0x3F;
      chip->is_read = (cmd_byte & 0x80) != 0;
     
      if (chip->stream_write_to_fifo && (chip->current_address != 0x09 || chip->is_read)) {
        chip->stream_write_to_fifo = false;
      }

    // if (chip->current_address == 0x09 || chip->current_address == 0x01) {
    //       printf("SPI cmd: 0x%02X, addr: 0x%02X, read: %s\n", cmd_byte, chip->current_address, chip->is_read ? "yes" : "no");
    // }

    if (chip->is_read) {
      handle_spi_read_command(chip);
        if (chip->read_count > 0) {
      spi_start(chip->spi, chip->spi_buffer, chip->read_count);
        }
        chip->spi_transaction_state = SPI_STATE_IDLE; // Stay idle, ready for next command
      } else { // It's a write command
        if (chip->current_address == 0x09) {
            chip->stream_write_to_fifo = true;
        }
        chip->spi_transaction_state = SPI_STATE_WAIT_DATA;
        spi_start(chip->spi, chip->spi_buffer, 1); // Wait for the data byte
      }
      break;
    }

    case SPI_STATE_WAIT_DATA: {
      uint8_t data_byte = buffer[0];
      handle_spi_write_command(chip, data_byte);

      if (chip->stream_write_to_fifo) {
        chip->spi_transaction_state = SPI_STATE_WAIT_DATA;
    spi_start(chip->spi, chip->spi_buffer, 1);
  } else {
        chip->spi_transaction_state = SPI_STATE_IDLE;
      }
      break;
    }
  }
}

// MIFARE command processing functions
static void handle_reqa_wupa_command(chip_state_t *chip) {
  // Only respond if a card is selected (index > 0)
  if (chip->selected_card_index > 0) {
      if (!chip->card_was_present) {
          chip->fifo[0] = 0x04;  // ATQA
          chip->fifo[1] = 0x00;
          chip->fifo_len = 2;
          update_fifo_level_register(chip);
          // Устанавливаем только RxIRq для успешного приема данных
      set_specific_irq_flag(chip, 0x20);  // RxIRq (corrected from 0x04)
          chip->anticoll_step = 0;
          chip->registers[0x0C] &= ~0x07; // Сброс RxLastBits в 0, так как ATQA - это полные байты
          chip->card_was_present = true; // Установить флаг: карта обнаружена
      } else {
          // Карта уже была обнаружена, не отвечаем повторно
          chip->fifo_len = 0;
          update_fifo_level_register(chip);
      }
  } else {
      chip->fifo_len = 0; // Clear FIFO if no card is selected
      update_fifo_level_register(chip); // Update FIFO level register
  }
}

static void handle_anticoll_command(chip_state_t *chip) {
//   printf("ANTICOLL command - step: %d, fifo_len: %d\n", chip->anticoll_step, chip->fifo_len);
//   log_chip_state(chip, "before ANTICOLL processing");
 
//   if (chip->fifo_len > 0) {
//     printf("ANTICOLL fifo content: ");
//     for (int i = 0; i < chip->fifo_len && i < 4; i++) {
//       printf("%02X ", chip->fifo[i]);
//     }
//     printf("\n");
//   }
 
  // Only process if a card is selected
  if (chip->selected_card_index == 0) {
      printf("ANTICOLL - no card selected, no response\n");
      chip->fifo_len = 0;
      update_fifo_level_register(chip);
      return;
  }

  // Обработка ANTICOLLISION для Cascade Level 1
  if (chip->anticoll_step == 0 && chip->fifo_len >= 1 && chip->fifo[0] == CMD_SEL_CL1) {
    // Очищаем FIFO перед формированием ответа
    chip->fifo_len = 0;
    printf("ANTICOLL - responding with UID for card %d\n", chip->selected_card_index);
    chip->fifo[chip->fifo_len++] = chip->uid[0];
    chip->fifo[chip->fifo_len++] = chip->uid[1];
    chip->fifo[chip->fifo_len++] = chip->uid[2];
    chip->fifo[chip->fifo_len++] = chip->uid[3];
    uint8_t bcc = chip->uid[0] ^ chip->uid[1] ^ chip->uid[2] ^ chip->uid[3];
    chip->fifo[chip->fifo_len++] = bcc;  // UID + BCC
    update_fifo_level_register(chip);
    set_specific_irq_flag(chip, 0x20);  // RxIRq (corrected from 0x04)
    chip->anticoll_step = 1;
    chip->current_level_known_bits = 32;  // Все 32 бита известны
    chip->registers[0x0C] &= ~0x07; // Сброс RxLastBits в 0, так как UID - это полные байты
//     printf("ANTICOLL processed - UID and BCC in FIFO: ");
//     for (int i = 0; i < chip->fifo_len; i++) {
//       printf("%02X ", chip->fifo[i]);
//     }
//     printf("\n");
  }
 
//   log_chip_state(chip, "after ANTICOLL processing");
}

static void handle_select_command(chip_state_t *chip) {
//   printf("SELECT command detected - processing full UID selection\n");
//   printf("SELECT command content: ");
//   for (int i = 0; i < chip->fifo_len; i++) {
//     printf("%02X ", chip->fifo[i]);
//   }
//   printf("\n");

  // Only process if a card is selected
  if (chip->selected_card_index == 0) {
      printf("SELECT - no card selected, no response\n");
      chip->fifo_len = 0;
      update_fifo_level_register(chip);
      return;
  }

  // Check if this is the correct SELECT command for our UID
  if (chip->fifo[2] == chip->uid[0] && chip->fifo[3] == chip->uid[1] &&
      chip->fifo[4] == chip->uid[2] && chip->fifo[5] == chip->uid[3]) {
    printf("SELECT - UID match for card %d, sending SAK\n", chip->selected_card_index);

    // Clear FIFO before sending SAK
      chip->fifo_len = 0;

    // Send SAK with CRC
    chip->fifo[0] = 0x08;  // SAK for MIFARE Classic 1K
    uint8_t crc[2];
    calc_crc_a(chip->fifo, 1, crc);
    chip->fifo[1] = crc[0];
    chip->fifo[2] = crc[1];
    chip->fifo_len = 3;

      update_fifo_level_register(chip);
    set_specific_irq_flag(chip, 0x20);  // RxIRq (corrected from 0x04)

      chip->card_selected = true;
    chip->authenticated = false; // Reset authentication state on new selection
    chip->select_completed = true;
    chip->registers[0x0C] &= ~0x07; // Сброс RxLastBits в 0, так как SAK - это полный байт
//     printf("SELECT completed - SAK+CRC sent: %02X %02X %02X\n", chip->fifo[0], chip->fifo[1], chip->fifo[2]);
    } else {
    printf("SELECT failed - UID mismatch for card %d\n", chip->selected_card_index);
    printf("Expected UID: %02X %02X %02X %02X\n",
           chip->uid[0], chip->uid[1], chip->uid[2], chip->uid[3]);
    printf("Received UID: %02X %02X %02X %02X\n",
           chip->fifo[2], chip->fifo[3], chip->fifo[4], chip->fifo[5]);
      chip->fifo_len = 0;
      update_fifo_level_register(chip);
    }
  // Reset anticoll_step after SELECT is done
      chip->anticoll_step = 0;
}

void process_mifare_command(chip_state_t *chip) {
  if (chip->fifo_len == 0) return;

  // Обработка второй фазы MIFARE WRITE, если она ожидается
  if (chip->pending_write_block != -1 && chip->fifo_len == 18) {
    printf("Processing MIFARE WRITE Phase 2 (block 0x%02X) - received 18 bytes (16 data + 2 CRC)\n", chip->pending_write_block);
    // Проверяем, авторизован ли доступ к сектору
    bool allow_write = chip->authenticated;
    if (chip->pending_write_block == 0) { // UID block
      allow_write = true; // Always allow write to block 0 for now (can be restricted later by authentication or backdoor)
    }

    if (allow_write) {
      // Копируем только 16 байт данных, игнорируя последние 2 байта CRC
      memcpy(&chip->card_data[chip->pending_write_block * 16], chip->fifo, 16);
      if (chip->pending_write_block == 0) {
        // Update UID from block 0
        memcpy(chip->uid, &chip->card_data[0], 4);
      }
     
      // Отправляем 4-битный ACK
      send_ack_response(chip);
    } else {
      printf("WRITE Phase 2 failed: not authenticated for this sector.\n");
      // Отправляем NACK или ничего не отправляем, позволяя таймауту произойти
      chip->fifo_len = 0; // Clear FIFO
      update_fifo_level_register(chip);
    }

    // Сбрасываем состояние записи
    chip->pending_write_block = -1;
    chip->pending_write_len = 0;
    return; // Завершаем обработку команды
  }
 
  // NEW: Обработка второй фазы двухступенчатых MIFARE команд (Decrement, Increment, Restore, Transfer)
  // These commands are split into two PCD_MIFARE_Transceive calls.
  // First call: command + block address
  // Second call: 4 bytes of data (for Increment/Decrement) or 0 (for Restore/Transfer)
  if (chip->pending_mifare_twostep_command != -1 && chip->fifo_len == 4) { // Expecting 4 bytes of data
      uint8_t command = chip->pending_mifare_twostep_command;
      uint8_t blockAddr = chip->pending_mifare_twostep_block_addr;
     
      if (chip->authenticated) {
          switch (command) {
              case CMD_DECREMENT: {
                  int32_t delta = decode_mifare_value(chip->fifo);
                  memcpy(chip->internal_data_register, &chip->card_data[blockAddr * 16], 16);
                  int32_t currentValue = decode_mifare_value(chip->internal_data_register);
                  currentValue -= delta;
                  encode_mifare_value(chip->internal_data_register, currentValue, blockAddr);
                  memcpy(&chip->card_data[blockAddr * 16], chip->internal_data_register, 16);
                  printf("MIFARE DECREMENT executed on block 0x%02X with delta %d. New value: %d\n", blockAddr, delta, currentValue);
                  break;
              }
              case CMD_INCREMENT: {
                  int32_t delta = decode_mifare_value(chip->fifo);
                  memcpy(chip->internal_data_register, &chip->card_data[blockAddr * 16], 16);
                  int32_t currentValue = decode_mifare_value(chip->internal_data_register);
                  currentValue += delta;
                  encode_mifare_value(chip->internal_data_register, currentValue, blockAddr);
                  memcpy(&chip->card_data[blockAddr * 16], chip->internal_data_register, 16);
                  printf("MIFARE INCREMENT executed on block 0x%02X with delta %d. New value: %d\n", blockAddr, delta, currentValue);
                  break;
              }
              case CMD_RESTORE:\
                  // For RESTORE, the action (copying to internal_data_register) happens in Phase 1.
                  // This Phase 2 just needs to send ACK.
                  printf("MIFARE RESTORE Phase 2 (data) received, data ignored. Command for block 0x%02X\n", blockAddr);
                  break;
              case CMD_TRANSFER:\
                  // For TRANSFER, the action (copying from internal_data_register to block) happens in Phase 1.
                  // This Phase 2 just needs to send ACK.
                  printf("MIFARE TRANSFER Phase 2 (data) received, data ignored. Command for block 0x%02X\n", blockAddr);
                  break;
          }
          // Send 4-bit ACK
          send_ack_response(chip);
      } else {
          printf("Two-step command (0x%02X) Phase 2 failed: not authenticated for block 0x%02X.\n", command, blockAddr);
          chip->fifo_len = 0;
          update_fifo_level_register(chip);
      }
      chip->pending_mifare_twostep_command = -1;
      chip->pending_mifare_twostep_block_addr = 0;
      return;
  }


  uint8_t cmd = chip->fifo[0];
  printf("Processing MIFARE command: 0x%02X (fifo_len=%d, anticoll_step=%d)\n", cmd, chip->fifo_len, chip->anticoll_step);

  switch (cmd) {
    case CMD_REQA:
    case CMD_WUPA:
      // printf("Handling REQA/WUPA command\n");
      handle_reqa_wupa_command(chip);
      break;

    case CMD_SEL_CL1:
    case CMD_SEL_CL2:
    case CMD_SEL_CL3:
      if (chip->anticoll_step == 1 && chip->fifo_len >= 9) {
        handle_select_command(chip);
      } else {
      handle_anticoll_command(chip);
      }
      break;

    case CMD_READ:
      // printf("Handling READ command (block 0x%02X)\n", chip->fifo[1]);
      if (chip->authenticated) {
        if (chip->fifo_len >= 2) {
          uint8_t blockAddr = chip->fifo[1];
          if (blockAddr < 64) { // 16 sectors * 4 blocks/sector
            printf("Reading block %d\n", blockAddr);
            // Copy 16 bytes from emulated card memory
            chip->fifo_len = 0; // Clear FIFO before filling
            memcpy(chip->fifo, &chip->card_data[blockAddr * 16], 16);
           
            // Append CRC
            uint8_t crc[2];
            calc_crc_a(chip->fifo, 16, crc);
            chip->fifo[16] = crc[0];
            chip->fifo[17] = crc[1];
            chip->fifo_len = 18;
            update_fifo_level_register(chip);
            set_specific_irq_flag(chip, 0x20); // RxIRq
            chip->registers[0x0C] &= ~0x07; // Clear RxLastBits to 0 (8 valid bits)
          } else {
            printf("READ failed: block address %d is out of bounds.\n", blockAddr);
            chip->fifo_len = 0;
            update_fifo_level_register(chip);
          }
        } else {
            printf("READ failed: command too short.\n");
            chip->fifo_len = 0;
            update_fifo_level_register(chip);
        }
      } else {
        printf("READ failed: not authenticated for this sector.\n");
        // Don't respond, let it time out.
        chip->fifo_len = 0;
        update_fifo_level_register(chip);
      }
      break;

    case CMD_WRITE:
      printf("Handling WRITE command (block 0x%02X)\n", chip->fifo[1]);
      uint8_t blockAddr = chip->fifo[1];
      bool allow_write = false;
      if (chip->authenticated) {
        allow_write = true;
      }
      // 如果後門打開,則允許寫入區塊 0。
      if (blockAddr == 0 && chip->uid_backdoor_open) {
        allow_write = true;
        printf("Backdoor open: allowing write to block 0 without authentication!\n");
        chip->uid_backdoor_open = false; // Сбросить после успешной записи
      }
      if (allow_write) {
        if (chip->fifo_len >= 2) { // CMD_WRITE + block_addr + CRC
          // First phase of MIFARE WRITE: send ACK and set up for next 16 bytes
          chip->pending_write_block = blockAddr;
          chip->pending_write_len = 16;

          // Send 4-bit ACK
          send_ack_response(chip);
        } else {
          printf("WRITE failed: command too short for phase 1.\n");
          chip->fifo_len = 0;
          update_fifo_level_register(chip);
        }
      }
      else {
        printf("WRITE failed: not authenticated for this sector.\n");
        chip->fifo_len = 0;
        update_fifo_level_register(chip);
      }
      break;

    case CMD_DECREMENT: // 0xC0
    case CMD_INCREMENT: // 0xC1
    case CMD_RESTORE:   // 0xC2
    case CMD_TRANSFER:  // 0xB0
      // Phase 1 of MIFARE Two-Step Commands (command + block address)
      if (chip->fifo_len >= 2) {
          uint8_t blockAddr = chip->fifo[1];
          if (blockAddr < 64) {
              chip->pending_mifare_twostep_command = cmd;
              chip->pending_mifare_twostep_block_addr = blockAddr;
             
              if (cmd == CMD_RESTORE) {
                  if (chip->authenticated) {
                      memcpy(chip->internal_data_register, &chip->card_data[blockAddr * 16], 16);
                      printf("MIFARE RESTORE executed: block 0x%02X restored to internal register.\n", blockAddr);
                  } else {
                      printf("MIFARE RESTORE failed: not authenticated for block 0x%02X.\n", blockAddr);
                      chip->fifo_len = 0;
                      update_fifo_level_register(chip);
                      return;
                  }
              } else if (cmd == CMD_TRANSFER) {
                  if (chip->authenticated) {
                      memcpy(&chip->card_data[blockAddr * 16], chip->internal_data_register, 16);
                      printf("MIFARE TRANSFER executed: internal register transferred to block 0x%02X.\n", blockAddr);
                  } else {
                      printf("MIFARE TRANSFER failed: not authenticated for block 0x%02X.\n", blockAddr);
                      chip->fifo_len = 0;
                      update_fifo_level_register(chip);
                      return;
                  }
              }

              // Send 4-bit ACK for the first phase
              send_ack_response(chip);
          } else {
              printf("Two-step command (0x%02X) failed: block address out of bounds.\n", cmd);
              chip->fifo_len = 0;
              update_fifo_level_register(chip);
          }
      } else {
          printf("Two-step command (0x%02X) failed: command too short for phase 1.\n", cmd);
          chip->fifo_len = 0;
          update_fifo_level_register(chip);
      }
      break;

    case CMD_UL_WRITE:
      printf("Handling MIFARE ULTRALIGHT WRITE command (page 0x%02X)\n", chip->fifo[1]);
      if (chip->fifo_len >= 6) { // CMD + pageAddr + data (4 bytes) + CRC (2 bytes)
        uint8_t pageAddr = chip->fifo[1];
        // MIFARE Ultralight имеет 16 страниц (0-15), каждая по 4 байта.
        // Проверяем, что адрес страницы допустим.
        // Page 0 is R/O UID, Page 1 is R/O internal, Page 2 is R/W
        // The lib tests write to page 4.
        if (pageAddr >= 2 && pageAddr < 16) {
          memcpy(&chip->card_data[pageAddr * 16], &chip->fifo[2], 4); // Copy only 4 bytes
          // Отправляем 4-битный ACK
          send_ack_response(chip);
        } else {
          printf("MIFARE ULTRALIGHT WRITE failed: page address %d out of bounds or read-only.\n", pageAddr);
          chip->fifo_len = 0;
          update_fifo_level_register(chip);
        }
      } else {
        printf("MIFARE ULTRALIGHT WRITE failed: invalid command length.\n");
        chip->fifo_len = 0;
        update_fifo_level_register(chip);
      }
      break;

    case 0x50: // HALT
      reset_chip_state(chip); // 重置狀態以重新連接
      chip->fifo_len = 0;
      chip->uid_backdoor_step1 = true; // 為下一個命令做好準備 0x40
      // set_specific_irq_flag(chip, 0x10); // IdleIRq - 此行已被刪除
      printf("HALT command received. Card state reset for re-discovery. No response will be sent.\n");
      break;
    case 0x40:
      if (chip->uid_backdoor_step1) {
        send_ack_response(chip);
        chip->uid_backdoor_step1 = false;
        chip->uid_backdoor_open = true; // 我們授權下一步
      } else {
        chip->fifo_len = 0;
      }
      break;
    case 0x43:
      if (chip->uid_backdoor_open) {
        send_ack_response(chip);
        // 現在允許寫入磁區 0。
        chip->uid_backdoor_open = true;
      } else {
        chip->fifo_len = 0;
      }
      break;

    case CMD_AUTH_A:
    case CMD_AUTH_B:
      // printf("Handling AUTHENTICATE command\n");
      // Simplified authentication: we just check the command in FIFO.
      // A real implementation would check the key against the sector trailer.
      if (chip->fifo_len >= 1 && (chip->fifo[0] == CMD_AUTH_A || chip->fifo[0] == CMD_AUTH_B)) {
          printf("Authentication successful (simulated)\n");
          chip->authenticated = true;
          // The command completes when IdleIRq is set.
          set_specific_irq_flag(chip, 0x10); // IdleIRq
      } else {
          printf("Authentication failed: incorrect command in FIFO (len=%d)\n", chip->fifo_len);
          // Maybe set an error flag? For now, do nothing and let it time out.
      }
      chip->fifo_len = 0; // Clear FIFO after auth attempt
      update_fifo_level_register(chip);
      chip->registers[0x01] = 0; // Go to Idle
      break;

    case CMD_CT:
      // printf("Handling CT command (Transceive)\n");
      // This command is typically used for Transmit and Receive.
      // For self-test, we just acknowledge it.
      set_specific_irq_flag(chip, 0x20); // TxIRq
      chip->registers[0x01] = 0; // Go to Idle
      break;

    case CMD_CALC_CRC:
      // printf("Handling CALC_CRC command\n");
      perform_crc_calculation(chip);
      chip->registers[0x01] = 0; // Go to Idle
      break;

    case CMD_IDLE:
      // printf("Handling IDLE command\n");
      chip->registers[0x01] = 0; // Go to Idle
      break;

    case CMD_MEM:
      // printf("Handling MEM command (Transfer FIFO to internal buffer)\n");
      // This command transfers FIFO data to internal buffer for self-test
      // In our emulation, we just acknowledge the command
      set_specific_irq_flag(chip, 0x10); // IdleIRq
      chip->registers[0x01] = 0; // Go to Idle
      break;

    case CMD_GEN_RANDOM_ID:
      // printf("Handling GEN_RANDOM_ID command\n");
      // This command generates a random ID for self-test
      // In our emulation, we just acknowledge the command
      set_specific_irq_flag(chip, 0x10); // IdleIRq
      chip->registers[0x01] = 0; // Go to Idle
      break;

    case CMD_TRANSMIT:
      // printf("Handling TRANSMIT command\n");
      // This command transmits data from FIFO for self-test
      // In our emulation, we just acknowledge the command
      set_specific_irq_flag(chip, 0x20); // TxIRq
      chip->registers[0x01] = 0; // Go to Idle
      break;

    case CMD_RECEIVE:
      // printf("Handling RECEIVE command\n");
      // This command activates the receiver for self-test
      // In our emulation, we just acknowledge the command
      set_specific_irq_flag(chip, 0x10); // IdleIRq
      chip->registers[0x01] = 0; // Go to Idle
      break;

    default:
      printf("Unknown MIFARE command: 0x%02X\n", cmd);
      break;
  }
}

// SPI read/write functions
static void read_version_register(chip_state_t *chip) {
  chip->spi_buffer[0] = 0x92;
  chip->read_count = 1;
}

static void read_comirq_register(chip_state_t *chip) {
  chip->spi_buffer[0] = chip->registers[0x04];
  chip->read_count = 1;
//   printf("ComIrqReg read: 0x%02X (IRQ flags: RxIRq=%d, IdleIRq=%d, LoAlertIRq=%d, ErrIRq=%d, TimerIRq=%d, TxIRq=%d)\n",
//          chip->registers[0x04],
//          (chip->registers[0x04] & 0x20) ? 1 : 0,  // RxIRq
//          (chip->registers[0x04] & 0x10) ? 1 : 0,  // IdleIRq
//          (chip->registers[0x04] & 0x02) ? 1 : 0,  // LoAlertIRq
//          (chip->registers[0x04] & 0x08) ? 1 : 0,  // ErrIRq
//          (chip->registers[0x04] & 0x10) ? 1 : 0,  // TimerIRq (corrected from 0x01 to 0x10 previously)
//          (chip->registers[0x04] & 0x40) ? 1 : 0); // TxIRq (corrected from 0x20 to 0x40 previously)
}

static void read_fifo_level_register(chip_state_t *chip) {
  chip->spi_buffer[0] = chip->fifo_len;
  chip->read_count = 1;
//   printf("FIFOLevelReg read: %d bytes\n", chip->fifo_len);
}

static void read_fifo_data_register(chip_state_t *chip) {
  if (chip->fifo_len > 0) {
    uint8_t bytes_to_read = chip->fifo_len;
    if (bytes_to_read > 18) bytes_to_read = 18;
//     printf("FIFO READ: fifo_len=%d, bytes_to_read=%d, first_byte=0x%02X\n",
//            chip->fifo_len, bytes_to_read, chip->fifo[0]);
   
    memcpy(chip->spi_buffer, chip->fifo, bytes_to_read);
    chip->read_count = bytes_to_read;
   
    // Удаляем прочитанные байты из FIFO, но не очищаем полностью
      fifo_remove_bytes(chip, bytes_to_read);
   
    // Устанавливаем RxIRq только если в FIFO еще есть данные
    if (chip->fifo_len > 0) {
      set_specific_irq_flag(chip, 0x20); // RxIRq
//       printf("RxIRq (0x20) set because FIFO still has %d bytes\n", chip->fifo_len);
    } else {
      clear_irq_flag(chip, 0x20); // Сбросить RxIRq (0x20)
//       printf("RxIRq (0x20) cleared because FIFO is now empty\n");
    }
   
//     printf("FIFO READ complete: buffer prepared with %d bytes, fifo now has %d bytes\n", bytes_to_read, chip->fifo_len);
  } else {
    chip->spi_buffer[0] = 0;
    chip->read_count = 1;
    // printf("FIFO READ: empty FIFO, returning 0\n", chip->fifo_len);
  }
}

static void handle_spi_read_command(chip_state_t *chip) {
  uint8_t val = chip->registers[chip->current_address];
 
  if (chip->current_address == VERSION_REG) {
    read_version_register(chip);
  } else if (chip->current_address == 0x04) {
    read_comirq_register(chip);
  } else if (chip->current_address == 0x0A) {
    read_fifo_level_register(chip);
  } else if (chip->current_address == 0x09) {
    read_fifo_data_register(chip);
  } else if (chip->current_address == 0x0C) {
    // Чтение ControlReg - важно для библиотеки MFRC522
    chip->spi_buffer[0] = val;
    chip->read_count = 1;
//     printf("ControlReg read: 0x%02X (RxLastBits=%d)\n", val, val & 0x07);
  } else if (chip->current_address == 0x06) {
    // Чтение ErrorReg - важно для библиотеки MFRC522
    chip->spi_buffer[0] = val;
    chip->read_count = 1;
//     printf("ErrorReg read: 0x%02X\n", val);
  } else if (chip->current_address == 0x36) {
    // Чтение AutoTestReg - важно для self-test
    chip->spi_buffer[0] = val;
    chip->read_count = 1;
//     printf("AutoTestReg read: 0x%02X\n", val);
  } else {
    chip->spi_buffer[0] = val;
    chip->read_count = 1;
  }
}

static void write_fifo_register(chip_state_t *chip, uint8_t val) {
  if (chip->fifo_len < FIFO_SIZE) {
    fifo_push(chip, val);
   
    // Отладка для всех входящих байтов
//     printf("FIFO push: 0x%02X (len %d)", val, chip->fifo_len);
   
    // Если это начало SELECT команды
//     if (val == 0x93 && chip->fifo_len == 1) {
//       printf(" - SELECT command start\n");
//     }
//     // Если это второй байт SELECT (0x70)
//     else if (chip->fifo_len == 2 && chip->fifo[0] == 0x93 && val == 0x70) {
//       printf(" - SELECT second byte\n");
//     }
//     // Если это байты UID в SELECT команде
//     else if (chip->fifo_len >= 3 && chip->fifo_len <= 6 && chip->fifo[0] == 0x93 && chip->fifo[1] == 0x70) {
//       printf(" - SELECT UID byte %d\n", chip->fifo_len - 2);
//     }
//     // Если это байты CRC в SELECT команде
//     else if (chip->fifo_len >= 7 && chip->fifo_len <= 9 && chip->fifo[0] == 0x93 && chip->fifo[1] == 0x70) {
//       printf(" - SELECT CRC byte %d\n", chip->fifo_len - 7);
//     } else {
//       printf("\n");
//     }

    // Проверяем, собираем ли мы SELECT команду
    if (chip->fifo[0] == CMD_SEL_CL1 && chip->anticoll_step == 1) {
//       printf("Building SELECT command: %d bytes received: ", chip->fifo_len);
//       for (int i = 0; i < chip->fifo_len; i++) {
//         printf("%02X ", chip->fifo[i]);
//       }
//       printf("\n");
     
      // Если получили все 9 байт SELECT команды
      if (chip->fifo_len == 9) {
//         printf("Full SELECT command received, processing...\n");
        process_mifare_command(chip);
      }
    }
  } else {
    printf("FIFO full, ignoring: 0x%02X\n", val);
  }
}

static void write_command_register(chip_state_t *chip, uint8_t val) {
//   printf("CommandReg = 0x%02X\n", val);
  switch (val) {
    case CMD_IDLE: // 0x00
      // printf("Command 0x00 - PCD_Idle (Idle)\n");
      // Clear command related IRQ flags: IdleIRq, RxIRq, TxIRq, ErrIRq
      chip->registers[0x04] &= ~(0x10 | 0x20 | 0x40 | 0x08);
      chip->registers[0x01] = 0x00; // Явно устанавливаем CommandReg в Idle
      break;

    case CMD_MEM: // 0x01
      // printf("Command 0x01 - PCD_Mem (Transfer FIFO to internal buffer)\n");
      set_specific_irq_flag(chip, 0x10); // IdleIRq
      chip->registers[0x01] = 0x00; // Go to Idle
      break;

    case CMD_GEN_RANDOM_ID: // 0x02
      // printf("Command 0x02 - PCD_GenerateRandomID (Generate random ID)\n");
      set_specific_irq_flag(chip, 0x10); // IdleIRq
      chip->registers[0x01] = 0x00; // Go to Idle
      break;

    case CMD_CALC_CRC: // 0x03
      // printf("Command 0x03 - PCD_CalcCRC (Calculate CRC)\n");
      if (chip->registers[0x36] == 0x09) { // Self-test mode
        // printf("Self-test mode detected - generating 64 bytes of test data\n");
        const uint8_t self_test_data[64] = {
          0x00, 0xEB, 0x66, 0xBA, 0x57, 0xBF, 0x23, 0x95,
          0xD0, 0xE3, 0x0D, 0x3D, 0x27, 0x89, 0x5C, 0xDE,
          0x9D, 0x3B, 0xA7, 0x00, 0x21, 0x5B, 0x89, 0x82,
          0x51, 0x3A, 0xEB, 0x02, 0x0C, 0xA5, 0x00, 0x49,
          0x7C, 0x84, 0x4D, 0xB3, 0xCC, 0xD2, 0x1B, 0x81,
          0x5D, 0x48, 0x76, 0xD5, 0x71, 0x61, 0x21, 0xA9,
          0x86, 0x96, 0x83, 0x38, 0xCF, 0x9D, 0x5B, 0x6D,
          0xDC, 0x15, 0xBA, 0x3E, 0x7D, 0x95, 0x3B, 0x2F
        };
        chip->fifo_len = 0;
        memcpy(chip->fifo, self_test_data, 64);
        chip->fifo_len = 64;
        update_fifo_level_register(chip);
        // printf("Self-test data generated: 64 bytes in FIFO\n");
      } else {
        perform_crc_calculation(chip);
      }
      chip->registers[0x01] = 0x00; // Go to Idle
      break;

    case CMD_TRANSMIT: // 0x04
      // printf("Command 0x04 - PCD_Transmit (Transmit data from FIFO)\n");
      set_specific_irq_flag(chip, 0x20); // TxIRq
      chip->registers[0x01] = 0x00; // Go to Idle
      break;

    case CMD_RECEIVE: // 0x08
      // printf("Command 0x08 - PCD_Receive (Activate receiver)\n");
      set_specific_irq_flag(chip, 0x10); // IdleIRq
      chip->registers[0x01] = 0x00; // Go to Idle
      break;

    case 0x0C: // PCD_Transceive
      // printf("Command 0x0C - PCD_Transceive (Transmit and receive)\n");
      if (chip->fifo_len > 0) {
    process_mifare_command(chip);
      }
      chip->registers[0x01] = 0x00; // Go to Idle
      break;

    case 0x0E: // PCD_MFAuthent
      // printf("Command 0x0E - PCD_MFAuthent (MIFARE Authenticate)\n");
      if (chip->fifo_len >= 1 && (chip->fifo[0] == CMD_AUTH_A || chip->fifo[0] == CMD_AUTH_B)) {
          printf("Authentication successful (simulated)\n");
          chip->authenticated = true;
          set_specific_irq_flag(chip, 0x10); // IdleIRq
    } else {
          printf("Authentication failed: incorrect command in FIFO (len=%d)\n", chip->fifo_len);
      }
      chip->fifo_len = 0;
      update_fifo_level_register(chip);
      chip->registers[0x01] = 0x00; // Go to Idle
      break;

    case 0x0F: { // PCD_SoftReset
      // printf("Command 0x0F - PCD_SoftReset (Soft Reset)\n");
      reset_chip_state(chip);
      chip->registers[0x01] = 0x00; // Явно сбрасываем CommandReg в Idle
      break;
    }

    case 0x10: { // Special handling for PowerDown bit (bit 4 of CommandReg)
      // This case handles writing 0x10 to CommandReg, which implies PCD_SoftPowerDown.
      // printf("Command 0x10 - PCD_SoftPowerDown (Power Down)\n");
      chip->registers[0x01] = val; // Set the command to PowerDown
      chip->registers[0x04] |= 0x10; // Set IdleIRq (from datasheet, or observation)
      chip->registers[0x04] &= ~(0x20 | 0x40); // Clear RxIRq and TxIRq
      break;
    }

    default:
      // Other commands may just set the CommandReg and let other logic handle it.
      // If it's a command that transitions from PowerDown to Active (PCD_SoftPowerUp)
      // The library does this by writing any non-0x10 value to CommandReg
      if ((chip->registers[0x01] & 0x10) && !(val & 0x10)) {
        // printf("Transition from PowerDown to Active (PCD_SoftPowerUp) by writing 0x%02X\n", val);
        chip->registers[0x01] = val; // Store the new command
        chip->registers[0x04] &= ~0x10; // Clear IdleIRq (indicating wake-up)
        // LoAlertIRq and HiAlertIRq might be set here depending on specific conditions
        // For simplicity, we can set them if the library expects them for a successful power up.
        // As per MFRC522.cpp, PCD_SoftPowerUp expects PowerDown bit to be cleared.
        // It does not explicitly check HiAlertIRq/LoAlertIRq.
      } else {
        chip->registers[0x01] = val; // Default: just store the value
      }
      break;
  }
}

static void handle_spi_write_command(chip_state_t *chip, uint8_t val) {
  uint8_t reg = chip->current_address;

  if (reg == 0x09) {
    write_fifo_register(chip, val);
  } else if (reg == 0x0A) {
    // FIFOLevelReg — возможен флаг FlushBuffer (бит 7)
    if (val & 0x80) {
//       printf("FIFO flush requested (write 0x%02X to FIFOLevelReg). Clearing FIFO (was %d bytes)\n", val, chip->fifo_len);
      chip->fifo_len = 0;
      update_fifo_level_register(chip);
    }
    chip->registers[reg] = val & 0x7F; // сохраняем без бита FlushBuffer
  } else if (reg == 0x01) {
    write_command_register(chip, val);
  }
  else if (reg == 0x04) {
    // Специальная обработка ComIrqReg
    {
//       printf("Direct write to ComIrqReg: 0x%02X (before: 0x%02X)\n", val, chip->registers[reg]);
      // If the library attempts to clear flags by writing 0x7F, clear all flags in our emulator.
      if (val == 0x7F) {
          chip->registers[reg] = 0x00; // Correctly clear all IRQ flags
          // printf("ComIrqReg cleared to 0x00 after 0x7F write.\n");
  } else {
          // For other values, just update the register. Specific flags will be set/cleared by other handlers.
          chip->registers[reg] = val;
//           printf("ComIrqReg after write: 0x%02X\n", chip->registers[reg]);
      }
      return; // Заменено break; на return;
    }
  } else if (reg == 0x08) { // Status2Reg
//     printf("Write to Status2Reg: 0x%02X\n", val);
    // If MFCrypto1On (bit 3) is being cleared, we should exit authenticated state.
    if ((chip->registers[reg] & 0x08) && !(val & 0x08)) {
//         printf("Exiting authenticated state.\n");
        chip->authenticated = false;
    }
    chip->registers[reg] = val;
  } else if (reg == 0x36) { // AutoTestReg
//     printf("Write to AutoTestReg: 0x%02X\n", val);
    chip->registers[reg] = val;
  }
  else {
    chip->registers[reg] = val;
  }
  chip->spi_buffer[0] = 0;
}

// FIFO management functions
static void fifo_push(chip_state_t *chip, uint8_t val) {
  if (chip->fifo_len < FIFO_SIZE) {
    chip->fifo[chip->fifo_len] = val;
    chip->fifo_len++;
    update_fifo_level_register(chip);
  }
}

static void fifo_remove_bytes(chip_state_t *chip, int bytes_to_remove) {
  if (bytes_to_remove > 0 && bytes_to_remove <= chip->fifo_len) {
//     printf("FIFO REMOVE: before memmove, len=%d, content: ", chip->fifo_len);
//     for(int i = 0; i < chip->fifo_len; ++i) printf("%02X ", chip->fifo[i]);
//     printf("\n");

    memmove(chip->fifo, chip->fifo + bytes_to_remove,
            (chip->fifo_len - bytes_to_remove) * sizeof(chip->fifo[0]));
    memset(chip->fifo + (chip->fifo_len - bytes_to_remove), 0, bytes_to_remove); // Explicitly zero out removed part

    chip->fifo_len -= bytes_to_remove;
    update_fifo_level_register(chip);
   
    // MFRC522 сбрасывает RxIRq, когда FIFO пуст после удаления байтов
    if (chip->fifo_len == 0) {
        clear_irq_flag(chip, 0x20); // Сбросить RxIRq (0x20)
//         printf("RxIRq (0x20) cleared because FIFO is empty after remove.\n");
    }
//     printf("FIFO REMOVE: after memmove and zeroing, len=%d, content: ", chip->fifo_len);
//     for(int i = 0; i < chip->fifo_len; ++i) printf("%02X ", chip->fifo[i]);
//     printf("\n");
  }
}

static void update_fifo_level_register(chip_state_t *chip) {
  chip->registers[0x0A] = chip->fifo_len;
}

// State management functions
static void reset_chip_state(chip_state_t *chip) {
  chip->authenticated = false;
  chip->card_selected = false;
  chip->anticoll_step = 0;
  chip->uid_read_completed = false;
  chip->cascade_level = 1;
  chip->current_level_known_bits = 0;
  chip->select_completed = false;
  chip->select_response_sent = 0;
  chip->registers[0x04] = 0;  // Сбрасываем все флаги IRQ
//   printf("Chip state reset - ComIrqReg cleared to 0x00\n");
}

static void set_irq_flag(chip_state_t *chip) {
//   printf("set_irq_flag called - before: ComIrqReg: 0x%02X\n", chip->registers[0x04]);
  chip->registers[0x04] |= (0x10 | 0x20);  // IdleIRq (0x10) + RxIRq (0x20)
  chip->registers[0x0D] &= ~0x80;
//   printf("set_irq_flag called - after: ComIrqReg: 0x%02X\n", chip->registers[0x04]);
}

static void clear_irq_flag(chip_state_t *chip, uint8_t flag) {
  chip->registers[0x04] &= ~flag;
//   printf("IRQ flag 0x%02X cleared - ComIrqReg: 0x%02X\n", flag, chip->registers[0x04]);
}

static void set_specific_irq_flag(chip_state_t *chip, uint8_t flag) {
//   printf("Before setting flag 0x%02X - ComIrqReg: 0x%02X\n", flag, chip->registers[0x04]);
  chip->registers[0x04] |= flag;
//   printf("After setting flag 0x%02X - ComIrqReg: 0x%02X\n", flag, chip->registers[0x04]);
}

static void log_chip_state(chip_state_t *chip) {
//   printf("=== CHIP STATE ===\n");
//   printf("ComIrqReg: 0x%02X, FIFOLevel: %d, ControlReg: 0x%02X\n",
//          chip->registers[0x04], chip->registers[0x0A], chip->registers[0x0C]);
//   printf("FIFO content: ");
//   for (int i = 0; i < chip->fifo_len && i < 8; i++) {
//     printf("%02X ", chip->fifo[i]);
//   }
//   printf("(len=%d)\n", chip->fifo_len);
//   printf("anticoll_step: %d, card_selected: %s, uid_read_completed: %s, select_completed: %s\n",
//          chip->anticoll_step, chip->card_selected ? "true" : "false",
//          chip->uid_read_completed ? "true" : "false", chip->select_completed ? "true" : "false");
//   printf("====================\n");
}

void send_ack_response(chip_state_t *chip) {
    chip->fifo_len = 0;
    chip->fifo[0] = 0x0A;
    chip->fifo_len = 1;
    update_fifo_level_register(chip);
    set_specific_irq_flag(chip, 0x20); // RxIRq
    chip->registers[0x0C] = (chip->registers[0x0C] & ~0x07) | 0x04; // Set RxLastBits to 4
//    printf("Sent ACK (0x0A). FIFO len: %d\n", chip->fifo_len);
}

// Global chip state
static chip_state_t g_chip_state;

void chip_pin_change(void *user_data, pin_t pin, uint32_t value);
void chip_spi_done(void *user_data, uint8_t *buffer, uint32_t count);



Library Manager



沒有留言:

張貼留言

WOKWI 模擬 MFRC522 RFID Reader + 5個 Tag 發行到MQTT 上

WOKWI 模擬 MFRC522 RFID Reader + 5個 Tag  發行到MQTT 上 MQTTgo.io # MQTT 設定 MQTT_BROKER = "mqttgo.io" MQTT_PORT = 1883 MQTT_TOPIC = ...