mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +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
				
			| @@ -55,6 +55,8 @@ esphome/components/pn532/* @OttoWinter @jesserockz | |||||||
| esphome/components/pn532_i2c/* @OttoWinter @jesserockz | esphome/components/pn532_i2c/* @OttoWinter @jesserockz | ||||||
| esphome/components/pn532_spi/* @OttoWinter @jesserockz | esphome/components/pn532_spi/* @OttoWinter @jesserockz | ||||||
| esphome/components/power_supply/* @esphome/core | esphome/components/power_supply/* @esphome/core | ||||||
|  | esphome/components/rc522/* @glmnet | ||||||
|  | esphome/components/rc522_i2c/* @glmnet | ||||||
| esphome/components/rc522_spi/* @glmnet | esphome/components/rc522_spi/* @glmnet | ||||||
| esphome/components/restart/* @esphome/core | esphome/components/restart/* @esphome/core | ||||||
| esphome/components/rf_bridge/* @jesserockz | esphome/components/rf_bridge/* @jesserockz | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								esphome/components/rc522/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								esphome/components/rc522/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome import automation, pins | ||||||
|  | from esphome.components import i2c | ||||||
|  | from esphome.const import CONF_ON_TAG, CONF_TRIGGER_ID, CONF_RESET_PIN | ||||||
|  | from esphome.core import coroutine | ||||||
|  |  | ||||||
|  | CODEOWNERS = ['@glmnet'] | ||||||
|  | AUTO_LOAD = ['binary_sensor'] | ||||||
|  | MULTI_CONF = True | ||||||
|  |  | ||||||
|  | CONF_RC522_ID = 'rc522_id' | ||||||
|  |  | ||||||
|  | rc522_ns = cg.esphome_ns.namespace('rc522') | ||||||
|  | RC522 = rc522_ns.class_('RC522', cg.PollingComponent, i2c.I2CDevice) | ||||||
|  | RC522Trigger = rc522_ns.class_('RC522Trigger', automation.Trigger.template(cg.std_string)) | ||||||
|  |  | ||||||
|  | RC522_SCHEMA = cv.Schema({ | ||||||
|  |     cv.GenerateID(): cv.declare_id(RC522), | ||||||
|  |     cv.Optional(CONF_RESET_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')) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @coroutine | ||||||
|  | def setup_rc522(var, config): | ||||||
|  |     yield cg.register_component(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) | ||||||
							
								
								
									
										43
									
								
								esphome/components/rc522/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								esphome/components/rc522/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | 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, coroutine | ||||||
|  | from . import rc522_ns, RC522, CONF_RC522_ID | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ['rc522'] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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_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, | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @coroutine | ||||||
|  | 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)) | ||||||
							
								
								
									
										758
									
								
								esphome/components/rc522/rc522.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										758
									
								
								esphome/components/rc522/rc522.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,758 @@ | |||||||
|  | #include "rc522.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | // Based on: | ||||||
|  | // - https://github.com/miguelbalboa/rfid | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace rc522 { | ||||||
|  |  | ||||||
|  | static const char *TAG = "rc522"; | ||||||
|  |  | ||||||
|  | static const uint8_t RESET_COUNT = 5; | ||||||
|  |  | ||||||
|  | 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]); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void RC522::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; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   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_(); | ||||||
|  |  | ||||||
|  |   static StatusCode LAST_STATUS = StatusCode::STATUS_OK; | ||||||
|  |  | ||||||
|  |   if (status != LAST_STATUS) { | ||||||
|  |     ESP_LOGD(TAG, "Status is now: %d", status); | ||||||
|  |     LAST_STATUS = status; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   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); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 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 = 4; 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 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										284
									
								
								esphome/components/rc522/rc522.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								esphome/components/rc522/rc522.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,284 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/automation.h" | ||||||
|  | #include "esphome/components/binary_sensor/binary_sensor.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace rc522 { | ||||||
|  |  | ||||||
|  | class RC522BinarySensor; | ||||||
|  | class RC522Trigger; | ||||||
|  | class RC522 : public PollingComponent { | ||||||
|  |  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_(); | ||||||
|  |   virtual uint8_t pcd_read_register(PcdRegister reg  ///< The register to read from. One of the PCD_Register enums. | ||||||
|  |                                     ) = 0; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 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. | ||||||
|  |    */ | ||||||
|  |   virtual 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. | ||||||
|  |                                  ) = 0; | ||||||
|  |   virtual void pcd_write_register(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | ||||||
|  |                                   uint8_t value     ///< The value to write. | ||||||
|  |                                   ) = 0; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 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. | ||||||
|  |    */ | ||||||
|  |   virtual 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. | ||||||
|  |                                   ) = 0; | ||||||
|  |  | ||||||
|  |   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}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | 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); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace rc522 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										22
									
								
								esphome/components/rc522_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								esphome/components/rc522_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import i2c, rc522 | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  |  | ||||||
