Vendor esp-idf-rc522 as plain source instead of submodule

The library is modified (NDEF reading, cascade anticollision) so vendoring
it directly avoids needing push access to the upstream fork.

https://claude.ai/code/session_01MhAnh68DPNnTjcKqjNYDmk
This commit is contained in:
Claude committed 2026-06-12 19:46:21 +00:00
1 parent bf9852c345
commit 4f9946bd17
7 files changed
+865 -5

No files matched your search

-4
View File
@@ -1,7 +1,3 @@
[submodule "device/components/esp-idf-rc522"]
path = device/components/esp-idf-rc522
url = https://github.com/sweidac/esp-idf-rc522.git
[submodule "device/components/arduino-esp32"] [submodule "device/components/arduino-esp32"]
path = device/components/arduino-esp32 path = device/components/arduino-esp32
url = https://github.com/espressif/arduino-esp32.git url = https://github.com/espressif/arduino-esp32.git
@@ -0,0 +1,4 @@
idf_component_register(
SRCS "rc522.c"
INCLUDE_DIRS "."
)
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Alija Bobija
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1 @@
COMPONENT_ADD_INCLUDEDIRS := .
+746
View File
@@ -0,0 +1,746 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "soc/gpio_struct.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#include "esp_log.h"
#include "rc522.h"
static const char* TAG = "ESP-RC522";
struct rc522 {
bool running;
rc522_config_t* config;
spi_device_handle_t spi;
TaskHandle_t task_handle;
bool scan_started;
uint8_t* lastSerialNumber;
};
typedef struct rc522* rc522_handle_t;
static rc522_handle_t hndl = NULL;
#define rc522_fw_version() rc522_read(0x37)
bool rc522_is_inited() {
return hndl != NULL;
}
static esp_err_t rc522_spi_init() {
if(! hndl || ! hndl->config) {
ESP_LOGE(TAG, "Fail to init SPI. Invalid handle");
return ESP_ERR_INVALID_STATE;
}
if(hndl->spi) {
ESP_LOGW(TAG, "SPI already initialized");
return ESP_ERR_INVALID_STATE;
}
spi_bus_config_t buscfg = {
.miso_io_num = hndl->config->miso_io,
.mosi_io_num = hndl->config->mosi_io,
.sclk_io_num = hndl->config->sck_io,
.quadwp_io_num = -1,
.quadhd_io_num = -1
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 5000000,
.mode = 0,
.spics_io_num = hndl->config->sda_io,
.queue_size = 7,
.flags = SPI_DEVICE_HALFDUPLEX
};
esp_err_t err = spi_bus_initialize(hndl->config->spi_host_id, &buscfg, 0);
if(err != ESP_OK) {
return err;
}
err = spi_bus_add_device(hndl->config->spi_host_id, &devcfg, &hndl->spi);
if(err != ESP_OK) {
spi_bus_free(hndl->config->spi_host_id);
hndl->spi = NULL;
}
return err;
}
static esp_err_t rc522_write_n(uint8_t addr, uint8_t n, uint8_t *data) {
uint8_t* buffer = (uint8_t*) malloc(n + 1);
buffer[0] = (addr << 1) & 0x7E;
for (uint8_t i = 1; i <= n; i++) {
buffer[i] = data[i-1];
}
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.length = 8 * (n + 1);
t.tx_buffer = buffer;
esp_err_t ret = spi_device_transmit(hndl->spi, &t);
free(buffer);
return ret;
}
static esp_err_t rc522_write(uint8_t addr, uint8_t val) {
return rc522_write_n(addr, 1, &val);
}
static uint8_t* rc522_read_n(uint8_t addr, uint8_t n) {
if (n <= 0) {
return NULL;
}
spi_transaction_t t;
memset(&t, 0, sizeof(t));
uint8_t* buffer = (uint8_t*) malloc(n);
t.flags = SPI_TRANS_USE_TXDATA;
t.length = 8;
t.tx_data[0] = ((addr << 1) & 0x7E) | 0x80;
t.rxlength = 8 * n;
t.rx_buffer = buffer;
esp_err_t ret = spi_device_transmit(hndl->spi, &t);
assert(ret == ESP_OK);
return buffer;
}
static uint8_t rc522_read(uint8_t addr) {
uint8_t* buffer = rc522_read_n(addr, 1);
uint8_t res = buffer[0];
free(buffer);
return res;
}
static esp_err_t rc522_set_bitmask(uint8_t addr, uint8_t mask) {
return rc522_write(addr, rc522_read(addr) | mask);
}
static esp_err_t rc522_clear_bitmask(uint8_t addr, uint8_t mask) {
return rc522_write(addr, rc522_read(addr) & ~mask);
}
static esp_err_t rc522_antenna_on() {
esp_err_t ret;
if(~ (rc522_read(0x14) & 0x03)) {
ret = rc522_set_bitmask(0x14, 0x03);
if(ret != ESP_OK) {
return ret;
}
}
return rc522_write(0x26, 0x60); // 43dB gain
}
static void rc522_task(void* arg);
esp_err_t rc522_init(rc522_config_t* config) {
if(! config) {
return ESP_ERR_INVALID_ARG;
}
if(hndl) {
ESP_LOGW(TAG, "Already initialized");
return ESP_ERR_INVALID_STATE;
}
if(! (hndl = calloc(1, sizeof(struct rc522)))) {
return ESP_ERR_NO_MEM;
}
if(! (hndl->config = calloc(1, sizeof(rc522_config_t)))) {
rc522_destroy();
return ESP_ERR_NO_MEM;
}
// copy config considering defaults
hndl->config->callback = config->callback;
hndl->config->ndef_callback = config->ndef_callback;
hndl->config->miso_io = config->miso_io == 0 ? RC522_DEFAULT_MISO : config->miso_io;
hndl->config->mosi_io = config->mosi_io == 0 ? RC522_DEFAULT_MOSI : config->mosi_io;
hndl->config->sck_io = config->sck_io == 0 ? RC522_DEFAULT_SCK : config->sck_io;
hndl->config->sda_io = config->sda_io == 0 ? RC522_DEFAULT_SDA : config->sda_io;
hndl->config->spi_host_id = config->spi_host_id == 0 ? RC522_DEFAULT_SPI_HOST : config->spi_host_id;
hndl->config->scan_interval_ms = config->scan_interval_ms < 50 ? RC522_DEFAULT_SCAN_INTERVAL_MS : config->scan_interval_ms;
hndl->config->task_stack_size = config->task_stack_size == 0 ? RC522_DEFAULT_TACK_STACK_SIZE : config->task_stack_size;
hndl->config->task_priority = config->task_priority == 0 ? RC522_DEFAULT_TACK_STACK_PRIORITY : config->task_priority;
esp_err_t err = rc522_spi_init();
if(err != ESP_OK) {
rc522_destroy();
return err;
}
// ---------- RW test ------------
const uint8_t test_addr = 0x24, test_val = 0x25;
for(uint8_t i = test_val; i < test_val + 2; i++) {
if((err = rc522_write(test_addr, i)) != ESP_OK || rc522_read(test_addr) != i) {
ESP_LOGE(TAG, "RW test fail");
rc522_destroy();
return err;
}
}
// ------- End of RW test --------
rc522_write(0x01, 0x0F);
rc522_write(0x2A, 0x8D);
rc522_write(0x2B, 0x3E);
rc522_write(0x2D, 0x1E);
rc522_write(0x2C, 0x00);
rc522_write(0x15, 0x40);
rc522_write(0x11, 0x3D);
rc522_antenna_on();
hndl->running = true;
if (xTaskCreate(rc522_task, "rc522_task", hndl->config->task_stack_size, NULL, hndl->config->task_priority, &hndl->task_handle) != pdTRUE) {
ESP_LOGE(TAG, "Fail to create rc522 task");
rc522_destroy();
return err;
}
if(err != ESP_OK) {
ESP_LOGE(TAG, "Fail to create timer");
rc522_destroy();
return err;
}
ESP_LOGI(TAG, "Initialized (firmware: 0x%x)", rc522_fw_version());
return ESP_OK;
}
uint64_t rc522_sn_to_u64(uint8_t* sn) {
if(!sn) {
return 0;
}
uint64_t result = 0;
for(int i = 4; i >= 0; i--) {
result |= ((uint64_t) sn[i] << (i * 8));
}
return result;
}
/* Returns pointer to dynamically allocated array of two element */
static uint8_t* rc522_calculate_crc(uint8_t *data, uint8_t n) {
rc522_clear_bitmask(0x05, 0x04);
rc522_set_bitmask(0x0A, 0x80);
rc522_write_n(0x09, n, data);
rc522_write(0x01, 0x03);
uint8_t i = 255;
uint8_t nn = 0;
for(;;) {
nn = rc522_read(0x05);
i--;
if(! (i != 0 && ! (nn & 0x04))) {
break;
}
}
uint8_t* res = (uint8_t*) malloc(2);
res[0] = rc522_read(0x22);
res[1] = rc522_read(0x21);
return res;
}
static uint8_t* rc522_card_write(uint8_t cmd, uint8_t *data, uint8_t n, uint8_t* res_n) {
uint8_t *result = NULL;
uint8_t irq = 0x00;
uint8_t irq_wait = 0x00;
uint8_t last_bits = 0;
uint8_t nn = 0;
if(cmd == 0x0E) {
irq = 0x12;
irq_wait = 0x10;
}
else if(cmd == 0x0C) {
irq = 0x77;
irq_wait = 0x30;
}
rc522_write(0x02, irq | 0x80);
rc522_clear_bitmask(0x04, 0x80);
rc522_set_bitmask(0x0A, 0x80);
rc522_write(0x01, 0x00);
rc522_write_n(0x09, n, data);
rc522_write(0x01, cmd);
if(cmd == 0x0C) {
rc522_set_bitmask(0x0D, 0x80);
}
uint16_t i = 1000;
for(;;) {
nn = rc522_read(0x04);
i--;
if(! (i != 0 && (((nn & 0x01) == 0) && ((nn & irq_wait) == 0)))) {
break;
}
}
rc522_clear_bitmask(0x0D, 0x80);
if(i != 0) {
if((rc522_read(0x06) & 0x1B) == 0x00) {
if(cmd == 0x0C) {
nn = rc522_read(0x0A);
last_bits = rc522_read(0x0C) & 0x07;
if (last_bits != 0) {
*res_n = (nn - 1) + last_bits;
} else {
*res_n = nn;
}
result = (uint8_t*) malloc(*res_n);
for(i = 0; i < *res_n; i++) {
result[i] = rc522_read(0x09);
}
}
}
}
return result;
}
static uint8_t* rc522_request(uint8_t* res_n) {
uint8_t* result = NULL;
rc522_write(0x0D, 0x07);
uint8_t req_mode = 0x26;
result = rc522_card_write(0x0C, &req_mode, 1, res_n);
if(*res_n * 8 != 0x10) {
free(result);
return NULL;
}
return result;
}
static uint8_t* rc522_anticoll() {
uint8_t res_n;
rc522_write(0x0D, 0x00);
uint8_t* result = rc522_card_write(0x0C, (uint8_t[]) { 0x93, 0x20 }, 2, &res_n);
if(result && res_n != 5) { // all cards/tags serial numbers is 5 bytes long (?)
free(result);
return NULL;
}
return result;
}
/* Send HALT and clear crypto bit. Card will only respond to WUPA after this. */
static void rc522_halt_card() {
uint8_t buf[] = { 0x50, 0x00, 0x00, 0x00 };
uint8_t* crc = rc522_calculate_crc(buf, 2);
buf[2] = crc[0];
buf[3] = crc[1];
free(crc);
uint8_t res_n;
uint8_t* res = rc522_card_write(0x0C, buf, 4, &res_n);
free(res);
rc522_clear_bitmask(0x08, 0x08);
}
static uint8_t* rc522_get_tag() {
uint8_t* result = NULL;
uint8_t* res_data = NULL;
uint8_t res_data_n;
res_data = rc522_request(&res_data_n);
if(res_data != NULL) {
free(res_data);
result = rc522_anticoll();
/* NOTE: card is NOT halted here — caller must call rc522_halt_card() */
}
return result;
}
/* URI prefix table for NDEF URI records (RFC 3986 shortened forms) */
static const char* const RC522_URI_PREFIXES[] = {
"", /* 0x00 no prefix */
"http://www.", /* 0x01 */
"https://www.", /* 0x02 */
"http://", /* 0x03 */
"https://", /* 0x04 */
"tel:", /* 0x05 */
"mailto:", /* 0x06 */
"ftp://anonymous:anonymous@", /* 0x07 */
"ftp://ftp.", /* 0x08 */
"ftps://", /* 0x09 */
"sftp://", /* 0x0A */
"smb://", /* 0x0B */
"nfs://", /* 0x0C */
"ftp://", /* 0x0D */
"dav://", /* 0x0E */
"news:", /* 0x0F */
"telnet://", /* 0x10 */
"imap:", /* 0x11 */
"rtsp://", /* 0x12 */
"urn:", /* 0x13 */
"pop:", /* 0x14 */
"sip:", /* 0x15 */
"sips:", /* 0x16 */
"tftp:", /* 0x17 */
"btspp://", /* 0x18 */
"btl2cap://", /* 0x19 */
"btgoep://", /* 0x1A */
"tcpobex://", /* 0x1B */
"irdaobex://", /* 0x1C */
"file://", /* 0x1D */
"urn:epc:id:", /* 0x1E */
"urn:epc:tag:", /* 0x1F */
"urn:epc:pat:", /* 0x20 */
"urn:epc:raw:", /* 0x21 */
"urn:epc:", /* 0x22 */
"urn:nfc:", /* 0x23 */
};
#define RC522_URI_PREFIX_COUNT (sizeof(RC522_URI_PREFIXES) / sizeof(RC522_URI_PREFIXES[0]))
/* Parse an NDEF message TLV block and extract the first URI record URL.
* Returns heap-allocated null-terminated URL string, or NULL if not found. */
static char* rc522_parse_ndef_url(const uint8_t* data, int len) {
int i = 0;
while(i < len) {
uint8_t tlv_type = data[i++];
if(tlv_type == 0xFE) break; /* Terminator TLV */
if(tlv_type == 0x00) continue; /* Null TLV — no length byte */
if(i >= len) break;
/* Read TLV length (1 or 3 bytes) */
uint16_t tlv_len;
if(data[i] == 0xFF) {
i++;
if(i + 1 >= len) break;
tlv_len = ((uint16_t)data[i] << 8) | data[i + 1];
i += 2;
} else {
tlv_len = data[i++];
}
if(tlv_type != 0x03 || tlv_len == 0 || i + (int)tlv_len > len) {
i += tlv_len;
continue;
}
/* Parse NDEF record header */
uint8_t header = data[i++];
uint8_t tnf = header & 0x07;
bool sr = (header & 0x10) != 0; /* Short Record */
bool il = (header & 0x08) != 0; /* ID Length present */
if(i >= len) break;
uint8_t type_len = data[i++];
uint32_t payload_len;
if(sr) {
if(i >= len) break;
payload_len = data[i++];
} else {
if(i + 3 >= len) break;
payload_len = ((uint32_t)data[i] << 24) | ((uint32_t)data[i+1] << 16)
| ((uint32_t)data[i+2] << 8) | data[i+3];
i += 4;
}
uint8_t id_len = 0;
if(il) {
if(i >= len) break;
id_len = data[i++];
}
/* Only handle Well-Known URI records (TNF=0x01, Type='U') */
if(tnf == 0x01 && type_len == 1 && i < len && data[i] == 0x55) {
i++; /* skip type byte 'U' */
i += id_len; /* skip optional ID */
if(payload_len > 0 && i < len) {
uint8_t prefix_code = data[i++];
payload_len--;
const char* prefix = (prefix_code < RC522_URI_PREFIX_COUNT) ? RC522_URI_PREFIXES[prefix_code] : "";
size_t prefix_len = strlen(prefix);
size_t body_len = (payload_len < (uint32_t)(len - i)) ? payload_len : (uint32_t)(len - i);
if(prefix_len + body_len > 255) body_len = 255 - prefix_len;
char* url = (char*) malloc(prefix_len + body_len + 1);
if(!url) return NULL;
memcpy(url, prefix, prefix_len);
memcpy(url + prefix_len, data + i, body_len);
url[prefix_len + body_len] = '\0';
return url;
}
}
/* Skip over this record */
i += type_len + id_len + (int)payload_len;
}
return NULL;
}
/* Wake a halted card with WUPA (0x52), perform cascade anticollision (CL1 + optional CL2),
* SELECT the card, read pages 4–15 of user memory (NDEF area), parse, then halt.
* Returns heap-allocated URL string or NULL. Card is always left in HALT state on return. */
static char* rc522_read_ndef_url() {
uint8_t res_n;
/* WUPA: wake halted card — identical bit-framing to REQA but byte is 0x52 */
rc522_write(0x0D, 0x07);
uint8_t wupa_cmd = 0x52;
uint8_t* atqa = rc522_card_write(0x0C, &wupa_cmd, 1, &res_n);
if(!atqa || res_n * 8 != 0x10) {
free(atqa);
return NULL;
}
free(atqa);
/* CL1 anti-collision */
rc522_write(0x0D, 0x00);
uint8_t* cl1 = rc522_card_write(0x0C, (uint8_t[]){ 0x93, 0x20 }, 2, &res_n);
if(!cl1 || res_n != 5) {
free(cl1);
rc522_halt_card();
return NULL;
}
bool cascade = (cl1[0] == 0x88); /* cascade tag byte indicates 7-byte UID */
/* CL1 SELECT */
uint8_t sel1[9] = { 0x93, 0x70, cl1[0], cl1[1], cl1[2], cl1[3], cl1[4] };
uint8_t* crc = rc522_calculate_crc(sel1, 7);
sel1[7] = crc[0]; sel1[8] = crc[1]; free(crc);
uint8_t sel_n;
uint8_t* sak1 = rc522_card_write(0x0C, sel1, 9, &sel_n);
free(cl1);
if(!sak1) {
rc522_halt_card();
return NULL;
}
free(sak1);
if(cascade) {
/* CL2 anti-collision */
rc522_write(0x0D, 0x00);
uint8_t* cl2 = rc522_card_write(0x0C, (uint8_t[]){ 0x95, 0x20 }, 2, &res_n);
if(!cl2 || res_n != 5) {
free(cl2);
rc522_halt_card();
return NULL;
}
/* CL2 SELECT */
uint8_t sel2[9] = { 0x95, 0x70, cl2[0], cl2[1], cl2[2], cl2[3], cl2[4] };
crc = rc522_calculate_crc(sel2, 7);
sel2[7] = crc[0]; sel2[8] = crc[1]; free(crc);
uint8_t* sak2 = rc522_card_write(0x0C, sel2, 9, &sel_n);
free(cl2);
if(!sak2) {
rc522_halt_card();
return NULL;
}
free(sak2);
}
/* Card is now SELECTED. Verify NDEF capability container at page 3.
* For NTAG/MIFARE Ultralight the NDEF magic byte is 0xE1 at CC[0]. */
uint8_t read_cc[4] = { 0x30, 0x03 };
crc = rc522_calculate_crc(read_cc, 2);
read_cc[2] = crc[0]; read_cc[3] = crc[1]; free(crc);
uint8_t* cc_data = rc522_card_write(0x0C, read_cc, 4, &res_n);
if(!cc_data || res_n < 4 || cc_data[0] != 0xE1) {
free(cc_data);
rc522_halt_card();
return NULL;
}
free(cc_data);
/* Read NDEF user pages 4–15 (12 pages × 4 bytes = 48 bytes).
* READ command (0x30) returns 16 bytes (4 pages) per call. */
uint8_t ndef_buf[48];
int ndef_len = 0;
for(uint8_t page = 4; page <= 12; page += 4) { /* 3 reads × 16 bytes = 48 bytes */
uint8_t read_cmd[4] = { 0x30, page };
crc = rc522_calculate_crc(read_cmd, 2);
read_cmd[2] = crc[0]; read_cmd[3] = crc[1]; free(crc);
uint8_t* page_data = rc522_card_write(0x0C, read_cmd, 4, &res_n);
if(!page_data || res_n < 16) {
free(page_data);
break;
}
int copy = (48 - ndef_len < 16) ? 48 - ndef_len : 16;
memcpy(ndef_buf + ndef_len, page_data, copy);
ndef_len += copy;
free(page_data);
}
rc522_halt_card();
if(ndef_len == 0) return NULL;
return rc522_parse_ndef_url(ndef_buf, ndef_len);
}
esp_err_t rc522_start(rc522_start_args_t start_args) {
esp_err_t err = rc522_init(&start_args);
return err != ESP_OK ? err : rc522_start2();
}
esp_err_t rc522_start2() {
if(! hndl) { return ESP_ERR_INVALID_STATE; }
hndl->scan_started = true;
return ESP_OK;
}
esp_err_t rc522_pause() {
if(! hndl) { return ESP_ERR_INVALID_STATE; }
if(! hndl->scan_started) {
return ESP_OK;
}
hndl->scan_started = false;
return ESP_OK;
}
void rc522_destroy() {
if(! hndl) { return; }
rc522_pause(); // stop timer
hndl->running = false; // task will delete itself
if(hndl->spi) {
spi_bus_remove_device(hndl->spi);
spi_bus_free(hndl->config->spi_host_id);
hndl->spi = NULL;
}
free(hndl->config);
hndl->config = NULL;
free(hndl);
hndl = NULL;
}
static bool isSameSerialNumber(uint8_t* one, uint8_t* other) {
if (one == NULL && other == NULL) {
return true;
}
if (one == NULL || other == NULL) {
return false;
}
return one[0] == other[0]
&& one[1] == other[1]
&& one[2] == other[2]
&& one[3] == other[3]
&& one[4] == other[4];
}
static void rc522_task(void* arg) {
while(hndl->running) {
if(!hndl->scan_started) {
vTaskDelay(100 / portTICK_PERIOD_MS);
continue;
}
uint8_t* serial_no = rc522_get_tag(); /* card NOT halted yet */
if(!isSameSerialNumber(serial_no, hndl->lastSerialNumber)) {
rc522_tag_callback_t cb = hndl->config->callback;
rc522_ndef_callback_t ndef_cb = hndl->config->ndef_callback;
if(serial_no == NULL) {
free(hndl->lastSerialNumber);
hndl->lastSerialNumber = NULL;
if(cb) cb(NULL);
if(ndef_cb) ndef_cb(NULL, NULL);
} else {
if(!hndl->lastSerialNumber) {
hndl->lastSerialNumber = malloc(5);
}
memcpy(hndl->lastSerialNumber, serial_no, 5);
if(cb) cb(serial_no);
if(ndef_cb) {
/* rc522_read_ndef_url() handles HALT internally after reading */
char* url = rc522_read_ndef_url();
ndef_cb(serial_no, url);
if(url) free(url);
} else {
rc522_halt_card();
}
}
} else if(serial_no != NULL) {
/* Same card still present — halt it again to prevent continuous REQA responses */
rc522_halt_card();
}
int delay_ms = hndl->config->scan_interval_ms;
if(serial_no != NULL) {
free(serial_no);
serial_no = NULL;
delay_ms *= 2;
}
vTaskDelay(delay_ms / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
+93
View File
@@ -0,0 +1,93 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "driver/spi_master.h"
#define RC522_DEFAULT_MISO (25)
#define RC522_DEFAULT_MOSI (23)
#define RC522_DEFAULT_SCK (19)
#define RC522_DEFAULT_SDA (22)
#define RC522_DEFAULT_SPI_HOST (VSPI_HOST)
#define RC522_DEFAULT_SCAN_INTERVAL_MS (125)
#define RC522_DEFAULT_TACK_STACK_SIZE (4 * 1024)
#define RC522_DEFAULT_TACK_STACK_PRIORITY (4)
typedef void(*rc522_tag_callback_t)(uint8_t*);
/* Called when a tag is detected (url != NULL) or removed (sn == NULL, url == NULL).
* url is the NDEF URI record content, or NULL if no NDEF was found on the tag.
* The caller must NOT free sn or url — they are freed by the library. */
typedef void(*rc522_ndef_callback_t)(uint8_t* sn, const char* url);
typedef struct {
int miso_io; /*<! MFRC522 MISO gpio (Default: 25) */
int mosi_io; /*<! MFRC522 MOSI gpio (Default: 23) */
int sck_io; /*<! MFRC522 SCK gpio (Default: 19) */
int sda_io; /*<! MFRC522 SDA gpio (Default: 22) */
spi_host_device_t spi_host_id; /*<! Default VSPI_HOST (SPI3) */
rc522_tag_callback_t callback; /*<! Called with 5-byte serial number, or NULL when tag removed */
rc522_ndef_callback_t ndef_callback; /*<! Called with (sn, url) on tag change; url may be NULL */
uint16_t scan_interval_ms; /*<! How fast will ESP32 scan for nearby tags, in miliseconds. Default: 125ms */
size_t task_stack_size; /*<! Stack size of rc522 task (Default: 4 * 1024) */
uint8_t task_priority; /*<! Priority of rc522 task (Default: 4) */
} rc522_config_t;
typedef rc522_config_t rc522_start_args_t;
/**
* @brief Initialize RC522 module.
* To start scanning tags - call rc522_resume or rc522_start2 function.
* @param config Configuration
* @return ESP_OK on success
*/
esp_err_t rc522_init(rc522_config_t* config);
/**
* @brief Convert serial number (array of 5 bytes) to uint64_t number
* @param sn Serial number
* @return Serial number in number representation. If fail, 0 will be retured
*/
uint64_t rc522_sn_to_u64(uint8_t* sn);
/**
* @brief Check if RC522 is inited
* @return true if RC522 is inited
*/
bool rc522_is_inited();
/**
* @brief This function will call rc522_init function and immediately start to scan tags by calling rc522_resume function.
* @param start_args Configuration
* @return ESP_OK on success
*/
esp_err_t rc522_start(rc522_start_args_t start_args);
/**
* @brief Start to scan tags. If already started, ESP_OK will just be returned.
* @return ESP_OK on success
*/
esp_err_t rc522_start2();
/**
* @brief Start to scan tags. If already started, ESP_OK will just be returned.
* @return ESP_OK on success
*/
#define rc522_resume() rc522_start2()
/**
* @brief Pause scan tags. If already paused, ESP_OK will just be returned.
* @return ESP_OK on success
*/
esp_err_t rc522_pause();
/**
* @brief Destroy RC522 and free all resources
*/
void rc522_destroy();
#ifdef __cplusplus
}
#endif