ESPHome 2025.5.0
Loading...
Searching...
No Matches
rc522.cpp
Go to the documentation of this file.
1#include "rc522.h"
2#include "esphome/core/log.h"
3
4// Based on:
5// - https://github.com/miguelbalboa/rfid
6
7namespace esphome {
8namespace rc522 {
9
10static const uint8_t WAIT_I_RQ = 0x30; // RxIRq and IdleIRq
11
12static const char *const TAG = "rc522";
13
14static const uint8_t RESET_COUNT = 5;
15
16std::string format_buffer(uint8_t *b, uint8_t len) {
17 char buf[32];
18 int offset = 0;
19 for (uint8_t i = 0; i < len; i++) {
20 const char *format = "%02X";
21 if (i + 1 < len)
22 format = "%02X-";
23 offset += sprintf(buf + offset, format, b[i]);
24 }
25 return std::string(buf);
26}
27
28std::string format_uid(std::vector<uint8_t> &uid) {
29 char buf[32];
30 int offset = 0;
31 for (size_t i = 0; i < uid.size(); i++) {
32 const char *format = "%02X";
33 if (i + 1 < uid.size())
34 format = "%02X-";
35 offset += sprintf(buf + offset, format, uid[i]);
36 }
37 return std::string(buf);
38}
39
41 state_ = STATE_SETUP;
42 // Pull device out of power down / reset state.
43
44 // First set the resetPowerDownPin as digital input, to check the MFRC522 power down mode.
45 if (reset_pin_ != nullptr) {
47
48 if (!reset_pin_->digital_read()) { // The MFRC522 chip is in power down mode.
49 ESP_LOGV(TAG, "Power down mode detected. Hard resetting...");
50 reset_pin_->pin_mode(gpio::FLAG_OUTPUT); // Now set the resetPowerDownPin as digital output.
51 reset_pin_->digital_write(false); // Make sure we have a clean LOW state.
52 delayMicroseconds(2); // 8.8.1 Reset timing requirements says about 100ns. Let us be generous: 2μsl
53 reset_pin_->digital_write(true); // Exit power down mode. This triggers a hard reset.
54 // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs.
55 // Let us be generous: 50ms.
57 return;
58 }
59 }
60
61 // Setup a soft reset
62 reset_count_ = RESET_COUNT;
64}
65
67 // Per original code, wait 50 ms
68 if (millis() - reset_timeout_ < 50)
69 return;
70
71 // Reset baud rates
72 ESP_LOGV(TAG, "Initialize");
73
76 // Reset ModWidthReg
78
79 // When communicating with a PICC we need a timeout if something goes wrong.
80 // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo].
81 // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg.
82 pcd_write_register(T_MODE_REG, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all
83 // communication modes at all speeds
84
85 // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25μs.
87 pcd_write_register(T_RELOAD_REG_H, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout.
89
90 // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
92 pcd_write_register(MODE_REG, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC
93 // command to 0x6363 (ISO 14443-3 part 6.2.4)
94
95 state_ = STATE_INIT;
96}
97
99 ESP_LOGCONFIG(TAG, "RC522:");
100 switch (this->error_code_) {
101 case NONE:
102 break;
103 case RESET_FAILED:
104 ESP_LOGE(TAG, "Reset command failed!");
105 break;
106 }
107
108 LOG_PIN(" RESET Pin: ", this->reset_pin_);
109
110 LOG_UPDATE_INTERVAL(this);
111
112 for (auto *child : this->binary_sensors_) {
113 LOG_BINARY_SENSOR(" ", "Tag", child);
114 }
115}
116
118 if (state_ == STATE_INIT) {
120 pcd_clear_register_bit_mask_(COLL_REG, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared.
123 state_ = STATE_PICC_REQUEST_A;
124 } else {
125 ESP_LOGW(TAG, "Communication takes longer than update interval: %d", state_);
126 }
127}
128
130 // First check reset is needed
131 if (reset_count_ > 0) {
132 pcd_reset_();
133 return;
134 }
135 if (state_ == STATE_SETUP) {
136 initialize_();
137 return;
138 }
139
140 StatusCode status = STATUS_ERROR; // For lint passing. TODO: refactor this
141 if (awaiting_comm_) {
142 if (state_ == STATE_SELECT_SERIAL_DONE) {
143 status = await_crc_();
144 } else {
146 }
147
148 if (status == STATUS_WAITING) {
149 return;
150 }
151 awaiting_comm_ = false;
152 ESP_LOGV(TAG, "finished communication status: %d, state: %d", status, state_);
153 }
154
155 switch (state_) {
157 if (status == STATUS_TIMEOUT) { // no tag present
158 for (auto *obj : this->binary_sensors_)
159 obj->on_scan_end(); // reset the binary sensors
160 ESP_LOGV(TAG, "CMD_REQA -> TIMEOUT (no tag present) %d", status);
161 state_ = STATE_DONE;
162 } else if (status != STATUS_OK) {
163 ESP_LOGW(TAG, "CMD_REQA -> Not OK %d", status);
164 state_ = STATE_DONE;
165 } else if (back_length_ != 2) { // || *valid_bits_ != 0) { // ATQA must be exactly 16 bits.
166 ESP_LOGW(TAG, "CMD_REQA -> OK, but unexpected back_length_ of %d", back_length_);
167 state_ = STATE_DONE;
168 } else {
169 state_ = STATE_READ_SERIAL;
170 }
171 if (state_ == STATE_DONE) {
172 // Don't wait another loop cycle
174 }
175 break;
176 }
177 case STATE_READ_SERIAL: {
178 ESP_LOGV(TAG, "STATE_READ_SERIAL (%d)", status);
179 switch (uid_idx_) {
180 case 0:
182 break;
183 case 3:
185 break;
186 case 6:
188 break;
189 default:
190 ESP_LOGE(TAG, "uid_idx_ invalid, uid_idx_ = %d", uid_idx_);
191 state_ = STATE_DONE;
192 }
193 buffer_[1] = 32;
195 state_ = STATE_SELECT_SERIAL;
196 break;
197 }
198 case STATE_SELECT_SERIAL: {
199 buffer_[1] = 0x70; // select
200 // todo: set CRC
201 buffer_[6] = buffer_[2] ^ buffer_[3] ^ buffer_[4] ^ buffer_[5];
204 break;
205 }
207 send_len_ = 6;
209 state_ = STATE_READ_SERIAL_DONE;
210 break;
211 }
213 if (status != STATUS_OK || back_length_ != 3) {
214 if (status == STATUS_TIMEOUT) {
215 ESP_LOGV(TAG, "STATE_READ_SERIAL_DONE -> TIMEOUT (no tag present) %d", status);
216 } else {
217 ESP_LOGW(TAG, "Unexpected response. Read status is %d. Read bytes: %d (%s)", status, back_length_,
218 format_buffer(buffer_, 9).c_str());
219 }
220
221 state_ = STATE_DONE;
222 uid_idx_ = 0;
223
225 return;
226 }
227
228 // copy the uid
229 bool cascade = buffer_[2] == PICC_CMD_CT; // todo: should be determined based on select response (buffer[6])
230 for (uint8_t i = 2 + cascade; i < 6; i++)
232 ESP_LOGVV(TAG, "copied uid to idx %d last byte is 0x%x, cascade is %d", uid_idx_, uid_buffer_[uid_idx_ - 1],
233 cascade);
234
235 if (cascade) { // there is more bytes in the UID
236 state_ = STATE_READ_SERIAL;
237 return;
238 }
239
240 std::vector<uint8_t> rfid_uid(std::begin(uid_buffer_), std::begin(uid_buffer_) + uid_idx_);
241 uid_idx_ = 0;
242 // ESP_LOGD(TAG, "Processing '%s'", format_uid(rfid_uid).c_str());
244 state_ = STATE_INIT; // scan again on next update
245 bool report = true;
246
247 for (auto *tag : this->binary_sensors_) {
248 if (tag->process(rfid_uid)) {
249 report = false;
250 }
251 }
252
253 if (this->current_uid_ == rfid_uid) {
254 return;
255 }
256
257 this->current_uid_ = rfid_uid;
258
259 for (auto *trigger : this->triggers_ontag_)
260 trigger->process(rfid_uid);
261
262 if (report) {
263 ESP_LOGD(TAG, "Found new tag '%s'", format_uid(rfid_uid).c_str());
264 }
265 break;
266 }
267 case STATE_DONE: {
268 if (!this->current_uid_.empty()) {
269 ESP_LOGV(TAG, "Tag '%s' removed", format_uid(this->current_uid_).c_str());
270 for (auto *trigger : this->triggers_ontagremoved_)
271 trigger->process(this->current_uid_);
272 }
273 this->current_uid_ = {};
274 state_ = STATE_INIT;
275 break;
276 }
277 default:
278 break;
279 }
280} // namespace rc522
281
286 // The datasheet does not mention how long the SoftRest command takes to complete.
287 // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg)
288 // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs.
289 // Let us be generous: 50ms.
290
291 if (millis() - reset_timeout_ < 50)
292 return;
293
294 if (reset_count_ == RESET_COUNT) {
295 ESP_LOGI(TAG, "Soft reset...");
296 // Issue the SoftReset command.
298 }
299
300 // Expect the PowerDown bit in CommandReg to be cleared (max 3x50ms)
301 if ((pcd_read_register(COMMAND_REG) & (1 << 4)) == 0) {
302 reset_count_ = 0;
303 ESP_LOGI(TAG, "Device online.");
304 // Wait for initialize
306 return;
307 }
308
309 if (--reset_count_ == 0) {
310 ESP_LOGE(TAG, "Unable to reset RC522.");
311 this->error_code_ = RESET_FAILED;
312 mark_failed();
313 }
314}
315
321 uint8_t value = pcd_read_register(TX_CONTROL_REG);
322 if ((value & 0x03) != 0x03) {
323 pcd_write_register(TX_CONTROL_REG, value | 0x03);
324 }
325}
326
331 uint8_t value = pcd_read_register(TX_CONTROL_REG);
332 if ((value & 0x03) != 0x00) {
333 pcd_write_register(TX_CONTROL_REG, value & ~0x03);
334 }
335}
336
341 uint8_t mask
342) {
343 uint8_t tmp = pcd_read_register(reg);
344 pcd_write_register(reg, tmp | mask); // set bit mask
345}
346
351 uint8_t mask
352) {
353 uint8_t tmp = pcd_read_register(reg);
354 pcd_write_register(reg, tmp & (~mask)); // clear bit mask
355}
356
363void RC522::pcd_transceive_data_(uint8_t send_len) {
364 ESP_LOGV(TAG, "PCD TRANSCEIVE: RX: %s", format_buffer(buffer_, send_len).c_str());
365 delayMicroseconds(1000); // we need 1 ms delay between antenna on and those communication commands
366 send_len_ = send_len;
367 // Prepare values for BitFramingReg
368 // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only)
369 // uint8_t. TxLastBits = BitFramingReg[2..0]
370 uint8_t bit_framing = (buffer_[0] == PICC_CMD_REQA) ? 7 : 0;
371
372 pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop any active command.
373 pcd_write_register(COM_IRQ_REG, 0x7F); // Clear all seven interrupt request bits
374 pcd_write_register(FIFO_LEVEL_REG, 0x80); // FlushBuffer = 1, FIFO initialization
375 pcd_write_register(FIFO_DATA_REG, send_len_, buffer_); // Write sendData to the FIFO
376 pcd_write_register(BIT_FRAMING_REG, bit_framing); // Bit adjustments
377 pcd_write_register(COMMAND_REG, PCD_TRANSCEIVE); // Execute the command
378 pcd_set_register_bit_mask_(BIT_FRAMING_REG, 0x80); // StartSend=1, transmission of data starts
379 awaiting_comm_ = true;
381}
382
384 if (millis() - awaiting_comm_time_ < 2) // wait at least 2 ms
385 return STATUS_WAITING;
386 uint8_t n = pcd_read_register(
387 COM_IRQ_REG); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq
388 if (n & 0x01) { // Timer interrupt - nothing received in 25ms
389 back_length_ = 0;
390 error_counter_ = 0; // reset the error counter
391 return STATUS_TIMEOUT;
392 }
393 if (!(n & WAIT_I_RQ)) { // None of the interrupts that signal success has been set.
394 // Wait for the command to complete.
395 if (millis() - awaiting_comm_time_ < 40)
396 return STATUS_WAITING;
397 back_length_ = 0;
398 ESP_LOGW(TAG, "Communication with the MFRC522 might be down, reset in %d",
399 10 - error_counter_); // todo: trigger reset?
400 if (error_counter_++ >= 10) {
401 setup();
402 error_counter_ = 0; // reset the error counter
403 }
404
405 return STATUS_TIMEOUT;
406 }
407 // Stop now if any errors except collisions were detected.
408 uint8_t error_reg_value = pcd_read_register(
409 ERROR_REG); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr
410 if (error_reg_value & 0x13) { // BufferOvfl ParityErr ProtocolErr
411 return STATUS_ERROR;
412 }
413 error_counter_ = 0; // reset the error counter
414
415 n = pcd_read_register(FIFO_LEVEL_REG); // Number of uint8_ts in the FIFO
416 if (n > sizeof(buffer_))
417 return STATUS_NO_ROOM;
418 if (n > sizeof(buffer_) - send_len_)
419 send_len_ = sizeof(buffer_) - n; // simply overwrite the sent values
420 back_length_ = n; // Number of uint8_ts returned
421 pcd_read_register(FIFO_DATA_REG, n, buffer_ + send_len_, rx_align_); // Get received data from FIFO
422 uint8_t valid_bits_local =
423 pcd_read_register(CONTROL_REG) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last
424 // received uint8_t. If this value is 000b, the whole uint8_t is valid.
425
426 // Tell about collisions
427 if (error_reg_value & 0x08) { // CollErr
428 ESP_LOGW(TAG, "collision error, received %d bytes + %d bits (but anticollision not implemented)",
429 back_length_ - (valid_bits_local > 0), valid_bits_local);
430 return STATUS_COLLISION;
431 }
432 // Tell about collisions
433 if (valid_bits_local) {
434 ESP_LOGW(TAG, "only %d valid bits received, tag distance to high? Error code is 0x%x", valid_bits_local,
435 error_reg_value); // TODO: is this always due to collissions?
436 return STATUS_ERROR;
437 }
438 ESP_LOGV(TAG, "received %d bytes: %s", back_length_, format_buffer(buffer_ + send_len_, back_length_).c_str());
439
440 return STATUS_OK;
441}
442
449void RC522::pcd_calculate_crc_(uint8_t *data,
450 uint8_t length
451) {
452 ESP_LOGVV(TAG, "pcd_calculate_crc_(..., %d, ...)", length);
453 pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop any active command.
454 pcd_write_register(DIV_IRQ_REG, 0x04); // Clear the CRCIRq interrupt request bit
455 pcd_write_register(FIFO_LEVEL_REG, 0x80); // FlushBuffer = 1, FIFO initialization
456 pcd_write_register(FIFO_DATA_REG, length, data); // Write data to the FIFO
457 pcd_write_register(COMMAND_REG, PCD_CALC_CRC); // Start the calculation
458
459 awaiting_comm_ = true;
461}
462
464 if (millis() - awaiting_comm_time_ < 2) // wait at least 2 ms
465 return STATUS_WAITING;
466
467 // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved
468 uint8_t n = pcd_read_register(DIV_IRQ_REG);
469 if (n & 0x04) { // CRCIRq bit set - calculation done
470 pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop calculating CRC for new content in the FIFO.
471 // Transfer the result from the registers to the result buffer
474
475 ESP_LOGVV(TAG, "pcd_calculate_crc_() STATUS_OK");
476 return STATUS_OK;
477 }
478 if (millis() - awaiting_comm_time_ < 89)
479 return STATUS_WAITING;
480
481 ESP_LOGD(TAG, "pcd_calculate_crc_() TIMEOUT");
482 // 89ms passed and nothing happened. Communication with the MFRC522 might be down.
483 return STATUS_TIMEOUT;
484}
485
486bool RC522BinarySensor::process(std::vector<uint8_t> &data) {
487 bool result = true;
488 if (data.size() != this->uid_.size()) {
489 result = false;
490 } else {
491 for (size_t i = 0; i < data.size(); i++) {
492 if (data[i] != this->uid_[i]) {
493 result = false;
494 break;
495 }
496 }
497 }
498 this->publish_state(result);
499 this->found_ = result;
500 return result;
501}
502void RC522Trigger::process(std::vector<uint8_t> &data) { this->trigger(format_uid(data)); }
503
504} // namespace rc522
505} // namespace esphome
uint8_t status
Definition bl0942.h:8
virtual void mark_failed()
Mark this component as failed.
virtual void pin_mode(gpio::Flags flags)=0
virtual void digital_write(bool value)=0
virtual bool digital_read()=0
void publish_state(bool state)
Publish a new state to the front-end.
bool process(std::vector< uint8_t > &data)
Definition rc522.cpp:486
std::vector< uint8_t > uid_
Definition rc522.h:270
std::vector< RC522Trigger * > triggers_ontagremoved_
Definition rc522.h:247
void dump_config() override
Definition rc522.cpp:98
virtual uint8_t pcd_read_register(PcdRegister reg)=0
GPIOPin * reset_pin_
Definition rc522.h:242
enum esphome::rc522::RC522::RC522Error NONE
void pcd_transceive_data_(uint8_t send_len)
Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back ...
Definition rc522.cpp:363
std::vector< RC522Trigger * > triggers_ontag_
Definition rc522.h:246
void pcd_reset_()
Performs a soft reset on the MFRC522 chip and waits for it to be ready again.
Definition rc522.cpp:285
uint32_t awaiting_comm_time_
Definition rc522.h:228
std::vector< RC522BinarySensor * > binary_sensors_
Definition rc522.h:245
void pcd_antenna_off_()
Turns the antenna off by disabling pins TX1 and TX2.
Definition rc522.cpp:330
uint8_t back_length_
In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned.
Definition rc522.h:235
void update() override
Definition rc522.cpp:117
StatusCode await_crc_()
Definition rc522.cpp:463
uint8_t reset_count_
Definition rc522.h:243
uint32_t reset_timeout_
Definition rc522.h:244
void setup() override
Definition rc522.cpp:40
uint8_t error_counter_
Definition rc522.h:238
StatusCode await_transceive_()
Definition rc522.cpp:383
void pcd_antenna_on_()
Turns the antenna on by enabling pins TX1 and TX2.
Definition rc522.cpp:320
void pcd_set_register_bit_mask_(PcdRegister reg, uint8_t mask)
Sets the bits given in mask in register reg.
Definition rc522.cpp:340
void pcd_calculate_crc_(uint8_t *data, uint8_t length)
Use the CRC coprocessor in the MFRC522 to calculate a CRC_A.
Definition rc522.cpp:449
std::vector< uint8_t > current_uid_
Definition rc522.h:248
void pcd_clear_register_bit_mask_(PcdRegister reg, uint8_t mask)
Clears the bits given in mask from register reg.
Definition rc522.cpp:350
uint8_t uid_buffer_[10]
Definition rc522.h:236
uint8_t buffer_[9]
buffer for communication, the first bits [0..back_idx-1] are for tx , [back_idx..back_idx+back_len] f...
Definition rc522.h:232
void loop() override
Definition rc522.cpp:129
virtual void pcd_write_register(PcdRegister reg, uint8_t value)=0
void process(std::vector< uint8_t > &data)
Definition rc522.cpp:502
@ FLAG_OUTPUT
Definition gpio.h:19
@ FLAG_INPUT
Definition gpio.h:18
std::string format_uid(std::vector< uint8_t > &uid)
Definition rc522.cpp:28
std::string format_buffer(uint8_t *b, uint8_t len)
Definition rc522.cpp:16
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:301
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition core.cpp:30
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:27
uint16_t length
Definition tt21100.cpp:0