mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-29 22:24:26 +00:00 
			
		
		
		
	Add rc522 i2c (#1432)
* split to spi and i2c * fix binary_sensor * i2c comms ready * fix rc522_spi binary sensor compat * lint * lint * add test and codeowners * fix refactor
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							400819175d
						
					
				
				
					commit
					fbc1b3e316
				
			| @@ -1,39 +1,21 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation, pins | ||||
| from esphome.components import spi | ||||
| from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID, CONF_RESET_PIN, CONF_CS_PIN | ||||
| from esphome.components import spi, rc522 | ||||
| from esphome.const import CONF_ID | ||||
|  | ||||
| CODEOWNERS = ['@glmnet'] | ||||
| DEPENDENCIES = ['spi'] | ||||
| AUTO_LOAD = ['binary_sensor'] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| AUTO_LOAD = ['rc522'] | ||||
|  | ||||
| rc522_spi_ns = cg.esphome_ns.namespace('rc522_spi') | ||||
| RC522 = rc522_spi_ns.class_('RC522', cg.PollingComponent, spi.SPIDevice) | ||||
| RC522Trigger = rc522_spi_ns.class_('RC522Trigger', automation.Trigger.template(cg.std_string)) | ||||
| RC522Spi = rc522_spi_ns.class_('RC522Spi', rc522.RC522, spi.SPIDevice) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema({ | ||||
|     cv.GenerateID(): cv.declare_id(RC522), | ||||
|     cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, | ||||
|     cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, | ||||
|     cv.Optional(CONF_ON_TAG): automation.validate_automation({ | ||||
|         cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RC522Trigger), | ||||
|     }), | ||||
| }).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema()) | ||||
| CONFIG_SCHEMA = cv.All(rc522.RC522_SCHEMA.extend({ | ||||
|     cv.GenerateID(): cv.declare_id(RC522Spi), | ||||
| }).extend(spi.spi_device_schema(cs_pin_required=True))) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     yield cg.register_component(var, config) | ||||
|     yield rc522.setup_rc522(var, config) | ||||
|     yield spi.register_spi_device(var, config) | ||||
|  | ||||
|     if CONF_RESET_PIN in config: | ||||
|         reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN]) | ||||
|         cg.add(var.set_reset_pin(reset)) | ||||
|  | ||||
|     for conf in config.get(CONF_ON_TAG, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) | ||||
|         cg.add(var.register_trigger(trigger)) | ||||
|         yield automation.build_automation(trigger, [(cg.std_string, 'x')], conf) | ||||
|   | ||||
| @@ -1,44 +1,9 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import binary_sensor | ||||
| from esphome.const import CONF_UID, CONF_ID | ||||
| from esphome.core import HexInt | ||||
| from . import rc522_spi_ns, RC522 | ||||
| import esphome.components.rc522.binary_sensor as rc522_binary_sensor | ||||
|  | ||||
| DEPENDENCIES = ['rc522_spi'] | ||||
| DEPENDENCIES = ['rc522'] | ||||
|  | ||||
| CONF_RC522_ID = 'rc522_id' | ||||
|  | ||||
|  | ||||
| def validate_uid(value): | ||||
|     value = cv.string_strict(value) | ||||
|     for x in value.split('-'): | ||||
|         if len(x) != 2: | ||||
|             raise cv.Invalid("Each part (separated by '-') of the UID must be two characters " | ||||
|                              "long.") | ||||
|         try: | ||||
|             x = int(x, 16) | ||||
|         except ValueError as err: | ||||
|             raise cv.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.") from err | ||||
|         if x < 0 or x > 255: | ||||
|             raise cv.Invalid("Valid values for UID parts (separated by '-') are 00 to FF") | ||||
|     return value | ||||
|  | ||||
|  | ||||
| RC522BinarySensor = rc522_spi_ns.class_('RC522BinarySensor', binary_sensor.BinarySensor) | ||||
|  | ||||
| CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ | ||||
|     cv.GenerateID(): cv.declare_id(RC522BinarySensor), | ||||
|     cv.GenerateID(CONF_RC522_ID): cv.use_id(RC522), | ||||
|     cv.Required(CONF_UID): validate_uid, | ||||
| }) | ||||
| CONFIG_SCHEMA = rc522_binary_sensor.CONFIG_SCHEMA | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     yield binary_sensor.register_binary_sensor(var, config) | ||||
|  | ||||
|     hub = yield cg.get_variable(config[CONF_RC522_ID]) | ||||
|     cg.add(hub.register_tag(var)) | ||||
|     addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')] | ||||
|     cg.add(var.set_uid(addr)) | ||||
|     yield rc522_binary_sensor.to_code(config) | ||||
|   | ||||
| @@ -9,218 +9,30 @@ namespace rc522_spi { | ||||
|  | ||||
| static const char *TAG = "rc522_spi"; | ||||
|  | ||||
| static const uint8_t RESET_COUNT = 5; | ||||
| void RC522Spi::setup() { | ||||
|   ESP_LOGI(TAG, "SPI Setup"); | ||||
|   this->spi_setup(); | ||||
|  | ||||
| void format_uid(char *buf, const uint8_t *uid, uint8_t uid_length) { | ||||
|   int offset = 0; | ||||
|   for (uint8_t i = 0; i < uid_length; i++) { | ||||
|     const char *format = "%02X"; | ||||
|     if (i + 1 < uid_length) | ||||
|       format = "%02X-"; | ||||
|     offset += sprintf(buf + offset, format, uid[i]); | ||||
|   } | ||||
|   RC522::setup(); | ||||
| } | ||||
|  | ||||
| void RC522::setup() { | ||||
|   spi_setup(); | ||||
|   initialize_pending_ = true; | ||||
|   // Pull device out of power down / reset state. | ||||
|  | ||||
|   // First set the resetPowerDownPin as digital input, to check the MFRC522 power down mode. | ||||
|   if (reset_pin_ != nullptr) { | ||||
|     reset_pin_->pin_mode(INPUT); | ||||
|  | ||||
|     if (reset_pin_->digital_read() == LOW) {  // The MFRC522 chip is in power down mode. | ||||
|       ESP_LOGV(TAG, "Power down mode detected. Hard resetting..."); | ||||
|       reset_pin_->pin_mode(OUTPUT);     // Now set the resetPowerDownPin as digital output. | ||||
|       reset_pin_->digital_write(LOW);   // Make sure we have a clean LOW state. | ||||
|       delayMicroseconds(2);             // 8.8.1 Reset timing requirements says about 100ns. Let us be generous: 2μsl | ||||
|       reset_pin_->digital_write(HIGH);  // Exit power down mode. This triggers a hard reset. | ||||
|       // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. | ||||
|       // Let us be generous: 50ms. | ||||
|       reset_timeout_ = millis(); | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Setup a soft reset | ||||
|   reset_count_ = RESET_COUNT; | ||||
|   reset_timeout_ = millis(); | ||||
| } | ||||
|  | ||||
| void RC522::initialize_() { | ||||
|   // Per originall code, wait 50 ms | ||||
|   if (millis() - reset_timeout_ < 50) | ||||
|     return; | ||||
|  | ||||
|   // Reset baud rates | ||||
|   ESP_LOGV(TAG, "Initialize"); | ||||
|  | ||||
|   pcd_write_register_(TX_MODE_REG, 0x00); | ||||
|   pcd_write_register_(RX_MODE_REG, 0x00); | ||||
|   // Reset ModWidthReg | ||||
|   pcd_write_register_(MOD_WIDTH_REG, 0x26); | ||||
|  | ||||
|   // When communicating with a PICC we need a timeout if something goes wrong. | ||||
|   // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. | ||||
|   // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. | ||||
|   pcd_write_register_(T_MODE_REG, 0x80);  // TAuto=1; timer starts automatically at the end of the transmission in all | ||||
|                                           // communication modes at all speeds | ||||
|  | ||||
|   // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25μs. | ||||
|   pcd_write_register_(T_PRESCALER_REG, 0xA9); | ||||
|   pcd_write_register_(T_RELOAD_REG_H, 0x03);  // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. | ||||
|   pcd_write_register_(T_RELOAD_REG_L, 0xE8); | ||||
|  | ||||
|   // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting | ||||
|   pcd_write_register_(TX_ASK_REG, 0x40); | ||||
|   pcd_write_register_(MODE_REG, 0x3D);  // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC | ||||
|                                         // command to 0x6363 (ISO 14443-3 part 6.2.4) | ||||
|   pcd_antenna_on_();                    // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) | ||||
|  | ||||
|   initialize_pending_ = false; | ||||
| } | ||||
|  | ||||
| void RC522::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "RC522:"); | ||||
|   switch (this->error_code_) { | ||||
|     case NONE: | ||||
|       break; | ||||
|     case RESET_FAILED: | ||||
|       ESP_LOGE(TAG, "Reset command failed!"); | ||||
|       break; | ||||
|   } | ||||
|  | ||||
| void RC522Spi::dump_config() { | ||||
|   RC522::dump_config(); | ||||
|   LOG_PIN("  CS Pin: ", this->cs_); | ||||
|   LOG_PIN("  RESET Pin: ", this->reset_pin_); | ||||
|  | ||||
|   LOG_UPDATE_INTERVAL(this); | ||||
|  | ||||
|   for (auto *child : this->binary_sensors_) { | ||||
|     LOG_BINARY_SENSOR("  ", "Tag", child); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void RC522::loop() { | ||||
|   // First check reset is needed | ||||
|   if (reset_count_ > 0) { | ||||
|     pcd_reset_(); | ||||
|     return; | ||||
|   } | ||||
|   if (initialize_pending_) { | ||||
|     initialize_(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (millis() - update_wait_ < this->update_interval_) | ||||
|     return; | ||||
|  | ||||
|   auto status = picc_is_new_card_present_(); | ||||
|  | ||||
|   if (status == STATUS_ERROR)  // No card | ||||
|   { | ||||
|     ESP_LOGE(TAG, "Error"); | ||||
|     // mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (status != STATUS_OK)  // We can receive STATUS_TIMEOUT when no card, or unexpected status. | ||||
|     return; | ||||
|  | ||||
|   // Try process card | ||||
|   if (!picc_read_card_serial_()) { | ||||
|     ESP_LOGW(TAG, "Requesting tag read failed!"); | ||||
|     return; | ||||
|   }; | ||||
|  | ||||
|   if (uid_.size < 4) { | ||||
|     return; | ||||
|     ESP_LOGW(TAG, "Read serial size: %d", uid_.size); | ||||
|   } | ||||
|  | ||||
|   update_wait_ = millis(); | ||||
|  | ||||
|   bool report = true; | ||||
|   // 1. Go through all triggers | ||||
|   for (auto *trigger : this->triggers_) | ||||
|     trigger->process(uid_.uiduint8_t, uid_.size); | ||||
|  | ||||
|   // 2. Find a binary sensor | ||||
|   for (auto *tag : this->binary_sensors_) { | ||||
|     if (tag->process(uid_.uiduint8_t, uid_.size)) { | ||||
|       // 2.1 if found, do not dump | ||||
|       report = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (report) { | ||||
|     char buf[32]; | ||||
|     format_uid(buf, uid_.uiduint8_t, uid_.size); | ||||
|     ESP_LOGD(TAG, "Found new tag '%s'", buf); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void RC522::update() { | ||||
|   for (auto *obj : this->binary_sensors_) | ||||
|     obj->on_scan_end(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. | ||||
|  */ | ||||
| void RC522::pcd_reset_() { | ||||
|   // The datasheet does not mention how long the SoftRest command takes to complete. | ||||
|   // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg) | ||||
|   // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. Let | ||||
|   // us be generous: 50ms. | ||||
|  | ||||
|   if (millis() - reset_timeout_ < 50) | ||||
|     return; | ||||
|  | ||||
|   if (reset_count_ == RESET_COUNT) { | ||||
|     ESP_LOGV(TAG, "Soft reset..."); | ||||
|     // Issue the SoftReset command. | ||||
|     pcd_write_register_(COMMAND_REG, PCD_SOFT_RESET); | ||||
|   } | ||||
|  | ||||
|   // Expect the PowerDown bit in CommandReg to be cleared (max 3x50ms) | ||||
|   if ((pcd_read_register_(COMMAND_REG) & (1 << 4)) == 0) { | ||||
|     reset_count_ = 0; | ||||
|     ESP_LOGI(TAG, "Device online."); | ||||
|     // Wait for initialize | ||||
|     reset_timeout_ = millis(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (--reset_count_ == 0) { | ||||
|     ESP_LOGE(TAG, "Unable to reset RC522."); | ||||
|     mark_failed(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Turns the antenna on by enabling pins TX1 and TX2. | ||||
|  * After a reset these pins are disabled. | ||||
|  */ | ||||
| void RC522::pcd_antenna_on_() { | ||||
|   uint8_t value = pcd_read_register_(TX_CONTROL_REG); | ||||
|   if ((value & 0x03) != 0x03) { | ||||
|     pcd_write_register_(TX_CONTROL_REG, value | 0x03); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Reads a uint8_t from the specified register in the MFRC522 chip. | ||||
|  * The interface is described in the datasheet section 8.1.2. | ||||
|  */ | ||||
| uint8_t RC522::pcd_read_register_(PcdRegister reg  ///< The register to read from. One of the PCD_Register enums. | ||||
| uint8_t RC522Spi::pcd_read_register(PcdRegister reg  ///< The register to read from. One of the PCD_Register enums. | ||||
| ) { | ||||
|   uint8_t value; | ||||
|   enable(); | ||||
|   transfer_byte(0x80 | reg); | ||||
|   value = read_byte(); | ||||
|   disable(); | ||||
|   ESP_LOGVV(TAG, "read_register_(%x) -> %x", reg, value); | ||||
|   ESP_LOGV(TAG, "read_register_(%x) -> %x", reg, value); | ||||
|   return value; | ||||
| } | ||||
|  | ||||
| @@ -228,10 +40,10 @@ uint8_t RC522::pcd_read_register_(PcdRegister reg  ///< The register to read fro | ||||
|  * Reads a number of uint8_ts from the specified register in the MFRC522 chip. | ||||
|  * The interface is described in the datasheet section 8.1.2. | ||||
|  */ | ||||
| void RC522::pcd_read_register_(PcdRegister reg,  ///< The register to read from. One of the PCD_Register enums. | ||||
|                                uint8_t count,    ///< The number of uint8_ts to read | ||||
|                                uint8_t *values,  ///< uint8_t array to store the values in. | ||||
|                                uint8_t rx_align  ///< Only bit positions rxAlign..7 in values[0] are updated. | ||||
| void RC522Spi::pcd_read_register(PcdRegister reg,  ///< The register to read from. One of the PCD_Register enums. | ||||
|                                  uint8_t count,    ///< The number of uint8_ts to read | ||||
|                                  uint8_t *values,  ///< uint8_t array to store the values in. | ||||
|                                  uint8_t rx_align  ///< Only bit positions rxAlign..7 in values[0] are updated. | ||||
| ) { | ||||
|   std::string buf; | ||||
|   buf = "Rx"; | ||||
| @@ -278,8 +90,8 @@ void RC522::pcd_read_register_(PcdRegister reg,  ///< The register to read from. | ||||
|   disable(); | ||||
| } | ||||
|  | ||||
| void RC522::pcd_write_register_(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | ||||
|                                 uint8_t value     ///< The value to write. | ||||
| void RC522Spi::pcd_write_register(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | ||||
|                                   uint8_t value     ///< The value to write. | ||||
| ) { | ||||
|   enable(); | ||||
|   // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. | ||||
| @@ -292,9 +104,9 @@ void RC522::pcd_write_register_(PcdRegister reg,  ///< The register to write to. | ||||
|  * Writes a number of uint8_ts to the specified register in the MFRC522 chip. | ||||
|  * The interface is described in the datasheet section 8.1.2. | ||||
|  */ | ||||
| void RC522::pcd_write_register_(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | ||||
|                                 uint8_t count,    ///< The number of uint8_ts to write to the register | ||||
|                                 uint8_t *values   ///< The values to write. uint8_t array. | ||||
| void RC522Spi::pcd_write_register(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | ||||
|                                   uint8_t count,    ///< The number of uint8_ts to write to the register | ||||
|                                   uint8_t *values   ///< The values to write. uint8_t array. | ||||
| ) { | ||||
|   std::string buf; | ||||
|   buf = "Tx"; | ||||
| @@ -313,545 +125,5 @@ void RC522::pcd_write_register_(PcdRegister reg,  ///< The register to write to. | ||||
|   ESP_LOGVV(TAG, "write_register_(%x, %d) -> %s", reg, count, buf.c_str()); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or | ||||
|  * selection. 7 bit frame. Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - | ||||
|  * probably due do bad antenna design. | ||||
|  * | ||||
|  * @return STATUS_OK on success, STATUS_??? otherwise. | ||||
|  */ | ||||
| RC522::StatusCode RC522::picc_request_a_( | ||||
|     uint8_t *buffer_atqa,  ///< The buffer to store the ATQA (Answer to request) in | ||||
|     uint8_t *buffer_size   ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. | ||||
| ) { | ||||
|   return picc_reqa_or_wupa_(PICC_CMD_REQA, buffer_atqa, buffer_size); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Transmits REQA or WUPA commands. | ||||
|  * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna | ||||
|  * design. | ||||
|  * | ||||
|  * @return STATUS_OK on success, STATUS_??? otherwise. | ||||
|  */ | ||||
| RC522::StatusCode RC522::picc_reqa_or_wupa_( | ||||
|     uint8_t command,       ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA | ||||
|     uint8_t *buffer_atqa,  ///< The buffer to store the ATQA (Answer to request) in | ||||
|     uint8_t *buffer_size   ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. | ||||
| ) { | ||||
|   uint8_t valid_bits; | ||||
|   RC522::StatusCode status; | ||||
|  | ||||
|   if (buffer_atqa == nullptr || *buffer_size < 2) {  // The ATQA response is 2 uint8_ts long. | ||||
|     return STATUS_NO_ROOM; | ||||
|   } | ||||
|   pcd_clear_register_bit_mask_(COLL_REG, 0x80);  // ValuesAfterColl=1 => Bits received after collision are cleared. | ||||
|   valid_bits = 7;  // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) | ||||
|                    // uint8_t. TxLastBits = BitFramingReg[2..0] | ||||
|   status = pcd_transceive_data_(&command, 1, buffer_atqa, buffer_size, &valid_bits); | ||||
|   if (status != STATUS_OK) | ||||
|     return status; | ||||
|   if (*buffer_size != 2 || valid_bits != 0) {  // ATQA must be exactly 16 bits. | ||||
|     ESP_LOGVV(TAG, "picc_reqa_or_wupa_() -> STATUS_ERROR"); | ||||
|     return STATUS_ERROR; | ||||
|   } | ||||
|  | ||||
|   return STATUS_OK; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Sets the bits given in mask in register reg. | ||||
|  */ | ||||
| void RC522::pcd_set_register_bit_mask_(PcdRegister reg,  ///< The register to update. One of the PCD_Register enums. | ||||
|                                        uint8_t mask      ///< The bits to set. | ||||
| ) { | ||||
|   uint8_t tmp = pcd_read_register_(reg); | ||||
|   pcd_write_register_(reg, tmp | mask);  // set bit mask | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Clears the bits given in mask from register reg. | ||||
|  */ | ||||
| void RC522::pcd_clear_register_bit_mask_(PcdRegister reg,  ///< The register to update. One of the PCD_Register enums. | ||||
|                                          uint8_t mask      ///< The bits to clear. | ||||
| ) { | ||||
|   uint8_t tmp = pcd_read_register_(reg); | ||||
|   pcd_write_register_(reg, tmp & (~mask));  // clear bit mask | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Executes the Transceive command. | ||||
|  * CRC validation can only be done if backData and backLen are specified. | ||||
|  * | ||||
|  * @return STATUS_OK on success, STATUS_??? otherwise. | ||||
|  */ | ||||
| RC522::StatusCode RC522::pcd_transceive_data_( | ||||
|     uint8_t *send_data,  ///< Pointer to the data to transfer to the FIFO. | ||||
|     uint8_t send_len,    ///< Number of uint8_ts to transfer to the FIFO. | ||||
|     uint8_t *back_data,  ///< nullptr or pointer to buffer if data should be read back after executing the command. | ||||
|     uint8_t *back_len,   ///< In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned. | ||||
|     uint8_t | ||||
|         *valid_bits,   ///< In/Out: The number of valid bits in the last uint8_t. 0 for 8 valid bits. Default nullptr. | ||||
|     uint8_t rx_align,  ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. | ||||
|     bool check_crc     ///< In: True => The last two uint8_ts of the response is assumed to be a CRC_A that must be | ||||
|                        ///< validated. | ||||
| ) { | ||||
|   uint8_t wait_i_rq = 0x30;  // RxIRq and IdleIRq | ||||
|   auto ret = pcd_communicate_with_picc_(PCD_TRANSCEIVE, wait_i_rq, send_data, send_len, back_data, back_len, valid_bits, | ||||
|                                         rx_align, check_crc); | ||||
|  | ||||
|   if (ret == STATUS_OK && *back_len == 5) | ||||
|     ESP_LOGVV(TAG, "pcd_transceive_data_(..., %d,  ) -> %d [%x, %x, %x, %x, %x]", send_len, ret, back_data[0], | ||||
|               back_data[1], back_data[2], back_data[3], back_data[4]); | ||||
|   else | ||||
|     ESP_LOGVV(TAG, "pcd_transceive_data_(..., %d, ... ) -> %d", send_len, ret); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO. | ||||
|  * CRC validation can only be done if backData and backLen are specified. | ||||
|  * | ||||
|  * @return STATUS_OK on success, STATUS_??? otherwise. | ||||
|  */ | ||||
| RC522::StatusCode RC522::pcd_communicate_with_picc_( | ||||
|     uint8_t command,      ///< The command to execute. One of the PCD_Command enums. | ||||
|     uint8_t wait_i_rq,    ///< The bits in the ComIrqReg register that signals successful completion of the command. | ||||
|     uint8_t *send_data,   ///< Pointer to the data to transfer to the FIFO. | ||||
|     uint8_t send_len,     ///< Number of uint8_ts to transfer to the FIFO. | ||||
|     uint8_t *back_data,   ///< nullptr or pointer to buffer if data should be read back after executing the command. | ||||
|     uint8_t *back_len,    ///< In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned. | ||||
|     uint8_t *valid_bits,  ///< In/Out: The number of valid bits in the last uint8_t. 0 for 8 valid bits. | ||||
|     uint8_t rx_align,     ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. | ||||
|     bool check_crc        ///< In: True => The last two uint8_ts of the response is assumed to be a CRC_A that must be | ||||
|                           ///< validated. | ||||
| ) { | ||||
|   ESP_LOGVV(TAG, "pcd_communicate_with_picc_(%d, %d,... %d)", command, wait_i_rq, check_crc); | ||||
|  | ||||
|   // Prepare values for BitFramingReg | ||||
|   uint8_t tx_last_bits = valid_bits ? *valid_bits : 0; | ||||
|   uint8_t bit_framing = | ||||
|       (rx_align << 4) + tx_last_bits;  // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] | ||||
|  | ||||
|   pcd_write_register_(COMMAND_REG, PCD_IDLE);               // Stop any active command. | ||||
|   pcd_write_register_(COM_IRQ_REG, 0x7F);                   // Clear all seven interrupt request bits | ||||
|   pcd_write_register_(FIFO_LEVEL_REG, 0x80);                // FlushBuffer = 1, FIFO initialization | ||||
|   pcd_write_register_(FIFO_DATA_REG, send_len, send_data);  // Write sendData to the FIFO | ||||
|   pcd_write_register_(BIT_FRAMING_REG, bit_framing);        // Bit adjustments | ||||
|   pcd_write_register_(COMMAND_REG, command);                // Execute the command | ||||
|   if (command == PCD_TRANSCEIVE) { | ||||
|     pcd_set_register_bit_mask_(BIT_FRAMING_REG, 0x80);  // StartSend=1, transmission of data starts | ||||
|   } | ||||
|  | ||||
|   // Wait for the command to complete. | ||||
|   // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops | ||||
|   // transmitting. Each iteration of the do-while-loop takes 17.86μs. | ||||
|   // TODO check/modify for other architectures than Arduino Uno 16bit | ||||
|   uint16_t i; | ||||
|   for (i = 2000; i > 0; i--) { | ||||
|     uint8_t n = pcd_read_register_( | ||||
|         COM_IRQ_REG);     // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq | ||||
|     if (n & wait_i_rq) {  // One of the interrupts that signal success has been set. | ||||
|       break; | ||||
|     } | ||||
|     if (n & 0x01) {  // Timer interrupt - nothing received in 25ms | ||||
|       return STATUS_TIMEOUT; | ||||
|     } | ||||
|   } | ||||
|   // 35.7ms and nothing happend. Communication with the MFRC522 might be down. | ||||
|   if (i == 0) { | ||||
|     return STATUS_TIMEOUT; | ||||
|   } | ||||
|  | ||||
|   // Stop now if any errors except collisions were detected. | ||||
|   uint8_t error_reg_value = pcd_read_register_( | ||||
|       ERROR_REG);  // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr | ||||
|   if (error_reg_value & 0x13) {  // BufferOvfl ParityErr ProtocolErr | ||||
|     return STATUS_ERROR; | ||||
|   } | ||||
|  | ||||
|   uint8_t valid_bits_local = 0; | ||||
|  | ||||
|   // If the caller wants data back, get it from the MFRC522. | ||||
|   if (back_data && back_len) { | ||||
|     uint8_t n = pcd_read_register_(FIFO_LEVEL_REG);  // Number of uint8_ts in the FIFO | ||||
|     if (n > *back_len) { | ||||
|       return STATUS_NO_ROOM; | ||||
|     } | ||||
|     *back_len = n;                                              // Number of uint8_ts returned | ||||
|     pcd_read_register_(FIFO_DATA_REG, n, back_data, rx_align);  // Get received data from FIFO | ||||
|     valid_bits_local = | ||||
|         pcd_read_register_(CONTROL_REG) & 0x07;  // RxLastBits[2:0] indicates the number of valid bits in the last | ||||
|                                                  // received uint8_t. If this value is 000b, the whole uint8_t is valid. | ||||
|     if (valid_bits) { | ||||
|       *valid_bits = valid_bits_local; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Tell about collisions | ||||
|   if (error_reg_value & 0x08) {  // CollErr | ||||
|     return STATUS_COLLISION; | ||||
|   } | ||||
|  | ||||
|   // Perform CRC_A validation if requested. | ||||
|   if (back_data && back_len && check_crc) { | ||||
|     // In this case a MIFARE Classic NAK is not OK. | ||||
|     if (*back_len == 1 && valid_bits_local == 4) { | ||||
|       return STATUS_MIFARE_NACK; | ||||
|     } | ||||
|     // We need at least the CRC_A value and all 8 bits of the last uint8_t must be received. | ||||
|     if (*back_len < 2 || valid_bits_local != 0) { | ||||
|       return STATUS_CRC_WRONG; | ||||
|     } | ||||
|     // Verify CRC_A - do our own calculation and store the control in controlBuffer. | ||||
|     uint8_t control_buffer[2]; | ||||
|     RC522::StatusCode status = pcd_calculate_crc_(&back_data[0], *back_len - 2, &control_buffer[0]); | ||||
|     if (status != STATUS_OK) { | ||||
|       return status; | ||||
|     } | ||||
|     if ((back_data[*back_len - 2] != control_buffer[0]) || (back_data[*back_len - 1] != control_buffer[1])) { | ||||
|       return STATUS_CRC_WRONG; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return STATUS_OK; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. | ||||
|  * | ||||
|  * @return STATUS_OK on success, STATUS_??? otherwise. | ||||
|  */ | ||||
|  | ||||
| RC522::StatusCode RC522::pcd_calculate_crc_( | ||||
|     uint8_t *data,   ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. | ||||
|     uint8_t length,  ///< In: The number of uint8_ts to transfer. | ||||
|     uint8_t *result  ///< Out: Pointer to result buffer. Result is written to result[0..1], low uint8_t first. | ||||
| ) { | ||||
|   ESP_LOGVV(TAG, "pcd_calculate_crc_(..., %d, ...)", length); | ||||
|   pcd_write_register_(COMMAND_REG, PCD_IDLE);        // Stop any active command. | ||||
|   pcd_write_register_(DIV_IRQ_REG, 0x04);            // Clear the CRCIRq interrupt request bit | ||||
|   pcd_write_register_(FIFO_LEVEL_REG, 0x80);         // FlushBuffer = 1, FIFO initialization | ||||
|   pcd_write_register_(FIFO_DATA_REG, length, data);  // Write data to the FIFO | ||||
|   pcd_write_register_(COMMAND_REG, PCD_CALC_CRC);    // Start the calculation | ||||
|  | ||||
|   // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73μs. | ||||
|   // TODO check/modify for other architectures than Arduino Uno 16bit | ||||
|  | ||||
|   // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73us. | ||||
|   for (uint16_t i = 5000; i > 0; i--) { | ||||
|     // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved | ||||
|     uint8_t n = pcd_read_register_(DIV_IRQ_REG); | ||||
|     if (n & 0x04) {                                // CRCIRq bit set - calculation done | ||||
|       pcd_write_register_(COMMAND_REG, PCD_IDLE);  // Stop calculating CRC for new content in the FIFO. | ||||
|       // Transfer the result from the registers to the result buffer | ||||
|       result[0] = pcd_read_register_(CRC_RESULT_REG_L); | ||||
|       result[1] = pcd_read_register_(CRC_RESULT_REG_H); | ||||
|  | ||||
|       ESP_LOGVV(TAG, "pcd_calculate_crc_() STATUS_OK"); | ||||
|       return STATUS_OK; | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGVV(TAG, "pcd_calculate_crc_() TIMEOUT"); | ||||
|   // 89ms passed and nothing happend. Communication with the MFRC522 might be down. | ||||
|   return STATUS_TIMEOUT; | ||||
| } | ||||
| /** | ||||
|  * Returns STATUS_OK if a PICC responds to PICC_CMD_REQA. | ||||
|  * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. | ||||
|  * | ||||
|  * @return  STATUS_OK on success, STATUS_??? otherwise. | ||||
|  */ | ||||
|  | ||||
| RC522::StatusCode RC522::picc_is_new_card_present_() { | ||||
|   uint8_t buffer_atqa[2]; | ||||
|   uint8_t buffer_size = sizeof(buffer_atqa); | ||||
|  | ||||
|   // Reset baud rates | ||||
|   pcd_write_register_(TX_MODE_REG, 0x00); | ||||
|   pcd_write_register_(RX_MODE_REG, 0x00); | ||||
|   // Reset ModWidthReg | ||||
|   pcd_write_register_(MOD_WIDTH_REG, 0x26); | ||||
|  | ||||
|   auto result = picc_request_a_(buffer_atqa, &buffer_size); | ||||
|  | ||||
|   ESP_LOGV(TAG, "picc_is_new_card_present_() -> %d", result); | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Simple wrapper around PICC_Select. | ||||
|  * Returns true if a UID could be read. | ||||
|  * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first. | ||||
|  * The read UID is available in the class variable uid. | ||||
|  * | ||||
|  * @return bool | ||||
|  */ | ||||
| bool RC522::picc_read_card_serial_() { | ||||
|   RC522::StatusCode result = picc_select_(&this->uid_); | ||||
|   ESP_LOGVV(TAG, "picc_select_(...) -> %d", result); | ||||
|   return (result == STATUS_OK); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Transmits SELECT/ANTICOLLISION commands to select a single PICC. | ||||
|  * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or | ||||
|  * PICC_WakeupA(). On success: | ||||
|  *     - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the | ||||
|  * ISO/IEC 14443-3 draft.) | ||||
|  *     - The UID size and value of the chosen PICC is returned in *uid along with the SAK. | ||||
|  * | ||||
|  * A PICC UID consists of 4, 7 or 10 uint8_ts. | ||||
|  * Only 4 uint8_ts can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: | ||||
|  *     UID size  Number of UID uint8_ts    Cascade levels    Example of PICC | ||||
|  *     ========  ===================    ==============    =============== | ||||
|  *     single         4            1        MIFARE Classic | ||||
|  *     double         7            2        MIFARE Ultralight | ||||
|  *     triple        10            3        Not currently in use? | ||||
|  * | ||||
|  * @return STATUS_OK on success, STATUS_??? otherwise. | ||||
|  */ | ||||
| RC522::StatusCode RC522::picc_select_( | ||||
|     Uid *uid,           ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. | ||||
|     uint8_t valid_bits  ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply | ||||
|                         ///< uid->size. | ||||
| ) { | ||||
|   bool uid_complete; | ||||
|   bool select_done; | ||||
|   bool use_cascade_tag; | ||||
|   uint8_t cascade_level = 1; | ||||
|   RC522::StatusCode result; | ||||
|   uint8_t count; | ||||
|   uint8_t check_bit; | ||||
|   uint8_t index; | ||||
|   uint8_t uid_index;                // The first index in uid->uiduint8_t[] that is used in the current Cascade Level. | ||||
|   int8_t current_level_known_bits;  // The number of known UID bits in the current Cascade Level. | ||||
|   uint8_t buffer[9];    // The SELECT/ANTICOLLISION commands uses a 7 uint8_t standard frame + 2 uint8_ts CRC_A | ||||
|   uint8_t buffer_used;  // The number of uint8_ts used in the buffer, ie the number of uint8_ts to transfer to the FIFO. | ||||
|   uint8_t rx_align;     // Used in BitFramingReg. Defines the bit position for the first bit received. | ||||
|   uint8_t tx_last_bits;  // Used in BitFramingReg. The number of valid bits in the last transmitted uint8_t. | ||||
|   uint8_t *response_buffer; | ||||
|   uint8_t response_length; | ||||
|  | ||||
|   // Description of buffer structure: | ||||
|   //    uint8_t 0: SEL         Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 | ||||
|   //    uint8_t 1: NVB          Number of Valid Bits (in complete command, not just the UID): High nibble: complete | ||||
|   // uint8_ts, | ||||
|   // Low nibble: Extra bits.     uint8_t 2: UID-data or CT    See explanation below. CT means Cascade Tag.     uint8_t | ||||
|   // 3: UID-data uint8_t 4: UID-data     uint8_t 5: UID-data     uint8_t 6: BCC          Block Check Character - XOR of | ||||
|   // uint8_ts 2-5 uint8_t 7: CRC_A uint8_t 8: CRC_A The BCC and CRC_A are only transmitted if we know all the UID bits | ||||
|   // of the current Cascade Level. | ||||
|   // | ||||
|   // Description of uint8_ts 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) | ||||
|   //    UID size  Cascade level  uint8_t2  uint8_t3  uint8_t4  uint8_t5 | ||||
|   //    ========  =============  =====  =====  =====  ===== | ||||
|   //     4 uint8_ts    1      uid0  uid1  uid2  uid3 | ||||
|   //     7 uint8_ts    1      CT    uid0  uid1  uid2 | ||||
|   //            2      uid3  uid4  uid5  uid6 | ||||
|   //    10 uint8_ts    1      CT    uid0  uid1  uid2 | ||||
|   //            2      CT    uid3  uid4  uid5 | ||||
|   //            3      uid6  uid7  uid8  uid9 | ||||
|  | ||||
|   // Sanity checks | ||||
|   if (valid_bits > 80) { | ||||
|     return STATUS_INVALID; | ||||
|   } | ||||
|  | ||||
|   ESP_LOGVV(TAG, "picc_select_(&, %d)", valid_bits); | ||||
|  | ||||
|   // Prepare MFRC522 | ||||
|   pcd_clear_register_bit_mask_(COLL_REG, 0x80);  // ValuesAfterColl=1 => Bits received after collision are cleared. | ||||
|  | ||||
|   // Repeat Cascade Level loop until we have a complete UID. | ||||
|   uid_complete = false; | ||||
|   while (!uid_complete) { | ||||
|     // Set the Cascade Level in the SEL uint8_t, find out if we need to use the Cascade Tag in uint8_t 2. | ||||
|     switch (cascade_level) { | ||||
|       case 1: | ||||
|         buffer[0] = PICC_CMD_SEL_CL1; | ||||
|         uid_index = 0; | ||||
|         use_cascade_tag = valid_bits && uid->size > 4;  // When we know that the UID has more than 4 uint8_ts | ||||
|         break; | ||||
|  | ||||
|       case 2: | ||||
|         buffer[0] = PICC_CMD_SEL_CL2; | ||||
|         uid_index = 3; | ||||
|         use_cascade_tag = valid_bits && uid->size > 7;  // When we know that the UID has more than 7 uint8_ts | ||||
|         break; | ||||
|  | ||||
|       case 3: | ||||
|         buffer[0] = PICC_CMD_SEL_CL3; | ||||
|         uid_index = 6; | ||||
|         use_cascade_tag = false;  // Never used in CL3. | ||||
|         break; | ||||
|  | ||||
|       default: | ||||
|         return STATUS_INTERNAL_ERROR; | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     // How many UID bits are known in this Cascade Level? | ||||
|     current_level_known_bits = valid_bits - (8 * uid_index); | ||||
|     if (current_level_known_bits < 0) { | ||||
|       current_level_known_bits = 0; | ||||
|     } | ||||
|     // Copy the known bits from uid->uiduint8_t[] to buffer[] | ||||
|     index = 2;  // destination index in buffer[] | ||||
|     if (use_cascade_tag) { | ||||
|       buffer[index++] = PICC_CMD_CT; | ||||
|     } | ||||
|     uint8_t uint8_ts_to_copy = current_level_known_bits / 8 + | ||||
|                                (current_level_known_bits % 8 | ||||
|                                     ? 1 | ||||
|                                     : 0);  // The number of uint8_ts needed to represent the known bits for this level. | ||||
|     if (uint8_ts_to_copy) { | ||||
|       uint8_t maxuint8_ts = | ||||
|           use_cascade_tag ? 3 : 4;  // Max 4 uint8_ts in each Cascade Level. Only 3 left if we use the Cascade Tag | ||||
|       if (uint8_ts_to_copy > maxuint8_ts) { | ||||
|         uint8_ts_to_copy = maxuint8_ts; | ||||
|       } | ||||
|       for (count = 0; count < uint8_ts_to_copy; count++) { | ||||
|         buffer[index++] = uid->uiduint8_t[uid_index + count]; | ||||
|       } | ||||
|     } | ||||
|     // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits | ||||
|     if (use_cascade_tag) { | ||||
|       current_level_known_bits += 8; | ||||
|     } | ||||
|  | ||||
|     // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. | ||||
|     select_done = false; | ||||
|     while (!select_done) { | ||||
|       // Find out how many bits and uint8_ts to send and receive. | ||||
|       if (current_level_known_bits >= 32) {  // All UID bits in this Cascade Level are known. This is a SELECT. | ||||
|  | ||||
|         if (response_length < 4) { | ||||
|           ESP_LOGW(TAG, "Not enough data received."); | ||||
|           return STATUS_INVALID; | ||||
|         } | ||||
|  | ||||
|         // Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); | ||||
|         buffer[1] = 0x70;  // NVB - Number of Valid Bits: Seven whole uint8_ts | ||||
|         // Calculate BCC - Block Check Character | ||||
|         buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; | ||||
|         // Calculate CRC_A | ||||
|         result = pcd_calculate_crc_(buffer, 7, &buffer[7]); | ||||
|         if (result != STATUS_OK) { | ||||
|           return result; | ||||
|         } | ||||
|         tx_last_bits = 0;  // 0 => All 8 bits are valid. | ||||
|         buffer_used = 9; | ||||
|         // Store response in the last 3 uint8_ts of buffer (BCC and CRC_A - not needed after tx) | ||||
|         response_buffer = &buffer[6]; | ||||
|         response_length = 3; | ||||
|       } else {  // This is an ANTICOLLISION. | ||||
|         // Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); | ||||
|         tx_last_bits = current_level_known_bits % 8; | ||||
|         count = current_level_known_bits / 8;     // Number of whole uint8_ts in the UID part. | ||||
|         index = 2 + count;                        // Number of whole uint8_ts: SEL + NVB + UIDs | ||||
|         buffer[1] = (index << 4) + tx_last_bits;  // NVB - Number of Valid Bits | ||||
|         buffer_used = index + (tx_last_bits ? 1 : 0); | ||||
|         // Store response in the unused part of buffer | ||||
|         response_buffer = &buffer[index]; | ||||
|         response_length = sizeof(buffer) - index; | ||||
|       } | ||||
|  | ||||
|       // Set bit adjustments | ||||
|       rx_align = tx_last_bits;  // Having a separate variable is overkill. But it makes the next line easier to read. | ||||
|       pcd_write_register_( | ||||
|           BIT_FRAMING_REG, | ||||
|           (rx_align << 4) + tx_last_bits);  // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] | ||||
|  | ||||
|       // Transmit the buffer and receive the response. | ||||
|       result = pcd_transceive_data_(buffer, buffer_used, response_buffer, &response_length, &tx_last_bits, rx_align); | ||||
|       if (result == STATUS_COLLISION) {  // More than one PICC in the field => collision. | ||||
|         uint8_t value_of_coll_reg = pcd_read_register_( | ||||
|             COLL_REG);  // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] | ||||
|         if (value_of_coll_reg & 0x20) {  // CollPosNotValid | ||||
|           return STATUS_COLLISION;       // Without a valid collision position we cannot continue | ||||
|         } | ||||
|         uint8_t collision_pos = value_of_coll_reg & 0x1F;  // Values 0-31, 0 means bit 32. | ||||
|         if (collision_pos == 0) { | ||||
|           collision_pos = 32; | ||||
|         } | ||||
|         if (collision_pos <= current_level_known_bits) {  // No progress - should not happen | ||||
|           return STATUS_INTERNAL_ERROR; | ||||
|         } | ||||
|         // Choose the PICC with the bit set. | ||||
|         current_level_known_bits = collision_pos; | ||||
|         count = current_level_known_bits % 8;  // The bit to modify | ||||
|         check_bit = (current_level_known_bits - 1) % 8; | ||||
|         index = 1 + (current_level_known_bits / 8) + (count ? 1 : 0);  // First uint8_t is index 0. | ||||
|         if (response_length > 2)  // Note: Otherwise buffer[index] might be not initialized | ||||
|           buffer[index] |= (1 << check_bit); | ||||
|       } else if (result != STATUS_OK) { | ||||
|         return result; | ||||
|       } else {                                 // STATUS_OK | ||||
|         if (current_level_known_bits >= 32) {  // This was a SELECT. | ||||
|           select_done = true;                  // No more anticollision | ||||
|                                                // We continue below outside the while. | ||||
|         } else {                               // This was an ANTICOLLISION. | ||||
|           // We now have all 32 bits of the UID in this Cascade Level | ||||
|           current_level_known_bits = 32; | ||||
|           // Run loop again to do the SELECT. | ||||
|         } | ||||
|       } | ||||
|     }  // End of while (!selectDone) | ||||
|  | ||||
|     // We do not check the CBB - it was constructed by us above. | ||||
|  | ||||
|     // Copy the found UID uint8_ts from buffer[] to uid->uiduint8_t[] | ||||
|     index = (buffer[2] == PICC_CMD_CT) ? 3 : 2;  // source index in buffer[] | ||||
|     uint8_ts_to_copy = (buffer[2] == PICC_CMD_CT) ? 3 : 4; | ||||
|     for (count = 0; count < uint8_ts_to_copy; count++) { | ||||
|       uid->uiduint8_t[uid_index + count] = buffer[index++]; | ||||
|     } | ||||
|  | ||||
|     // Check response SAK (Select Acknowledge) | ||||
|     if (response_length != 3 || tx_last_bits != 0) {  // SAK must be exactly 24 bits (1 uint8_t + CRC_A). | ||||
|       return STATUS_ERROR; | ||||
|     } | ||||
|     // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those uint8_ts are not needed | ||||
|     // anymore. | ||||
|     result = pcd_calculate_crc_(response_buffer, 1, &buffer[2]); | ||||
|     if (result != STATUS_OK) { | ||||
|       return result; | ||||
|     } | ||||
|     if ((buffer[2] != response_buffer[1]) || (buffer[3] != response_buffer[2])) { | ||||
|       return STATUS_CRC_WRONG; | ||||
|     } | ||||
|     if (response_buffer[0] & 0x04) {  // Cascade bit set - UID not complete yes | ||||
|       cascade_level++; | ||||
|     } else { | ||||
|       uid_complete = true; | ||||
|       uid->sak = response_buffer[0]; | ||||
|     } | ||||
|   }  // End of while (!uidComplete) | ||||
|  | ||||
|   // Set correct uid->size | ||||
|   uid->size = 3 * cascade_level + 1; | ||||
|  | ||||
|   return STATUS_OK; | ||||
| } | ||||
|  | ||||
| bool RC522BinarySensor::process(const uint8_t *data, uint8_t len) { | ||||
|   if (len != this->uid_.size()) | ||||
|     return false; | ||||
|  | ||||
|   for (uint8_t i = 0; i < len; i++) { | ||||
|     if (data[i] != this->uid_[i]) | ||||
|       return false; | ||||
|   } | ||||
|  | ||||
|   this->publish_state(true); | ||||
|   this->found_ = true; | ||||
|   return true; | ||||
| } | ||||
| void RC522Trigger::process(const uint8_t *uid, uint8_t uid_length) { | ||||
|   char buf[32]; | ||||
|   format_uid(buf, uid, uid_length); | ||||
|   this->trigger(std::string(buf)); | ||||
| } | ||||
|  | ||||
| }  // namespace rc522_spi | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -10,292 +10,46 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/components/binary_sensor/binary_sensor.h" | ||||
| #include "esphome/components/rc522/rc522.h" | ||||
| #include "esphome/components/spi/spi.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace rc522_spi { | ||||
|  | ||||
| class RC522BinarySensor; | ||||
| class RC522Trigger; | ||||
|  | ||||
| class RC522 : public PollingComponent, | ||||
|               public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING, | ||||
|                                     spi::DATA_RATE_4MHZ> { | ||||
| class RC522Spi : public rc522::RC522, | ||||
|                  public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING, | ||||
|                                        spi::DATA_RATE_4MHZ> { | ||||
|  public: | ||||
|   void setup() override; | ||||
|  | ||||
|   void dump_config() override; | ||||
|  | ||||
|   void update() override; | ||||
|   float get_setup_priority() const override { return setup_priority::DATA; }; | ||||
|  | ||||
|   void loop() override; | ||||
|  | ||||
|   void register_tag(RC522BinarySensor *tag) { this->binary_sensors_.push_back(tag); } | ||||
|   void register_trigger(RC522Trigger *trig) { this->triggers_.push_back(trig); } | ||||
|  | ||||
|   void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; } | ||||
|  | ||||
|  protected: | ||||
|   enum PcdRegister : uint8_t { | ||||
|     // Page 0: Command and status | ||||
|     // 0x00      // reserved for future use | ||||
|     COMMAND_REG = 0x01 << 1,      // starts and stops command execution | ||||
|     COM_I_EN_REG = 0x02 << 1,     // enable and disable interrupt request control bits | ||||
|     DIV_I_EN_REG = 0x03 << 1,     // enable and disable interrupt request control bits | ||||
|     COM_IRQ_REG = 0x04 << 1,      // interrupt request bits | ||||
|     DIV_IRQ_REG = 0x05 << 1,      // interrupt request bits | ||||
|     ERROR_REG = 0x06 << 1,        // error bits showing the error status of the last command executed | ||||
|     STATUS1_REG = 0x07 << 1,      // communication status bits | ||||
|     STATUS2_REG = 0x08 << 1,      // receiver and transmitter status bits | ||||
|     FIFO_DATA_REG = 0x09 << 1,    // input and output of 64 uint8_t FIFO buffer | ||||
|     FIFO_LEVEL_REG = 0x0A << 1,   // number of uint8_ts stored in the FIFO buffer | ||||
|     WATER_LEVEL_REG = 0x0B << 1,  // level for FIFO underflow and overflow warning | ||||
|     CONTROL_REG = 0x0C << 1,      // miscellaneous control registers | ||||
|     BIT_FRAMING_REG = 0x0D << 1,  // adjustments for bit-oriented frames | ||||
|     COLL_REG = 0x0E << 1,         // bit position of the first bit-collision detected on the RF interface | ||||
|     //                 0x0F     // reserved for future use | ||||
|  | ||||
|     // Page 1: Command | ||||
|     //               0x10      // reserved for future use | ||||
|     MODE_REG = 0x11 << 1,          // defines general modes for transmitting and receiving | ||||
|     TX_MODE_REG = 0x12 << 1,       // defines transmission data rate and framing | ||||
|     RX_MODE_REG = 0x13 << 1,       // defines reception data rate and framing | ||||
|     TX_CONTROL_REG = 0x14 << 1,    // controls the logical behavior of the antenna driver pins TX1 and TX2 | ||||
|     TX_ASK_REG = 0x15 << 1,        // controls the setting of the transmission modulation | ||||
|     TX_SEL_REG = 0x16 << 1,        // selects the internal sources for the antenna driver | ||||
|     RX_SEL_REG = 0x17 << 1,        // selects internal receiver settings | ||||
|     RX_THRESHOLD_REG = 0x18 << 1,  // selects thresholds for the bit decoder | ||||
|     DEMOD_REG = 0x19 << 1,         // defines demodulator settings | ||||
|     //               0x1A      // reserved for future use | ||||
|     //               0x1B      // reserved for future use | ||||
|     MF_TX_REG = 0x1C << 1,  // controls some MIFARE communication transmit parameters | ||||
|     MF_RX_REG = 0x1D << 1,  // controls some MIFARE communication receive parameters | ||||
|     //               0x1E      // reserved for future use | ||||
|     SERIAL_SPEED_REG = 0x1F << 1,  // selects the speed of the serial UART interface | ||||
|  | ||||
|     // Page 2: Configuration | ||||
|     //               0x20      // reserved for future use | ||||
|     CRC_RESULT_REG_H = 0x21 << 1,  // shows the MSB and LSB values of the CRC calculation | ||||
|     CRC_RESULT_REG_L = 0x22 << 1, | ||||
|     //               0x23      // reserved for future use | ||||
|     MOD_WIDTH_REG = 0x24 << 1,  // controls the ModWidth setting? | ||||
|     //               0x25      // reserved for future use | ||||
|     RF_CFG_REG = 0x26 << 1,       // configures the receiver gain | ||||
|     GS_N_REG = 0x27 << 1,         // selects the conductance of the antenna driver pins TX1 and TX2 for modulation | ||||
|     CW_GS_P_REG = 0x28 << 1,      // defines the conductance of the p-driver output during periods of no modulation | ||||
|     MOD_GS_P_REG = 0x29 << 1,     // defines the conductance of the p-driver output during periods of modulation | ||||
|     T_MODE_REG = 0x2A << 1,       // defines settings for the internal timer | ||||
|     T_PRESCALER_REG = 0x2B << 1,  // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg. | ||||
|     T_RELOAD_REG_H = 0x2C << 1,   // defines the 16-bit timer reload value | ||||
|     T_RELOAD_REG_L = 0x2D << 1, | ||||
|     T_COUNTER_VALUE_REG_H = 0x2E << 1,  // shows the 16-bit timer value | ||||
|     T_COUNTER_VALUE_REG_L = 0x2F << 1, | ||||
|  | ||||
|     // Page 3: Test Registers | ||||
|     //               0x30      // reserved for future use | ||||
|     TEST_SEL1_REG = 0x31 << 1,       // general test signal configuration | ||||
|     TEST_SEL2_REG = 0x32 << 1,       // general test signal configuration | ||||
|     TEST_PIN_EN_REG = 0x33 << 1,     // enables pin output driver on pins D1 to D7 | ||||
|     TEST_PIN_VALUE_REG = 0x34 << 1,  // defines the values for D1 to D7 when it is used as an I/O bus | ||||
|     TEST_BUS_REG = 0x35 << 1,        // shows the status of the internal test bus | ||||
|     AUTO_TEST_REG = 0x36 << 1,       // controls the digital self-test | ||||
|     VERSION_REG = 0x37 << 1,         // shows the software version | ||||
|     ANALOG_TEST_REG = 0x38 << 1,     // controls the pins AUX1 and AUX2 | ||||
|     TEST_DA_C1_REG = 0x39 << 1,      // defines the test value for TestDAC1 | ||||
|     TEST_DA_C2_REG = 0x3A << 1,      // defines the test value for TestDAC2 | ||||
|     TEST_ADC_REG = 0x3B << 1         // shows the value of ADC I and Q channels | ||||
|                                      //               0x3C      // reserved for production tests | ||||
|                                      //               0x3D      // reserved for production tests | ||||
|                                      //               0x3E      // reserved for production tests | ||||
|                                      //               0x3F      // reserved for production tests | ||||
|   }; | ||||
|  | ||||
|   // MFRC522 commands. Described in chapter 10 of the datasheet. | ||||
|   enum PcdCommand : uint8_t { | ||||
|     PCD_IDLE = 0x00,                // no action, cancels current command execution | ||||
|     PCD_MEM = 0x01,                 // stores 25 uint8_ts into the internal buffer | ||||
|     PCD_GENERATE_RANDOM_ID = 0x02,  // generates a 10-uint8_t random ID number | ||||
|     PCD_CALC_CRC = 0x03,            // activates the CRC coprocessor or performs a self-test | ||||
|     PCD_TRANSMIT = 0x04,            // transmits data from the FIFO buffer | ||||
|     PCD_NO_CMD_CHANGE = 0x07,       // no command change, can be used to modify the CommandReg register bits without | ||||
|                                     // affecting the command, for example, the PowerDown bit | ||||
|     PCD_RECEIVE = 0x08,             // activates the receiver circuits | ||||
|     PCD_TRANSCEIVE = | ||||
|         0x0C,  // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission | ||||
|     PCD_MF_AUTHENT = 0x0E,  // performs the MIFARE standard authentication as a reader | ||||
|     PCD_SOFT_RESET = 0x0F   // resets the MFRC522 | ||||
|   }; | ||||
|  | ||||
|   // Commands sent to the PICC. | ||||
|   enum PiccCommand : uint8_t { | ||||
|     // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) | ||||
|     PICC_CMD_REQA = 0x26,     // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for | ||||
|                               // anticollision or selection. 7 bit frame. | ||||
|     PICC_CMD_WUPA = 0x52,     // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and | ||||
|                               // prepare for anticollision or selection. 7 bit frame. | ||||
|     PICC_CMD_CT = 0x88,       // Cascade Tag. Not really a command, but used during anti collision. | ||||
|     PICC_CMD_SEL_CL1 = 0x93,  // Anti collision/Select, Cascade Level 1 | ||||
|     PICC_CMD_SEL_CL2 = 0x95,  // Anti collision/Select, Cascade Level 2 | ||||
|     PICC_CMD_SEL_CL3 = 0x97,  // Anti collision/Select, Cascade Level 3 | ||||
|     PICC_CMD_HLTA = 0x50,     // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. | ||||
|     PICC_CMD_RATS = 0xE0,     // Request command for Answer To Reset. | ||||
|     // The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9) | ||||
|     // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on | ||||
|     // the sector. | ||||
|     // The read/write commands can also be used for MIFARE Ultralight. | ||||
|     PICC_CMD_MF_AUTH_KEY_A = 0x60,  // Perform authentication with Key A | ||||
|     PICC_CMD_MF_AUTH_KEY_B = 0x61,  // Perform authentication with Key B | ||||
|     PICC_CMD_MF_READ = | ||||
|         0x30,  // Reads one 16 uint8_t block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. | ||||
|     PICC_CMD_MF_WRITE = 0xA0,  // Writes one 16 uint8_t block to the authenticated sector of the PICC. Called | ||||
|                                // "COMPATIBILITY WRITE" for MIFARE Ultralight. | ||||
|     PICC_CMD_MF_DECREMENT = | ||||
|         0xC0,  // Decrements the contents of a block and stores the result in the internal data register. | ||||
|     PICC_CMD_MF_INCREMENT = | ||||
|         0xC1,  // Increments the contents of a block and stores the result in the internal data register. | ||||
|     PICC_CMD_MF_RESTORE = 0xC2,   // Reads the contents of a block into the internal data register. | ||||
|     PICC_CMD_MF_TRANSFER = 0xB0,  // Writes the contents of the internal data register to a block. | ||||
|     // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) | ||||
|     // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. | ||||
|     PICC_CMD_UL_WRITE = 0xA2  // Writes one 4 uint8_t page to the PICC. | ||||
|   }; | ||||
|  | ||||
|   // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more. | ||||
|   // last value set to 0xff, then compiler uses less ram, it seems some optimisations are triggered | ||||
|   enum StatusCode : uint8_t { | ||||
|     STATUS_OK,                 // Success | ||||
|     STATUS_ERROR,              // Error in communication | ||||
|     STATUS_COLLISION,          // Collission detected | ||||
|     STATUS_TIMEOUT,            // Timeout in communication. | ||||
|     STATUS_NO_ROOM,            // A buffer is not big enough. | ||||
|     STATUS_INTERNAL_ERROR,     // Internal error in the code. Should not happen ;-) | ||||
|     STATUS_INVALID,            // Invalid argument. | ||||
|     STATUS_CRC_WRONG,          // The CRC_A does not match | ||||
|     STATUS_MIFARE_NACK = 0xff  // A MIFARE PICC responded with NAK. | ||||
|   }; | ||||
|  | ||||
|   // A struct used for passing the UID of a PICC. | ||||
|   using Uid = struct { | ||||
|     uint8_t size;  // Number of uint8_ts in the UID. 4, 7 or 10. | ||||
|     uint8_t uiduint8_t[10]; | ||||
|     uint8_t sak;  // The SAK (Select acknowledge) uint8_t returned from the PICC after successful selection. | ||||
|   }; | ||||
|  | ||||
|   Uid uid_; | ||||
|   uint32_t update_wait_{0}; | ||||
|  | ||||
|   void pcd_reset_(); | ||||
|   void initialize_(); | ||||
|   void pcd_antenna_on_(); | ||||
|   uint8_t pcd_read_register_(PcdRegister reg  ///< The register to read from. One of the PCD_Register enums. | ||||
|   ); | ||||
|   uint8_t pcd_read_register(PcdRegister reg  ///< The register to read from. One of the PCD_Register enums. | ||||
|                             ) override; | ||||
|  | ||||
|   /** | ||||
|    * Reads a number of uint8_ts from the specified register in the MFRC522 chip. | ||||
|    * The interface is described in the datasheet section 8.1.2. | ||||
|    */ | ||||
|   void pcd_read_register_(PcdRegister reg,  ///< The register to read from. One of the PCD_Register enums. | ||||
|                           uint8_t count,    ///< The number of uint8_ts to read | ||||
|                           uint8_t *values,  ///< uint8_t array to store the values in. | ||||
|                           uint8_t rx_align  ///< Only bit positions rxAlign..7 in values[0] are updated. | ||||
|   ); | ||||
|   void pcd_write_register_(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | ||||
|                            uint8_t value     ///< The value to write. | ||||
|   ); | ||||
|   void pcd_read_register(PcdRegister reg,  ///< The register to read from. One of the PCD_Register enums. | ||||
|                          uint8_t count,    ///< The number of uint8_ts to read | ||||
|                          uint8_t *values,  ///< uint8_t array to store the values in. | ||||
|                          uint8_t rx_align  ///< Only bit positions rxAlign..7 in values[0] are updated. | ||||
|                          ) override; | ||||
|   void pcd_write_register(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | ||||
|                           uint8_t value     ///< The value to write. | ||||
|                           ) override; | ||||
|  | ||||
|   /** | ||||
|    * Writes a number of uint8_ts to the specified register in the MFRC522 chip. | ||||
|    * The interface is described in the datasheet section 8.1.2. | ||||
|    */ | ||||
|   void pcd_write_register_(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | ||||
|                            uint8_t count,    ///< The number of uint8_ts to write to the register | ||||
|                            uint8_t *values   ///< The values to write. uint8_t array. | ||||
|   ); | ||||
|  | ||||
|   StatusCode picc_request_a_( | ||||
|       uint8_t *buffer_atqa,  ///< The buffer to store the ATQA (Answer to request) in | ||||
|       uint8_t *buffer_size   ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. | ||||
|   ); | ||||
|   StatusCode picc_reqa_or_wupa_( | ||||
|       uint8_t command,       ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA | ||||
|       uint8_t *buffer_atqa,  ///< The buffer to store the ATQA (Answer to request) in | ||||
|       uint8_t *buffer_size   ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. | ||||
|   ); | ||||
|   void pcd_set_register_bit_mask_(PcdRegister reg,  ///< The register to update. One of the PCD_Register enums. | ||||
|                                   uint8_t mask      ///< The bits to set. | ||||
|   ); | ||||
|   void pcd_clear_register_bit_mask_(PcdRegister reg,  ///< The register to update. One of the PCD_Register enums. | ||||
|                                     uint8_t mask      ///< The bits to clear. | ||||
|   ); | ||||
|  | ||||
|   StatusCode pcd_transceive_data_(uint8_t *send_data, uint8_t send_len, uint8_t *back_data, uint8_t *back_len, | ||||
|                                   uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false); | ||||
|   StatusCode pcd_communicate_with_picc_(uint8_t command, uint8_t wait_i_rq, uint8_t *send_data, uint8_t send_len, | ||||
|                                         uint8_t *back_data = nullptr, uint8_t *back_len = nullptr, | ||||
|                                         uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false); | ||||
|   StatusCode pcd_calculate_crc_( | ||||
|       uint8_t *data,   ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. | ||||
|       uint8_t length,  ///< In: The number of uint8_ts to transfer. | ||||
|       uint8_t *result  ///< Out: Pointer to result buffer. Result is written to result[0..1], low uint8_t first. | ||||
|   ); | ||||
|   RC522::StatusCode picc_is_new_card_present_(); | ||||
|   bool picc_read_card_serial_(); | ||||
|   StatusCode picc_select_( | ||||
|       Uid *uid,               ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. | ||||
|       uint8_t valid_bits = 0  ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also | ||||
|                               ///< supply uid->size. | ||||
|   ); | ||||
|  | ||||
|   /** Read a data frame from the RC522 and return the result as a vector. | ||||
|    * | ||||
|    * Note that is_ready needs to be checked first before requesting this method. | ||||
|    * | ||||
|    * On failure, an empty vector is returned. | ||||
|    */ | ||||
|   std::vector<uint8_t> r_c522_read_data_(); | ||||
|  | ||||
|   GPIOPin *reset_pin_{nullptr}; | ||||
|   uint8_t reset_count_{0}; | ||||
|   uint32_t reset_timeout_{0}; | ||||
|   bool initialize_pending_{false}; | ||||
|   std::vector<RC522BinarySensor *> binary_sensors_; | ||||
|   std::vector<RC522Trigger *> triggers_; | ||||
|  | ||||
|   enum RC522Error { | ||||
|     NONE = 0, | ||||
|     RESET_FAILED, | ||||
|   } error_code_{NONE}; | ||||
|   void pcd_write_register(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | ||||
|                           uint8_t count,    ///< The number of uint8_ts to write to the register | ||||
|                           uint8_t *values   ///< The values to write. uint8_t array. | ||||
|                           ) override; | ||||
| }; | ||||
|  | ||||
| class RC522BinarySensor : public binary_sensor::BinarySensor { | ||||
|  public: | ||||
|   void set_uid(const std::vector<uint8_t> &uid) { uid_ = uid; } | ||||
|  | ||||
|   bool process(const uint8_t *data, uint8_t len); | ||||
|  | ||||
|   void on_scan_end() { | ||||
|     if (!this->found_) { | ||||
|       this->publish_state(false); | ||||
|     } | ||||
|     this->found_ = false; | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   std::vector<uint8_t> uid_; | ||||
|   bool found_{false}; | ||||
| }; | ||||
|  | ||||
| class RC522Trigger : public Trigger<std::string> { | ||||
|  public: | ||||
|   void process(const uint8_t *uid, uint8_t uid_length); | ||||
| }; | ||||
|  | ||||
| #ifndef MFRC522_SPICLOCK | ||||
| #define MFRC522_SPICLOCK SPI_CLOCK_DIV4  // MFRC522 accept upto 10MHz | ||||
| #endif | ||||
|  | ||||
| }  // namespace rc522_spi | ||||
| }  // namespace esphome | ||||
|   | ||||
		Reference in New Issue
	
	Block a user