|  | CODEOWNERS = ['@glmnet'] | ||||||
|  | DEPENDENCIES = ['i2c'] | ||||||
|  | AUTO_LOAD = ['rc522'] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | rc522_i2c_ns = cg.esphome_ns.namespace('rc522_i2c') | ||||||
|  | RC522I2C = rc522_i2c_ns.class_('RC522I2C', rc522.RC522, i2c.I2CDevice) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.All(rc522.RC522_SCHEMA.extend({ | ||||||
|  |     cv.GenerateID(): cv.declare_id(RC522I2C), | ||||||
|  | }).extend(i2c.i2c_device_schema(0x2c))) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     yield rc522.setup_rc522(var, config) | ||||||
|  |     yield i2c.register_i2c_device(var, config) | ||||||
							
								
								
									
										99
									
								
								esphome/components/rc522_i2c/rc522_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								esphome/components/rc522_i2c/rc522_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | |||||||
|  | #include "rc522_i2c.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace rc522_i2c { | ||||||
|  |  | ||||||
|  | static const char *TAG = "rc522_i2c"; | ||||||
|  |  | ||||||
|  | void RC522I2C::dump_config() { | ||||||
|  |   RC522::dump_config(); | ||||||
|  |   LOG_I2C_DEVICE(this); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 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 RC522I2C::pcd_read_register(PcdRegister reg  ///< The register to read from. One of the PCD_Register enums. | ||||||
|  | ) { | ||||||
|  |   uint8_t value; | ||||||
|  |   read_byte(reg >> 1, &value); | ||||||
|  |   ESP_LOGVV(TAG, "read_register_(%x) -> %x", reg, value); | ||||||
|  |   return value; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 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 RC522I2C::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. | ||||||
|  | ) { | ||||||
|  |   if (count == 0) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   std::string buf; | ||||||
|  |   buf = "Rx"; | ||||||
|  |   char cstrb[20]; | ||||||
|  |  | ||||||
|  |   uint8_t b = values[0]; | ||||||
|  |   read_bytes(reg >> 1, values, count); | ||||||
|  |  | ||||||
|  |   if (rx_align)  // Only update bit positions rxAlign..7 in values[0] | ||||||
|  |   { | ||||||
|  |     // Create bit mask for bit positions rxAlign..7 | ||||||
|  |     uint8_t mask = 0xFF << rx_align; | ||||||
|  |     // Apply mask to both current value of values[0] and the new data in values array. | ||||||
|  |     values[0] = (b & ~mask) | (values[0] & mask); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void RC522I2C::pcd_write_register(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | ||||||
|  |                                   uint8_t value     ///< The value to write. | ||||||
|  | ) { | ||||||
|  |   this->write_byte(reg >> 1, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 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 RC522I2C::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. | ||||||
|  | ) { | ||||||
|  |   write_bytes(reg >> 1, values, count); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // bool RC522I2C::write_data(const std::vector<uint8_t> &data) { | ||||||
|  | // return this->write_bytes_raw(data.data(), data.size()); } | ||||||
|  |  | ||||||
|  | // bool RC522I2C::read_data(std::vector<uint8_t> &data, uint8_t len) { | ||||||
|  | //   delay(5); | ||||||
|  |  | ||||||
|  | //   std::vector<uint8_t> ready; | ||||||
|  | //   ready.resize(1); | ||||||
|  | //   uint32_t start_time = millis(); | ||||||
|  | //   while (true) { | ||||||
|  | //     if (this->read_bytes_raw(ready.data(), 1)) { | ||||||
|  | //       if (ready[0] == 0x01) | ||||||
|  | //         break; | ||||||
|  | //     } | ||||||
|  |  | ||||||
|  | //     if (millis() - start_time > 100) { | ||||||
|  | //       ESP_LOGV(TAG, "Timed out waiting for readiness from RC522!"); | ||||||
|  | //       return false; | ||||||
|  | //     } | ||||||
|  | //   } | ||||||
|  |  | ||||||
|  | //   data.resize(len + 1); | ||||||
|  | //   this->read_bytes_raw(data.data(), len + 1); | ||||||
|  | //   return true; | ||||||
|  | // } | ||||||
|  |  | ||||||
|  | }  // namespace rc522_i2c | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										42
									
								
								esphome/components/rc522_i2c/rc522_i2c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								esphome/components/rc522_i2c/rc522_i2c.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/rc522/rc522.h" | ||||||
|  | #include "esphome/components/i2c/i2c.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace rc522_i2c { | ||||||
|  |  | ||||||
|  | class RC522I2C : public rc522::RC522, public i2c::I2CDevice { | ||||||
|  |  public: | ||||||
|  |   void dump_config() override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   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. | ||||||
|  |                          ) 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. | ||||||
|  |                           ) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace rc522_i2c | ||||||
|  | }  // namespace esphome | ||||||
| @@ -1,39 +1,21 @@ | |||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome import automation, pins | from esphome.components import spi, rc522 | ||||||
| from esphome.components import spi | from esphome.const import CONF_ID | ||||||
| from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID, CONF_RESET_PIN, CONF_CS_PIN |  | ||||||
|  |  | ||||||
| CODEOWNERS = ['@glmnet'] | CODEOWNERS = ['@glmnet'] | ||||||
| DEPENDENCIES = ['spi'] | DEPENDENCIES = ['spi'] | ||||||
| AUTO_LOAD = ['binary_sensor'] | AUTO_LOAD = ['rc522'] | ||||||
| MULTI_CONF = True |  | ||||||
|  |  | ||||||
|  |  | ||||||
| rc522_spi_ns = cg.esphome_ns.namespace('rc522_spi') | rc522_spi_ns = cg.esphome_ns.namespace('rc522_spi') | ||||||
| RC522 = rc522_spi_ns.class_('RC522', cg.PollingComponent, spi.SPIDevice) | RC522Spi = rc522_spi_ns.class_('RC522Spi', rc522.RC522, spi.SPIDevice) | ||||||
| RC522Trigger = rc522_spi_ns.class_('RC522Trigger', automation.Trigger.template(cg.std_string)) |  | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = cv.Schema({ | CONFIG_SCHEMA = cv.All(rc522.RC522_SCHEMA.extend({ | ||||||
|     cv.GenerateID(): cv.declare_id(RC522), |     cv.GenerateID(): cv.declare_id(RC522Spi), | ||||||
|     cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, | }).extend(spi.spi_device_schema(cs_pin_required=True))) | ||||||
|     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()) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def to_code(config): | def to_code(config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     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) |     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.components.rc522.binary_sensor as rc522_binary_sensor | ||||||
| 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 |  | ||||||
|  |  | ||||||
| DEPENDENCIES = ['rc522_spi'] | DEPENDENCIES = ['rc522'] | ||||||
|  |  | ||||||
| CONF_RC522_ID = 'rc522_id' | CONFIG_SCHEMA = rc522_binary_sensor.CONFIG_SCHEMA | ||||||
|  |  | ||||||
|  |  | ||||||
| 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, |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def to_code(config): | def to_code(config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     yield rc522_binary_sensor.to_code(config) | ||||||
|     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)) |  | ||||||
|   | |||||||
| @@ -9,218 +9,30 @@ namespace rc522_spi { | |||||||
|  |  | ||||||
| static const char *TAG = "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) { |   RC522::setup(); | ||||||
|   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]); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void RC522::setup() { | void RC522Spi::dump_config() { | ||||||
|   spi_setup(); |   RC522::dump_config(); | ||||||
|   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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   LOG_PIN("  CS Pin: ", this->cs_); |   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. |  * Reads a uint8_t from the specified register in the MFRC522 chip. | ||||||
|  * The interface is described in the datasheet section 8.1.2. |  * 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; |   uint8_t value; | ||||||
|   enable(); |   enable(); | ||||||
|   transfer_byte(0x80 | reg); |   transfer_byte(0x80 | reg); | ||||||
|   value = read_byte(); |   value = read_byte(); | ||||||
|   disable(); |   disable(); | ||||||
|   ESP_LOGVV(TAG, "read_register_(%x) -> %x", reg, value); |   ESP_LOGV(TAG, "read_register_(%x) -> %x", reg, value); | ||||||
|   return value; |   return value; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -228,7 +40,7 @@ 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. |  * 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. |  * 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. | 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 count,    ///< The number of uint8_ts to read | ||||||
|                                  uint8_t *values,  ///< uint8_t array to store the values in. |                                  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. |                                  uint8_t rx_align  ///< Only bit positions rxAlign..7 in values[0] are updated. | ||||||
| @@ -278,7 +90,7 @@ void RC522::pcd_read_register_(PcdRegister reg,  ///< The register to read from. | |||||||
|   disable(); |   disable(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void RC522::pcd_write_register_(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | 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. |                                   uint8_t value     ///< The value to write. | ||||||
| ) { | ) { | ||||||
|   enable(); |   enable(); | ||||||
| @@ -292,7 +104,7 @@ 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. |  * 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. |  * 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. | 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 count,    ///< The number of uint8_ts to write to the register | ||||||
|                                   uint8_t *values   ///< The values to write. uint8_t array. |                                   uint8_t *values   ///< The values to write. uint8_t array. | ||||||
| ) { | ) { | ||||||
| @@ -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()); |   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 rc522_spi | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -10,17 +10,13 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/automation.h" | #include "esphome/components/rc522/rc522.h" | ||||||
| #include "esphome/components/binary_sensor/binary_sensor.h" |  | ||||||
| #include "esphome/components/spi/spi.h" | #include "esphome/components/spi/spi.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace rc522_spi { | namespace rc522_spi { | ||||||
|  |  | ||||||
| class RC522BinarySensor; | class RC522Spi : public rc522::RC522, | ||||||
| class RC522Trigger; |  | ||||||
|  |  | ||||||
| class RC522 : public PollingComponent, |  | ||||||
|                  public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING, |                  public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING, | ||||||
|                                        spi::DATA_RATE_4MHZ> { |                                        spi::DATA_RATE_4MHZ> { | ||||||
|  public: |  public: | ||||||
| @@ -28,274 +24,32 @@ class RC522 : public PollingComponent, | |||||||
|  |  | ||||||
|   void dump_config() 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: |  protected: | ||||||
|   enum PcdRegister : uint8_t { |   uint8_t pcd_read_register(PcdRegister reg  ///< The register to read from. One of the PCD_Register enums. | ||||||
|     // Page 0: Command and status |                             ) override; | ||||||
|     // 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. |  | ||||||
|   ); |  | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Reads a number of uint8_ts from the specified register in the MFRC522 chip. |    * 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. |    * 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. |   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 count,    ///< The number of uint8_ts to read | ||||||
|                          uint8_t *values,  ///< uint8_t array to store the values in. |                          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. |                          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. |   void pcd_write_register(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums. | ||||||
|                           uint8_t value     ///< The value to write. |                           uint8_t value     ///< The value to write. | ||||||
|   ); |                           ) override; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Writes a number of uint8_ts to the specified register in the MFRC522 chip. |    * 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. |    * 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. |   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 count,    ///< The number of uint8_ts to write to the register | ||||||
|                           uint8_t *values   ///< The values to write. uint8_t array. |                           uint8_t *values   ///< The values to write. uint8_t array. | ||||||
|   ); |                           ) override; | ||||||
|  |  | ||||||
|   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}; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| 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 rc522_spi | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -813,7 +813,7 @@ esp32_touch: | |||||||
|  |  | ||||||
| binary_sensor: | binary_sensor: | ||||||
|   - platform: gpio |   - platform: gpio | ||||||
|     name: "MCP23S08 Pin #1" |     name: 'MCP23S08 Pin #1' | ||||||
|     pin: |     pin: | ||||||
|       mcp23s08: mcp23s08_hub |       mcp23s08: mcp23s08_hub | ||||||
|       # Use pin number 1 |       # Use pin number 1 | ||||||
| @@ -822,7 +822,7 @@ binary_sensor: | |||||||
|       mode: INPUT_PULLUP |       mode: INPUT_PULLUP | ||||||
|       inverted: False |       inverted: False | ||||||
|   - platform: gpio |   - platform: gpio | ||||||
|     name: "MCP23S17 Pin #1" |     name: 'MCP23S17 Pin #1' | ||||||
|     pin: |     pin: | ||||||
|       mcp23s17: mcp23s17_hub |       mcp23s17: mcp23s17_hub | ||||||
|       # Use pin number 1 |       # Use pin number 1 | ||||||
| @@ -1391,7 +1391,7 @@ climate: | |||||||
|  |  | ||||||
| switch: | switch: | ||||||
|   - platform: gpio |   - platform: gpio | ||||||
|     name: "MCP23S08 Pin #0" |     name: 'MCP23S08 Pin #0' | ||||||
|     pin: |     pin: | ||||||
|       mcp23s08: mcp23s08_hub |       mcp23s08: mcp23s08_hub | ||||||
|       # Use pin number 0 |       # Use pin number 0 | ||||||
| @@ -1399,7 +1399,7 @@ switch: | |||||||
|       mode: OUTPUT |       mode: OUTPUT | ||||||
|       inverted: False |       inverted: False | ||||||
|   - platform: gpio |   - platform: gpio | ||||||
|     name: "MCP23S17 Pin #0" |     name: 'MCP23S17 Pin #0' | ||||||
|     pin: |     pin: | ||||||
|       mcp23s17: mcp23s17_hub |       mcp23s17: mcp23s17_hub | ||||||
|       # Use pin number 0 |       # Use pin number 0 | ||||||
| @@ -1823,6 +1823,12 @@ rc522_spi: | |||||||
|     - lambda: |- |     - lambda: |- | ||||||
|         ESP_LOGD("main", "Found tag %s", x.c_str()); |         ESP_LOGD("main", "Found tag %s", x.c_str()); | ||||||
|  |  | ||||||
|  | rc522_i2c: | ||||||
|  |   update_interval: 1s | ||||||
|  |   on_tag: | ||||||
|  |     - lambda: |- | ||||||
|  |         ESP_LOGD("main", "Found tag %s", x.c_str()); | ||||||
|  |  | ||||||
| gps: | gps: | ||||||
|  |  | ||||||
| time: | time: | ||||||
| @@ -1933,7 +1939,7 @@ text_sensor: | |||||||
|           value: '0' |           value: '0' | ||||||
|       - canbus.send: |       - canbus.send: | ||||||
|           can_id: 23 |           can_id: 23 | ||||||
|           data: [ 0x10, 0x20, 0x30 ] |           data: [0x10, 0x20, 0x30] | ||||||
|   - platform: template |   - platform: template | ||||||
|     name: Template Text Sensor |     name: Template Text Sensor | ||||||
|     id: template_text |     id: template_text | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user