ESPHome  2022.8.0
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 
7 namespace esphome {
8 namespace rc522 {
9 
10 static const uint8_t WAIT_I_RQ = 0x30; // RxIRq and IdleIRq
11 
12 static const char *const TAG = "rc522";
13 
14 static const uint8_t RESET_COUNT = 5;
15 
16 std::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 
28 std::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 
40 void RC522::setup() {
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) {
119  pcd_antenna_on_();
120  pcd_clear_register_bit_mask_(COLL_REG, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared.
121  buffer_[0] = PICC_CMD_REQA;
123  state_ = STATE_PICC_REQUEST_A;
124  } else {
125  ESP_LOGW(TAG, "Communication takes longer than update interval: %d", state_);
126  }
127 }
128 
129 void RC522::loop() {
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 {
145  status = await_transceive_();
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_) {
156  case STATE_PICC_REQUEST_A: {
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];
202  pcd_calculate_crc_(buffer_, 7);
203  state_ = STATE_SELECT_SERIAL_DONE;
204  break;
205  }
207  send_len_ = 6;
209  state_ = STATE_READ_SERIAL_DONE;
210  break;
211  }
212  case STATE_READ_SERIAL_DONE: {
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++)
231  uid_buffer_[uid_idx_++] = buffer_[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_)
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  this->current_uid_ = {};
269  state_ = STATE_INIT;
270  break;
271  }
272  default:
273  break;
274  }
275 } // namespace rc522
276 
281  // The datasheet does not mention how long the SoftRest command takes to complete.
282  // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg)
283  // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs.
284  // Let us be generous: 50ms.
285 
286  if (millis() - reset_timeout_ < 50)
287  return;
288 
289  if (reset_count_ == RESET_COUNT) {
290  ESP_LOGI(TAG, "Soft reset...");
291  // Issue the SoftReset command.
293  }
294 
295  // Expect the PowerDown bit in CommandReg to be cleared (max 3x50ms)
296  if ((pcd_read_register(COMMAND_REG) & (1 << 4)) == 0) {
297  reset_count_ = 0;
298  ESP_LOGI(TAG, "Device online.");
299  // Wait for initialize
301  return;
302  }
303 
304  if (--reset_count_ == 0) {
305  ESP_LOGE(TAG, "Unable to reset RC522.");
306  this->error_code_ = RESET_FAILED;
307  mark_failed();
308  }
309 }
310 
316  uint8_t value = pcd_read_register(TX_CONTROL_REG);
317  if ((value & 0x03) != 0x03) {
318  pcd_write_register(TX_CONTROL_REG, value | 0x03);
319  }
320 }
321 
326  uint8_t value = pcd_read_register(TX_CONTROL_REG);
327  if ((value & 0x03) != 0x00) {
328  pcd_write_register(TX_CONTROL_REG, value & ~0x03);
329  }
330 }
331 
336  uint8_t mask
337 ) {
338  uint8_t tmp = pcd_read_register(reg);
339  pcd_write_register(reg, tmp | mask); // set bit mask
340 }
341 
346  uint8_t mask
347 ) {
348  uint8_t tmp = pcd_read_register(reg);
349  pcd_write_register(reg, tmp & (~mask)); // clear bit mask
350 }
351 
358 void RC522::pcd_transceive_data_(uint8_t send_len) {
359  ESP_LOGV(TAG, "PCD TRANSCEIVE: RX: %s", format_buffer(buffer_, send_len).c_str());
360  delayMicroseconds(1000); // we need 1 ms delay between antenna on and those communication commands
361  send_len_ = send_len;
362  // Prepare values for BitFramingReg
363  // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only)
364  // uint8_t. TxLastBits = BitFramingReg[2..0]
365  uint8_t bit_framing = (buffer_[0] == PICC_CMD_REQA) ? 7 : 0;
366 
367  pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop any active command.
368  pcd_write_register(COM_IRQ_REG, 0x7F); // Clear all seven interrupt request bits
369  pcd_write_register(FIFO_LEVEL_REG, 0x80); // FlushBuffer = 1, FIFO initialization
370  pcd_write_register(FIFO_DATA_REG, send_len_, buffer_); // Write sendData to the FIFO
371  pcd_write_register(BIT_FRAMING_REG, bit_framing); // Bit adjustments
372  pcd_write_register(COMMAND_REG, PCD_TRANSCEIVE); // Execute the command
373  pcd_set_register_bit_mask_(BIT_FRAMING_REG, 0x80); // StartSend=1, transmission of data starts
374  awaiting_comm_ = true;
376 }
377 
379  if (millis() - awaiting_comm_time_ < 2) // wait at least 2 ms
380  return STATUS_WAITING;
381  uint8_t n = pcd_read_register(
382  COM_IRQ_REG); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq
383  if (n & 0x01) { // Timer interrupt - nothing received in 25ms
384  back_length_ = 0;
385  error_counter_ = 0; // reset the error counter
386  return STATUS_TIMEOUT;
387  }
388  if (!(n & WAIT_I_RQ)) { // None of the interrupts that signal success has been set.
389  // Wait for the command to complete.
390  if (millis() - awaiting_comm_time_ < 40)
391  return STATUS_WAITING;
392  back_length_ = 0;
393  ESP_LOGW(TAG, "Communication with the MFRC522 might be down, reset in %d",
394  10 - error_counter_); // todo: trigger reset?
395  if (error_counter_++ > 10)
396  setup();
397 
398  return STATUS_TIMEOUT;
399  }
400  // Stop now if any errors except collisions were detected.
401  uint8_t error_reg_value = pcd_read_register(
402  ERROR_REG); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr
403  if (error_reg_value & 0x13) { // BufferOvfl ParityErr ProtocolErr
404  return STATUS_ERROR;
405  }
406  error_counter_ = 0; // reset the error counter
407 
408  n = pcd_read_register(FIFO_LEVEL_REG); // Number of uint8_ts in the FIFO
409  if (n > sizeof(buffer_))
410  return STATUS_NO_ROOM;
411  if (n > sizeof(buffer_) - send_len_)
412  send_len_ = sizeof(buffer_) - n; // simply overwrite the sent values
413  back_length_ = n; // Number of uint8_ts returned
414  pcd_read_register(FIFO_DATA_REG, n, buffer_ + send_len_, rx_align_); // Get received data from FIFO
415  uint8_t valid_bits_local =
416  pcd_read_register(CONTROL_REG) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last
417  // received uint8_t. If this value is 000b, the whole uint8_t is valid.
418 
419  // Tell about collisions
420  if (error_reg_value & 0x08) { // CollErr
421  ESP_LOGW(TAG, "collision error, received %d bytes + %d bits (but anticollision not implemented)",
422  back_length_ - (valid_bits_local > 0), valid_bits_local);
423  return STATUS_COLLISION;
424  }
425  // Tell about collisions
426  if (valid_bits_local) {
427  ESP_LOGW(TAG, "only %d valid bits received, tag distance to high? Error code is 0x%x", valid_bits_local,
428  error_reg_value); // TODO: is this always due to collissions?
429  return STATUS_ERROR;
430  }
431  ESP_LOGV(TAG, "received %d bytes: %s", back_length_, format_buffer(buffer_ + send_len_, back_length_).c_str());
432 
433  return STATUS_OK;
434 }
435 
442 void RC522::pcd_calculate_crc_(uint8_t *data,
443  uint8_t length
444 ) {
445  ESP_LOGVV(TAG, "pcd_calculate_crc_(..., %d, ...)", length);
446  pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop any active command.
447  pcd_write_register(DIV_IRQ_REG, 0x04); // Clear the CRCIRq interrupt request bit
448  pcd_write_register(FIFO_LEVEL_REG, 0x80); // FlushBuffer = 1, FIFO initialization
449  pcd_write_register(FIFO_DATA_REG, length, data); // Write data to the FIFO
450  pcd_write_register(COMMAND_REG, PCD_CALC_CRC); // Start the calculation
451 
452  awaiting_comm_ = true;
454 }
455 
457  if (millis() - awaiting_comm_time_ < 2) // wait at least 2 ms
458  return STATUS_WAITING;
459 
460  // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved
461  uint8_t n = pcd_read_register(DIV_IRQ_REG);
462  if (n & 0x04) { // CRCIRq bit set - calculation done
463  pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop calculating CRC for new content in the FIFO.
464  // Transfer the result from the registers to the result buffer
467 
468  ESP_LOGVV(TAG, "pcd_calculate_crc_() STATUS_OK");
469  return STATUS_OK;
470  }
471  if (millis() - awaiting_comm_time_ < 89)
472  return STATUS_WAITING;
473 
474  ESP_LOGD(TAG, "pcd_calculate_crc_() TIMEOUT");
475  // 89ms passed and nothing happened. Communication with the MFRC522 might be down.
476  return STATUS_TIMEOUT;
477 }
478 
479 bool RC522BinarySensor::process(std::vector<uint8_t> &data) {
480  bool result = true;
481  if (data.size() != this->uid_.size()) {
482  result = false;
483  } else {
484  for (size_t i = 0; i < data.size(); i++) {
485  if (data[i] != this->uid_[i]) {
486  result = false;
487  break;
488  }
489  }
490  }
491  this->publish_state(result);
492  this->found_ = result;
493  return result;
494 }
495 void RC522Trigger::process(std::vector<uint8_t> &data) { this->trigger(format_uid(data)); }
496 
497 } // namespace rc522
498 } // namespace esphome
virtual void digital_write(bool value)=0
uint8_t rx_align_
Definition: rc522.h:236
void pcd_reset_()
Performs a soft reset on the MFRC522 chip and waits for it to be ready again.
Definition: rc522.cpp:280
void pcd_set_register_bit_mask_(PcdRegister reg, uint8_t mask)
Sets the bits given in mask in register reg.
Definition: rc522.cpp:335
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:229
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:358
void update() override
Definition: rc522.cpp:117
virtual void pcd_write_register(PcdRegister reg, uint8_t value)=0
virtual void pin_mode(gpio::Flags flags)=0
void dump_config() override
Definition: rc522.cpp:98
uint8_t back_length_
In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned.
Definition: rc522.h:232
StatusCode await_crc_()
Definition: rc522.cpp:456
uint8_t uid_buffer_[10]
Definition: rc522.h:233
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:26
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:442
void setup() override
Definition: rc522.cpp:40
std::vector< RC522Trigger * > triggers_
Definition: rc522.h:243
uint32_t awaiting_comm_time_
Definition: rc522.h:225
bool process(std::vector< uint8_t > &data)
Definition: rc522.cpp:479
uint8_t reset_count_
Definition: rc522.h:240
std::string format_uid(std::vector< uint8_t > &uid)
Definition: rc522.cpp:28
void process(std::vector< uint8_t > &data)
Definition: rc522.cpp:495
GPIOPin * reset_pin_
Definition: rc522.h:239
virtual bool digital_read()=0
uint8_t send_len_
Definition: rc522.h:231
enum esphome::rc522::RC522::RC522Error NONE
uint8_t error_counter_
Definition: rc522.h:235
void pcd_antenna_off_()
Turns the antenna off by disabling pins TX1 and TX2.
Definition: rc522.cpp:325
uint32_t reset_timeout_
Definition: rc522.h:241
std::string size_t len
Definition: helpers.h:278
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:111
Definition: a4988.cpp:4
std::vector< uint8_t > current_uid_
Definition: rc522.h:244
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:29
StatusCode await_transceive_()
Definition: rc522.cpp:378
virtual uint8_t pcd_read_register(PcdRegister reg)=0
void pcd_antenna_on_()
Turns the antenna on by enabling pins TX1 and TX2.
Definition: rc522.cpp:315
uint8_t uid_idx_
Definition: rc522.h:234
void loop() override
Definition: rc522.cpp:129
void pcd_clear_register_bit_mask_(PcdRegister reg, uint8_t mask)
Clears the bits given in mask from register reg.
Definition: rc522.cpp:345
std::string format_buffer(uint8_t *b, uint8_t len)
Definition: rc522.cpp:16
std::vector< RC522BinarySensor * > binary_sensors_
Definition: rc522.h:242