### Unit Initialization and Setup Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Initializes the M5Unit-RFID library by adding the UnitRFID2 to the UnitUnified manager and configuring the I2C communication. ```APIDOC ## Unit Initialization and Setup `UnitWS1850S` (aliased as `m5::unit::UnitRFID2`) is the primary driver for Unit RFID2. It must be added to a `UnitUnified` manager and initialized before use. Wire must be configured for Port A (QWIIC) at 400 kHz; the GROVE port (Port B / SoftwareI2C) is explicitly unsupported due to timing constraints. ```cpp #define USING_UNIT_RFID2 #include #include #include // Includes UnitWS1850S and UnitMFRC522 #include m5::unit::UnitUnified Units; m5::unit::UnitRFID2 unit{}; // UnitWS1850S, I2C address 0x28 void setup() { M5.begin(); // For most boards: use Port A (QWIIC) pins at 400 kHz auto sda = M5.getPin(m5::pin_name_t::port_a_sda); auto scl = M5.getPin(m5::pin_name_t::port_a_scl); Wire.end(); Wire.begin(sda, scl, 400 * 1000U); if (!Units.add(unit, Wire) || !Units.begin()) { // Failed – halt while (true) { m5::utility::delay(10000); } } // Unit is ready; NFCLayerA can now be constructed over it } void loop() { M5.update(); Units.update(); // Must be called every loop iteration } ``` ``` -------------------------------- ### Generate Doxygen Documentation Source: https://github.com/m5stack/m5unit-rfid/blob/main/README.md Execute the `docs/doxy.sh` script to generate Doxygen documentation locally. Ensure Doxygen, pcregrep, and Git are installed if you wish to include commit hashes in the HTML output. ```bash bash docs/doxy.sh ``` -------------------------------- ### Select RFID2 Unit for Arduino IDE Source: https://github.com/m5stack/m5unit-rfid/blob/main/README.md Uncomment the `#define USING_UNIT_RFID2` directive to specify the RFID2 unit for use with the M5Unit-RFID library in Arduino IDE. This is necessary because examples are shared with M5Unit-NFC. ```cpp // For UnitNFC // #define USING_UNIT_NFC // For CapNFC // #define USING_HACKER_CAP // For UnitRFID2 // #define USING_UNIT_RFID2 ``` ```cpp // #define USING_UNIT_NFC // #define USING_HACKER_CAP #define USING_UNIT_RFID2 ``` -------------------------------- ### Initialize Unit RFID2 with M5Unified Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Initializes the UnitRFID2 module using M5Unified framework. Ensure Wire is configured for Port A (QWIIC) at 400 kHz. The Units.update() function must be called in the loop. ```cpp #define USING_UNIT_RFID2 #include #include #include // Includes UnitWS1850S and UnitMFRC522 #include m5::unit::UnitUnified Units; m5::unit::UnitRFID2 unit {}; // UnitWS1850S, I2C address 0x28 void setup() { M5.begin(); // For most boards: use Port A (QWIIC) pins at 400 kHz auto sda = M5.getPin(m5::pin_name_t::port_a_sda); auto scl = M5.getPin(m5::pin_name_t::port_a_scl); Wire.end(); Wire.begin(sda, scl, 400 * 1000U); if (!Units.add(unit, Wire) || !Units.begin()) { // Failed – halt while (true) { m5::utility::delay(10000); } } // Unit is ready; NFCLayerA can now be constructed over it } void loop() { M5.update(); Units.update(); // Must be called every loop iteration } ``` -------------------------------- ### Configure UnitRFID2 Initialization Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Customize UnitRFID2 initialization parameters using config_t before calling Units.begin(). Options include antenna enable, receiver gain, software CRC usage, and mode register value. ```cpp m5::unit::UnitRFID2 unit {}; void setup() { // Customize before Units.begin() auto cfg = unit.config(); cfg.enable_antenna = true; cfg.receiver_gain = m5::unit::mfrc522::ReceiverGain::dB48; // Maximum gain cfg.software_crc = false; // Use hardware CRC coprocessor cfg.mode_reg = 0x3D; // Default ModeReg value (CRC preset 0x6363) unit.config(cfg); // ... Wire setup and Units.add/begin ... } ``` -------------------------------- ### Low-Level PICC Selection with `request`, `selectWithAnticollision`, `hlt` Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Directly access the ISO14443A state machine for REQA/WUPA, anti-collision, targeted SELECT, and HLTA. This is useful for custom workflows bypassing higher-level abstractions. Ensure to call `hlt()` to halt the PICC after selection. ```cpp using namespace m5::nfc::a; m5::unit::UnitRFID2 unit {}; void manualSelect() { uint16_t atqa {}; // REQA: detect any idle PICC in field if (!unit.request(atqa)) { M5_LOGW("No PICC (or collision)"); return; } M5_LOGI("ATQA: %04X", atqa); PICC picc {}; bool completed{false}; uint8_t lv = 1; // Cascade levels: 1 (4-byte UID), 2 (7-byte UID), 3 (10-byte UID) while (!completed && lv <= 3) { if (!unit.selectWithAnticollision(completed, picc, lv++)) { M5_LOGE("Anti-collision failed at level %u", lv - 1); unit.hlt(); return; } } if (completed) { M5_LOGI("Selected: UID=%s SAK=%02X", picc.uidAsString().c_str(), picc.sak); } unit.hlt(); // Send HLTA to halt the PICC } ``` -------------------------------- ### NFC-A PICC Detection and Identification Source: https://context7.com/m5stack/m5unit-rfid/llms.txt `m5::nfc::NFCLayerA` wraps the unit driver and provides the primary high-level API. `detect()` sends a REQA/WUPA and populates a `PICC` struct with UID, ATQA, and SAK. `identify()` refines the preliminary type classification into the exact PICC type and memory layout. ```APIDOC ## NFC-A PICC Detection and Identification via `NFCLayerA` ### Description `m5::nfc::NFCLayerA` provides high-level NFC-A operations. `detect()` finds PICCs and populates their basic information, while `identify()` determines the specific card type and memory details. ### Methods - `detect(std::vector& piccs)`: Detects all PICCs in the field and populates the provided vector with their basic information. - `identify(PICC& picc)`: Refines the identification of a detected PICC to determine its exact type and memory layout. - `deactivate()`: Sends an HLTA command and stops RF field activity. ### Parameters #### `detect` - **piccs** (std::vector&): Output parameter, a vector to be populated with detected PICC information. #### `identify` - **picc** (PICC&): The PICC object to identify. ### Example ```cpp #include using namespace m5::nfc::a; m5::unit::UnitRFID2 unit; m5::nfc::NFCLayerA nfc_a{unit}; void loop() { M5.update(); Units.update(); std::vector piccs; if (nfc_a.detect(piccs)) { for (auto& picc : piccs) { if (nfc_a.identify(picc)) { M5.Log.printf("UID: %s Type: %s User area: %u / Total: %u bytes\n", picc.uidAsString().c_str(), picc.typeAsString().c_str(), picc.userAreaSize(), picc.totalSize()); } } nfc_a.deactivate(); } } ``` ``` -------------------------------- ### NFC-A PICC Detection and Identification Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Uses NFCLayerA to detect NFC PICCs in the field, retrieve their UID, ATQA, and SAK, and identify the exact chip type and memory layout. Requires M5UnitUnifiedNFC.h and proper unit initialization. ```cpp #include using namespace m5::nfc::a; m5::unit::UnitRFID2 unit{}; m5::nfc::NFCLayerA nfc_a{unit}; void loop() { M5.update(); Units.update(); std::vector piccs; // detect() performs REQA + anti-collision for all PICCs in field if (nfc_a.detect(piccs)) { for (auto& picc : piccs) { // identify() determines exact chip type (e.g., NTAG_213, MIFARE_Classic_1K) if (nfc_a.identify(picc)) { M5.Log.printf("UID: %s Type: %s User area: %u / Total: %u bytes\n", picc.uidAsString().c_str(), picc.typeAsString().c_str(), picc.userAreaSize(), picc.totalSize()); // e.g., "UID: 04A3B2C1 Type: MIFARE_Classic_1K User area: 752 / Total: 1024 bytes" } } nfc_a.deactivate(); // Send HLTA and stop RF field activity } } ``` -------------------------------- ### Authenticate MIFARE Classic Sector and Read Block Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Authenticates a MIFARE Classic sector using Key A before reading a block. Ensure the sector is properly authenticated before attempting to read. `mifareClassicStopCrypto1()` is used to exit the encrypted session. ```cpp using namespace m5::nfc::a; using namespace m5::nfc::a::mifare::classic; m5::unit::UnitRFID2 unit{}; m5::nfc::NFCLayerA nfc_a{unit}; void authenticateAndRead() { PICC picc{}; if (!nfc_a.detect(picc) || !nfc_a.identify(picc) || !nfc_a.reactivate(picc)) return; if (!picc.isMifareClassic()) { nfc_a.deactivate(); return; } constexpr uint8_t block = 4; constexpr Key keyA = DEFAULT_KEY; // {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF} // Authenticate sector containing block 4 with Key A if (!unit.mifareClassicAuthenticateA(picc, block, keyA)) { M5_LOGE("Auth A failed"); nfc_a.deactivate(); return; } uint8_t buf[16]{}; if (unit.readBlock(buf, block)) { m5::utility::log::dump(buf, 16, false); // Hex dump of 16 bytes } unit.mifareClassicStopCrypto1(); // Exit crypto state nfc_a.deactivate(); } ``` -------------------------------- ### UnitMFRC522 / UnitWS1850S — `config_t` Configuration Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Configures the RFID unit's behavior before initialization, including antenna enable, receiver gain, software CRC usage, and mode register settings. ```APIDOC ## UnitMFRC522 / UnitWS1850S — `config_t` Configuration `config_t` controls initialization behavior: mode register value, antenna enable, receiver gain, and whether to use software CRC. Set before calling `begin()` via `unit.config(cfg)`. ```cpp m5::unit::UnitRFID2 unit{}; void setup() { // Customize before Units.begin() auto cfg = unit.config(); cfg.enable_antenna = true; cfg.receiver_gain = m5::unit::mfrc522::ReceiverGain::dB48; // Maximum gain cfg.software_crc = false; // Use hardware CRC coprocessor cfg.mode_reg = 0x3D; // Default ModeReg value (CRC preset 0x6363) unit.config(cfg); // ... Wire setup and Units.add/begin ... } ``` ``` -------------------------------- ### Raw NFC-A Transceive with `nfcaTransceive` Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Sends an arbitrary ISO14443A frame and receives a response. Use this for custom or proprietary card protocols not covered by higher-level APIs. Ensure the hardware CRC is appended by the library. ```cpp m5::unit::UnitRFID2 unit {}; void rawTransceiveExample() { // Send RATS (Request for Answer to Select) for ISO-DEP activation // RATS: E0 [param] — param = 0x50 (FSDI=5, CID=0) const uint8_t rats_cmd[] = {0xE0, 0x50}; uint8_t rx_buf[32]{}; uint16_t rx_len = sizeof(rx_buf); if (unit.nfcaTransceive(rx_buf, rx_len, rats_cmd, sizeof(rats_cmd), 30 /*timeout ms*/)) { M5_LOGI("ATS received (%u bytes):", rx_len); m5::utility::log::dump(rx_buf, rx_len, false); // First byte is TL (length), followed by ATS data } else { M5_LOGE("RATS transceive failed"); } } ``` -------------------------------- ### Page Read/Write for Ultralight/NTAG Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Operations for MIFARE Ultralight and NTAG cards that use 4-byte pages. `read4` reads one page, and `write4` writes one page. ```APIDOC ## Page Read/Write for Ultralight/NTAG — `read4`, `write4` ### Description MIFARE Ultralight and NTAG cards use 4-byte pages. `read4` reads one 4-byte page; `write4` writes one 4-byte page. Ultralight reads always return 4 pages (16 bytes) aligned to a 4-page boundary. ### Methods - `read4(uint8_t* buffer, uint8_t page_address)`: Reads 4 bytes from the specified page. - `write4(uint8_t page_address, const uint8_t* data, size_t length)`: Writes up to 4 bytes to the specified page. ### Parameters #### `read4` - **buffer** (uint8_t*): Output buffer to store the read data (4 bytes). - **page_address** (uint8_t): The address of the 4-byte page to read. #### `write4` - **page_address** (uint8_t): The address of the 4-byte page to write. - **data** (const uint8_t*): Pointer to the data buffer to write. - **length** (size_t): The number of bytes to write (max 4). ### Example ```cpp m5::nfc::NFCLayerA nfc_a{unit}; void readWriteNTAGPage() { PICC picc; if (!nfc_a.detect(picc) || !nfc_a.identify(picc) || !nfc_a.reactivate(picc)) return; if (!picc.supportsNFC()) { nfc_a.deactivate(); return; } constexpr uint8_t page = 10; const char data[] = "M5"; if (nfc_a.write4(page, (const uint8_t*)data, sizeof(data))) { M5_LOGI("Page write OK"); } uint8_t rbuf[4]{}; if (nfc_a.read4(rbuf, page)) { M5_LOGI("Page read: %c%c", rbuf[0], rbuf[1]); } nfc_a.deactivate(); } ``` ``` -------------------------------- ### Low-Level PICC Selection Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Provides direct access to the ISO14443A state machine, including REQA/WUPA, anti-collision loop across cascade levels, targeted SELECT, and HLTA. This is used when bypassing the higher-level `NFCLayerA` for custom workflows. ```APIDOC ## Low-Level PICC Selection — `request`, `wakeup`, `selectWithAnticollision`, `select`, `hlt` Direct access to the ISO14443A state machine: REQA/WUPA, anti-collision loop across cascade levels, targeted SELECT, and HLTA. Used when bypassing `NFCLayerA` for custom workflows. ```cpp using namespace m5::nfc::a; m5::unit::UnitRFID2 unit {}; void manualSelect() { uint16_t atqa {}; // REQA: detect any idle PICC in field if (!unit.request(atqa)) { M5_LOGW("No PICC (or collision)"); return; } M5_LOGI("ATQA: %04X", atqa); PICC picc {}; bool completed {false}; uint8_t lv = 1; // Cascade levels: 1 (4-byte UID), 2 (7-byte UID), 3 (10-byte UID) while (!completed && lv <= 3) { if (!unit.selectWithAnticollision(completed, picc, lv++)) { M5_LOGE("Anti-collision failed at level %u", lv - 1); unit.hlt(); return; } } if (completed) { M5_LOGI("Selected: UID=%s SAK=%02X", picc.uidAsString().c_str(), picc.sak); } unit.hlt(); // Send HLTA to halt the PICC } ``` ``` -------------------------------- ### Dump All Accessible Card Memory Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Dumps all accessible blocks or pages of an activated PICC to the log. For MIFARE Classic, Key A is required for sector authentication. For other card types, the key parameter is ignored. ```cpp using namespace m5::nfc::a::mifare::classic; m5::nfc::NFCLayerA nfc_a{unit}; constexpr Key keyA = DEFAULT_KEY; void dumpCard() { PICC picc{}; if (!nfc_a.detect(picc) || !nfc_a.identify(picc) || !nfc_a.reactivate(picc)) return; M5.Log.printf("Dumping %s (UID: %s) %u bytes\n", picc.typeAsString().c_str(), picc.uidAsString().c_str(), picc.totalSize()); // For MIFARE Classic, keyA is used to authenticate each sector // For Ultralight/NTAG/DESFire, the key parameter is ignored nfc_a.dump(keyA); nfc_a.deactivate(); } ``` -------------------------------- ### Ultralight/NTAG Page Read/Write Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Reads and writes individual 4-byte pages on MIFARE Ultralight and NTAG cards. Ultralight reads return 16 bytes aligned to a 4-page boundary. Ensure the PICC supports NFC. ```cpp m5::nfc::NFCLayerA nfc_a{unit}; void readWriteNTAGPage() { PICC picc{}; if (!nfc_a.detect(picc) || !nfc_a.identify(picc) || !nfc_a.reactivate(picc)) return; if (!picc.supportsNFC()) { nfc_a.deactivate(); return; } constexpr uint8_t page = 10; // User page (NTAG213 user area starts at page 4) const char data[] = "M5"; // Max 4 bytes per page write if (nfc_a.write4(page, (const uint8_t*)data, sizeof(data))) { M5_LOGI("Page write OK"); } uint8_t rbuf[4]{}; if (nfc_a.read4(rbuf, page)) { M5_LOGI("Page read: %c%c", rbuf[0], rbuf[1]); // Expected: "M5" } nfc_a.deactivate(); } ``` -------------------------------- ### Antenna Control — `readAntennaStatus`, `turnOnAntenna`, `turnOffAntenna` Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Provides functions to check the current status of the RF antenna and to explicitly turn it on or off, useful for power management between scans. ```APIDOC ## Antenna Control — `readAntennaStatus`, `turnOnAntenna`, `turnOffAntenna` Reads or changes the RF antenna state by controlling the TX1RFEn and TX2RFEn bits of the TxControlReg register. ```cpp m5::unit::UnitRFID2 unit{}; void manageAntenna() { bool on{}; if (unit.readAntennaStatus(on)) { M5_LOGI("Antenna is %s", on ? "ON" : "OFF"); } // Power-save: turn off antenna between scans unit.turnOffAntenna(); m5::utility::delay(100); unit.turnOnAntenna(); // Re-enable before next detection attempt } ``` ``` -------------------------------- ### Run RFID Chip Self-Test Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Executes the MFRC522 built-in self-test to verify chip firmware. This operation blocks until completion and re-initializes the unit afterward. ```cpp m5::unit::UnitRFID2 unit {}; void verifyHardware() { if (unit.selfTest()) { M5_LOGI("Self-test PASSED – chip firmware verified"); } else { M5_LOGE("Self-test FAILED – unknown or faulty chip"); } } ``` -------------------------------- ### MIFARE Classic Block Read/Write Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Performs read and write operations on 16-byte blocks of MIFARE Classic cards. Requires prior authentication, which is handled internally by `write16` and `read16`. Operates on the user area only. ```cpp using namespace m5::nfc::a; using namespace m5::nfc::a::mifare::classic; m5::nfc::NFCLayerA nfc_a{unit}; constexpr Key keyA = DEFAULT_KEY; // 0xFFFFFFFFFFFF void readWriteClassicBlock() { PICC picc{}; if (!nfc_a.detect(picc) || !nfc_a.identify(picc) || !nfc_a.reactivate(picc)) return; if (!picc.isMifareClassic()) { nfc_a.deactivate(); return; } constexpr uint8_t target_block = 4; // First data block in sector 1 const char msg[] = "M5Unit-RFID"; // Write 16 bytes to block 4 (auth is handled internally) if (nfc_a.write16(target_block, (const uint8_t*)msg, sizeof(msg))) { M5_LOGI("Write OK"); } // Read back 16 bytes uint8_t rbuf[16]{}; if (nfc_a.read16(rbuf, target_block)) { M5_LOGI("Read: %s", (char*)rbuf); // Expected: "M5Unit-RFID" } nfc_a.deactivate(); } ``` -------------------------------- ### `softReset` — Software Reset Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Performs a software reset of the RFID chip. It can be blocking or non-blocking, allowing for control over when the operation completes. ```APIDOC ## `softReset` — Software Reset Resets the MFRC522/WS1850S chip and optionally blocks until startup completes (~37.5 ms). Returns `true` on success. ```cpp m5::unit::UnitRFID2 unit{}; void resetAndReinit() { // Non-blocking reset if (!unit.softReset(false)) { M5_LOGE("Reset command failed"); return; } m5::utility::delay(50); // Manual wait after non-blocking reset // Blocking reset (waits for power-down flag to clear, up to 250 ms) if (!unit.softReset(true)) { M5_LOGE("Blocking reset failed"); } } ``` ``` -------------------------------- ### `selfTest` — Firmware Self-Test Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Executes the MFRC522's built-in self-test to verify firmware integrity against known reference bytes. This operation blocks until completion and re-initializes the unit. ```APIDOC ## `selfTest` — Firmware Self-Test Runs the MFRC522 built-in self-test (section 16.1.1) against known firmware reference bytes for versions 0.0/1.0/2.0 and the Fudan FM17522 clone. Blocks until complete and re-initializes the unit afterward via `begin()`. ```cpp m5::unit::UnitRFID2 unit{}; void verifyHardware() { if (unit.selfTest()) { M5_LOGI("Self-test PASSED – chip firmware verified"); } else { M5_LOGE("Self-test FAILED – unknown or faulty chip"); } } ``` ``` -------------------------------- ### Block Read/Write for MIFARE Classic Source: https://context7.com/m5stack/m5unit-rfid/llms.txt High-level read/write operations for MIFARE Classic cards. `read16`/`write16` operate on any 16-byte block, requiring prior authentication for MIFARE Classic blocks. ```APIDOC ## Block Read/Write via `NFCLayerA` — `read`, `write`, `read16`, `write16` ### Description High-level read/write operates on the user area only (safety mode). `read16`/`write16` operate on any 16-byte block (MIFARE Classic sector block). MIFARE Classic blocks require prior authentication. ### Methods - `read16(uint8_t* buffer, uint8_t block_address)`: Reads 16 bytes from the specified block. - `write16(uint8_t block_address, const uint8_t* data, size_t length)`: Writes up to 16 bytes to the specified block. ### Parameters #### `read16` - **buffer** (uint8_t*): Output buffer to store the read data. - **block_address** (uint8_t): The address of the 16-byte block to read. #### `write16` - **block_address** (uint8_t): The address of the 16-byte block to write. - **data** (const uint8_t*): Pointer to the data buffer to write. - **length** (size_t): The number of bytes to write (max 16). ### Example ```cpp using namespace m5::nfc::a; using namespace m5::nfc::a::mifare::classic; m5::nfc::NFCLayerA nfc_a{unit}; constexpr Key keyA = DEFAULT_KEY; void readWriteClassicBlock() { PICC picc; if (!nfc_a.detect(picc) || !nfc_a.identify(picc) || !nfc_a.reactivate(picc)) return; if (!picc.isMifareClassic()) { nfc_a.deactivate(); return; } constexpr uint8_t target_block = 4; const char msg[] = "M5Unit-RFID"; if (nfc_a.write16(target_block, (const uint8_t*)msg, sizeof(msg))) { M5_LOGI("Write OK"); } uint8_t rbuf[16]{}; if (nfc_a.read16(rbuf, target_block)) { M5_LOGI("Read: %s", (char*)rbuf); } nfc_a.deactivate(); } ``` ``` -------------------------------- ### Card Memory Dump Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Dumps all accessible blocks or pages of an activated PICC (NFC tag) to the log output. For MIFARE Classic, a Key A must be provided for sector authentication. ```APIDOC ## NFCLayerA::dump ### Description Dumps the entire accessible memory of an NFC tag (PICC) to the console log. For MIFARE Classic tags, authentication is required for each sector. ### Method - `dump(const Key& keyA)` ### Parameters - **keyA** (Key) - The Key A used to authenticate sectors on MIFARE Classic tags. This parameter is ignored for other tag types like Ultralight or NTAG. ### Endpoint N/A (This is an SDK method) ### Request Example ```cpp // Assuming nfc_a is an initialized NFCLayerA object and a PICC is active nfc_a.dump(DEFAULT_KEY); // Use default Key A for MIFARE Classic ``` ### Response - **void**: The function outputs memory dump information directly to the log. ### Related Functions - `m5::nfc::a::mifare::classic::mifareClassicAuthenticateA`: For authenticating MIFARE Classic sectors. ``` -------------------------------- ### Raw NFC-A Transceive Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Sends an arbitrary ISO14443A frame and receives the response. This is useful for implementing custom or proprietary card protocols not covered by higher-level APIs. The hardware CRC is automatically appended. ```APIDOC ## Raw NFC-A Transceive — `nfcaTransceive` Sends an arbitrary ISO14443A frame (with hardware CRC appended) and receives the response. Used to implement custom or proprietary card protocols not covered by the higher-level APIs. ```cpp m5::unit::UnitRFID2 unit {}; void rawTransceiveExample() { // Send RATS (Request for Answer to Select) for ISO-DEP activation // RATS: E0 [param] — param = 0x50 (FSDI=5, CID=0) const uint8_t rats_cmd[] = {0xE0, 0x50}; uint8_t rx_buf[32]{}; uint16_t rx_len = sizeof(rx_buf); if (unit.nfcaTransceive(rx_buf, rx_len, rats_cmd, sizeof(rats_cmd), 30 /*timeout ms*/)) { M5_LOGI("ATS received (%u bytes):", rx_len); m5::utility::log::dump(rx_buf, rx_len, false); // First byte is TL (length), followed by ATS data } else { M5_LOGE("RATS transceive failed"); } } ``` ``` -------------------------------- ### MIFARE Classic Authentication Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Authenticates a MIFARE Classic sector using Key A or Key B before performing read/write operations. `mifareClassicStopCrypto1()` is used to end the encrypted session. ```APIDOC ## mifareClassicAuthenticateA, mifareClassicAuthenticateB ### Description Authenticates a MIFARE Classic sector with Key A or Key B. This is a prerequisite for reading or writing any block within that sector. ### Method - `mifareClassicAuthenticateA(const PICC& picc, uint8_t block, const Key& key)` - `mifareClassicAuthenticateB(const PICC& picc, uint8_t block, const Key& key)` ### Parameters - **picc** (PICC) - The detected and identified PICC (tag). - **block** (uint8_t) - The block number within the sector to authenticate. - **key** (Key) - The authentication key (Key A or Key B). ### Endpoint N/A (This is an SDK method) ### Request Example ```cpp // Authenticate sector containing block 4 with Key A if (!unit.mifareClassicAuthenticateA(picc, block, keyA)) { M5_LOGE("Auth A failed"); // Handle authentication failure } ``` ### Response - **bool**: Returns `true` if authentication is successful, `false` otherwise. ### Related Functions - `mifareClassicStopCrypto1()`: Ends the encrypted session. ``` -------------------------------- ### NDEF Read and Write Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Handles reading and writing NDEF (NFC Data Exchange Format) messages on Type 2 and Type 4 tags. Supports URI, Text (multilingual), and MIME records. ```APIDOC ## NDEF Read and Write — `ndefRead`, `ndefWrite`, `ndefIsValidFormat` ### Description Provides functionality to read and write NDEF messages on compatible NFC tags (Type 2 and Type 4). It supports various NDEF record types, including URIs, multilingual text, and MIME types. ### Methods - `ndefWrite(const TLV& message)` - `ndefRead(TLV& message)` - `ndefIsValidFormat(bool& valid)` ### Parameters - **message** (TLV): For `ndefWrite`, this is the NDEF message to write. For `ndefRead`, this TLV object will be populated with the read message. - **valid** (bool&): Output parameter for `ndefIsValidFormat`, indicating if the tag is NDEF formatted. ### Endpoint N/A (This is an SDK method) ### Request Example ```cpp // Writing an NDEF message with URI and Text records TLV msg{Tag::Message}; Record r[3]{}; // ... populate records ... msg.push_back(r[0]); // ... if (nfc_a.ndefWrite(msg)) { M5_LOGI("NDEF write OK"); } else { M5_LOGE("NDEF write failed"); } // Reading an NDEF message TLV read_msg; if (nfc_a.ndefRead(read_msg) && read_msg.isMessageTLV()) { // Process records in read_msg } // Checking NDEF format validity bool valid; nfc_a.ndefIsValidFormat(valid); if (!valid) { M5_LOGW("Not NDEF formatted"); } ``` ### Response - **ndefWrite**: Returns `true` on successful write, `false` otherwise. - **ndefRead**: Returns `true` if a message was successfully read, `false` otherwise. - **ndefIsValidFormat**: Sets the `valid` parameter to `true` if the tag is NDEF formatted, `false` otherwise. ### Related Functions - `mifareUltralightChangeFormatToNDEF()`: Converts Ultralight tags to NDEF format. ``` -------------------------------- ### Calculate CRC Checksum Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Computes a 16-bit CRC-16/ISO14443A checksum using hardware or software. Useful for validating raw frame data. Ensure the unit is initialized. ```cpp m5::unit::UnitRFID2 unit{}; void demonstrateCRC() { const uint8_t data[] = {0x60, 0x07}; // AUTH KeyA, block 7 uint16_t hw_crc{}, sw_crc{}; if (unit.calculateCRC(hw_crc, data, sizeof(data))) { M5_LOGI("HW CRC: %04X", hw_crc); // Expected: varies by mode reg CRC preset } if (unit.calculateSoftwareCRC(sw_crc, data, sizeof(data))) { M5_LOGI("SW CRC: %04X", sw_crc); } M5_LOGI("CRC match: %s", hw_crc == sw_crc ? "YES" : "NO"); } ``` -------------------------------- ### Receiver Gain Configuration Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Adjusts the RF receiver's signal voltage gain factor (18–48 dB) to trade off read distance vs. noise rejection. `readReceiverGain` retrieves the current gain, and `writeReceiverGain` sets a new gain value. ```APIDOC ## Receiver Gain — `readReceiverGain`, `writeReceiverGain` ### Description Adjusts the RF receiver's signal voltage gain factor (18–48 dB) to trade off read distance vs. noise rejection. ### Methods - `readReceiverGain(ReceiverGain& gain)`: Reads the current receiver gain. - `writeReceiverGain(ReceiverGain gain)`: Writes a new receiver gain value. ### Parameters #### `readReceiverGain` - **gain** (ReceiverGain&): Output parameter to store the current receiver gain. #### `writeReceiverGain` - **gain** (ReceiverGain): The desired receiver gain value (e.g., `ReceiverGain::dB48`, `ReceiverGain::dB18`). ### Example ```cpp m5::unit::UnitRFID2 unit{}; void configureGain() { m5::unit::mfrc522::ReceiverGain gain; if (unit.readReceiverGain(gain)) { M5_LOGI("Current gain index: %u", m5::stl::to_underlying(gain)); } // Set to maximum sensitivity for longer read range if (!unit.writeReceiverGain(m5::unit::mfrc522::ReceiverGain::dB48)) { M5_LOGE("Failed to set gain"); } // Set to lower gain (18 dB) in noisy RF environments unit.writeReceiverGain(m5::unit::mfrc522::ReceiverGain::dB18); } ``` ``` -------------------------------- ### MIFARE Classic Value Block Operations Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Performs increment, decrement, or transfer operations on MIFARE Classic value blocks, which store signed 32-bit integers. Prior sector authentication is required. ```APIDOC ## mifareClassicValueBlock ### Description Manages signed 32-bit integer values stored in MIFARE Classic value blocks. Supports increment, decrement, and transfer operations. Requires prior authentication of the target block's sector. ### Method - `mifareClassicValueBlock(Command cmd, uint8_t block, int32_t value = 0)` - `mifareClassicValueBlock(Command cmd, uint8_t block)` ### Parameters - **cmd** (Command) - The operation to perform: `INCREMENT`, `DECREMENT`, `TRANSFER`. - **block** (uint8_t) - The target value block number. - **value** (int32_t, optional) - The value to use for increment/decrement operations. Defaults to 0. ### Endpoint N/A (This is an SDK method) ### Request Example ```cpp // Increment value block 5 by 10 and transfer unit.mifareClassicValueBlock(m5::nfc::a::Command::INCREMENT, 5, 10); unit.mifareClassicValueBlock(m5::nfc::a::Command::TRANSFER, 5); // Decrement by 3 and transfer unit.mifareClassicValueBlock(m5::nfc::a::Command::DECREMENT, 5, 3); unit.mifareClassicValueBlock(m5::nfc::a::Command::TRANSFER, 5); ``` ### Response - **bool**: Returns `true` if the operation is successful, `false` otherwise. ### Related Functions - `mifareClassicAuthenticateA`/`mifareClassicAuthenticateB`: For sector authentication. ``` -------------------------------- ### Write NDEF Message to Tag Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Writes NDEF messages (URI, Text) to Type 2 or Type 4 tags. Converts Ultralight tags to NDEF format if necessary. Ensure the tag supports NDEF and has sufficient space. ```cpp using namespace m5::nfc; using namespace m5::nfc::a; using namespace m5::nfc::ndef; m5::nfc::NFCLayerA nfc_a{unit}; void writeNDEF() { PICC picc{}; if (!nfc_a.detect(picc) || !nfc_a.identify(picc) || !nfc_a.reactivate(picc)) return; if (!picc.supportsNDEF()) { nfc_a.deactivate(); return; } // Convert Ultralight to NDEF format if needed (irreversible) if (picc.isMifareUltralight()) { nfc_a.mifareUltralightChangeFormatToNDEF(); } // Build NDEF message: URI + multilingual text records TLV msg{Tag::Message}; Record r[3]{}; r[0].setURIPayload("m5stack.com/", URIProtocol::HTTPS); r[1].setTextPayload("Hello M5Stack", "en"); r[2].setTextPayload("こんにちは M5Stack", "ja"); uint32_t max_size = nfc_a.activatedPICC().userAreaSize() - 1; for (auto& rec : r) { msg.push_back(rec); if (msg.required() > max_size) { msg.pop_back(); break; } } if (nfc_a.ndefWrite(msg)) { M5_LOGI("NDEF write OK"); } else { M5_LOGE("NDEF write failed"); } nfc_a.deactivate(); } ``` -------------------------------- ### Perform Software Reset on RFID Chip Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Resets the MFRC522/WS1850S chip. Use softReset(false) for a non-blocking reset requiring a manual delay, or softReset(true) for a blocking reset that waits for completion. ```cpp m5::unit::UnitRFID2 unit {}; void resetAndReinit() { // Non-blocking reset if (!unit.softReset(false)) { M5_LOGE("Reset command failed"); return; } m5::utility::delay(50); // Manual wait after non-blocking reset // Blocking reset (waits for power-down flag to clear, up to 250 ms) if (!unit.softReset(true)) { M5_LOGE("Blocking reset failed"); } } ``` -------------------------------- ### MIFARE Classic Value Block Operations Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Performs increment, decrement, and transfer operations on a MIFARE Classic value block. Prior authentication of the sector is required. The `TRANSFER` command is used to commit changes. ```cpp using namespace m5::nfc::a; using namespace m5::nfc::a::mifare::classic; m5::unit::UnitRFID2 unit{}; void valueBlockDemo(const PICC& picc) { constexpr uint8_t block = 5; // Value block (pre-formatted) constexpr Key keyA = DEFAULT_KEY; if (!unit.mifareClassicAuthenticateA(picc, block, keyA)) return; // Increment value block by 10 unit.mifareClassicValueBlock(m5::nfc::a::Command::INCREMENT, block, 10); // Transfer result back to the same block unit.mifareClassicValueBlock(m5::nfc::a::Command::TRANSFER, block); // Decrement by 3 unit.mifareClassicValueBlock(m5::nfc::a::Command::DECREMENT, block, 3); unit.mifareClassicValueBlock(m5::nfc::a::Command::TRANSFER, block); unit.mifareClassicStopCrypto1(); } ``` -------------------------------- ### Configure Receiver Gain Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Adjusts the RF receiver's signal voltage gain. Use higher gain for longer read distance and lower gain in noisy environments. Ensure the unit is initialized before use. ```cpp m5::unit::UnitRFID2 unit{}; void configureGain() { m5::unit::mfrc522::ReceiverGain gain{}; if (unit.readReceiverGain(gain)) { M5_LOGI("Current gain index: %u", m5::stl::to_underlying(gain)); } // Set to maximum sensitivity for longer read range if (!unit.writeReceiverGain(m5::unit::mfrc522::ReceiverGain::dB48)) { M5_LOGE("Failed to set gain"); } // Set to lower gain (18 dB) in noisy RF environments unit.writeReceiverGain(m5::unit::mfrc522::ReceiverGain::dB18); } ``` -------------------------------- ### CRC Calculation Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Computes a 16-bit CRC-16/ISO14443A checksum using either the on-chip hardware CRC coprocessor or a software implementation. Useful for validating raw frame data. ```APIDOC ## CRC Calculation — `calculateCRC`, `calculateSoftwareCRC` ### Description Computes a 16-bit CRC-16/ISO14443A checksum using either the on-chip hardware CRC coprocessor or a software implementation. Useful for validating raw frame data. ### Methods - `calculateCRC(uint16_t& crc, const uint8_t* data, size_t length)`: Calculates CRC using the hardware coprocessor. - `calculateSoftwareCRC(uint16_t& crc, const uint8_t* data, size_t length)`: Calculates CRC using a software implementation. ### Parameters #### `calculateCRC` and `calculateSoftwareCRC` - **crc** (uint16_t&): Output parameter to store the calculated CRC. - **data** (const uint8_t*): Pointer to the data buffer. - **length** (size_t): The number of bytes in the data buffer. ### Example ```cpp m5::unit::UnitRFID2 unit; void demonstrateCRC() { const uint8_t data[] = {0x60, 0x07}; // AUTH KeyA, block 7 uint16_t hw_crc, sw_crc; if (unit.calculateCRC(hw_crc, data, sizeof(data))) { M5_LOGI("HW CRC: %04X", hw_crc); } if (unit.calculateSoftwareCRC(sw_crc, data, sizeof(data))) { M5_LOGI("SW CRC: %04X", sw_crc); } M5_LOGI("CRC match: %s", hw_crc == sw_crc ? "YES" : "NO"); } ``` ``` -------------------------------- ### Control RFID Antenna Power Source: https://context7.com/m5stack/m5unit-rfid/llms.txt Read the current antenna status or explicitly turn the RF antenna ON or OFF. Useful for power-saving by disabling the antenna between scans. ```cpp m5::unit::UnitRFID2 unit {}; void manageAntenna() { bool on {}; if (unit.readAntennaStatus(on)) { M5_LOGI("Antenna is %s", on ? "ON" : "OFF"); } // Power-save: turn off antenna between scans unit.turnOffAntenna(); m5::utility::delay(100); unit.turnOnAntenna(); // Re-enable before next detection attempt } ```