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:
Guillermo Ruffino
2021-01-12 10:13:53 -03:00
committed by GitHub
parent 400819175d
commit fbc1b3e316
13 changed files with 1359 additions and 1092 deletions
+2
View File
@@ -55,6 +55,8 @@ esphome/components/pn532/* @OttoWinter @jesserockz
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
esphome/components/pn532_spi/* @OttoWinter @jesserockz
esphome/components/power_supply/* @esphome/core
esphome/components/rc522/* @glmnet
esphome/components/rc522_i2c/* @glmnet
esphome/components/rc522_spi/* @glmnet
esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz
+38
View 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
View 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))
File diff suppressed because it is too large Load Diff
+284
View 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
View 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)
@@ -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
View 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
+8 -26
View File
@@ -1,39 +1,21 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation, pins
from esphome.components import spi
from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID, CONF_RESET_PIN, CONF_CS_PIN
from esphome.components import spi, rc522
from esphome.const import CONF_ID
CODEOWNERS = ['@glmnet']
DEPENDENCIES = ['spi']
AUTO_LOAD = ['binary_sensor']
MULTI_CONF = True
AUTO_LOAD = ['rc522']
rc522_spi_ns = cg.esphome_ns.namespace('rc522_spi')
RC522 = rc522_spi_ns.class_('RC522', cg.PollingComponent, spi.SPIDevice)
RC522Trigger = rc522_spi_ns.class_('RC522Trigger', automation.Trigger.template(cg.std_string))
RC522Spi = rc522_spi_ns.class_('RC522Spi', rc522.RC522, spi.SPIDevice)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(RC522),
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_ON_TAG): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RC522Trigger),
}),
}).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema())
CONFIG_SCHEMA = cv.All(rc522.RC522_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(RC522Spi),
}).extend(spi.spi_device_schema(cs_pin_required=True)))
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield rc522.setup_rc522(var, config)
yield spi.register_spi_device(var, config)
if CONF_RESET_PIN in config:
reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN])
cg.add(var.set_reset_pin(reset))
for conf in config.get(CONF_ON_TAG, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
cg.add(var.register_trigger(trigger))
yield automation.build_automation(trigger, [(cg.std_string, 'x')], conf)
+4 -39
View File
@@ -1,44 +1,9 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import CONF_UID, CONF_ID
from esphome.core import HexInt
from . import rc522_spi_ns, RC522
import esphome.components.rc522.binary_sensor as rc522_binary_sensor
DEPENDENCIES = ['rc522_spi']
DEPENDENCIES = ['rc522']
CONF_RC522_ID = 'rc522_id'
def validate_uid(value):
value = cv.string_strict(value)
for x in value.split('-'):
if len(x) != 2:
raise cv.Invalid("Each part (separated by '-') of the UID must be two characters "
"long.")
try:
x = int(x, 16)
except ValueError as err:
raise cv.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.") from err
if x < 0 or x > 255:
raise cv.Invalid("Valid values for UID parts (separated by '-') are 00 to FF")
return value
RC522BinarySensor = rc522_spi_ns.class_('RC522BinarySensor', binary_sensor.BinarySensor)
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(RC522BinarySensor),
cv.GenerateID(CONF_RC522_ID): cv.use_id(RC522),
cv.Required(CONF_UID): validate_uid,
})
CONFIG_SCHEMA = rc522_binary_sensor.CONFIG_SCHEMA
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield binary_sensor.register_binary_sensor(var, config)
hub = yield cg.get_variable(config[CONF_RC522_ID])
cg.add(hub.register_tag(var))
addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')]
cg.add(var.set_uid(addr))
yield rc522_binary_sensor.to_code(config)
File diff suppressed because it is too large Load Diff
+18 -264
View File
@@ -10,292 +10,46 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/rc522/rc522.h"
#include "esphome/components/spi/spi.h"
namespace esphome {
namespace rc522_spi {
class RC522BinarySensor;
class RC522Trigger;
class RC522 : public PollingComponent,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
spi::DATA_RATE_4MHZ> {
class RC522Spi : public rc522::RC522,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
spi::DATA_RATE_4MHZ> {
public:
void setup() override;
void dump_config() override;
void update() override;
float get_setup_priority() const override { return setup_priority::DATA; };
void loop() override;
void register_tag(RC522BinarySensor *tag) { this->binary_sensors_.push_back(tag); }
void register_trigger(RC522Trigger *trig) { this->triggers_.push_back(trig); }
void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; }
protected:
enum PcdRegister : uint8_t {
// Page 0: Command and status
// 0x00 // reserved for future use
COMMAND_REG = 0x01 << 1, // starts and stops command execution
COM_I_EN_REG = 0x02 << 1, // enable and disable interrupt request control bits
DIV_I_EN_REG = 0x03 << 1, // enable and disable interrupt request control bits
COM_IRQ_REG = 0x04 << 1, // interrupt request bits
DIV_IRQ_REG = 0x05 << 1, // interrupt request bits
ERROR_REG = 0x06 << 1, // error bits showing the error status of the last command executed
STATUS1_REG = 0x07 << 1, // communication status bits
STATUS2_REG = 0x08 << 1, // receiver and transmitter status bits
FIFO_DATA_REG = 0x09 << 1, // input and output of 64 uint8_t FIFO buffer
FIFO_LEVEL_REG = 0x0A << 1, // number of uint8_ts stored in the FIFO buffer
WATER_LEVEL_REG = 0x0B << 1, // level for FIFO underflow and overflow warning
CONTROL_REG = 0x0C << 1, // miscellaneous control registers
BIT_FRAMING_REG = 0x0D << 1, // adjustments for bit-oriented frames
COLL_REG = 0x0E << 1, // bit position of the first bit-collision detected on the RF interface
// 0x0F // reserved for future use
// Page 1: Command
// 0x10 // reserved for future use
MODE_REG = 0x11 << 1, // defines general modes for transmitting and receiving
TX_MODE_REG = 0x12 << 1, // defines transmission data rate and framing
RX_MODE_REG = 0x13 << 1, // defines reception data rate and framing
TX_CONTROL_REG = 0x14 << 1, // controls the logical behavior of the antenna driver pins TX1 and TX2
TX_ASK_REG = 0x15 << 1, // controls the setting of the transmission modulation
TX_SEL_REG = 0x16 << 1, // selects the internal sources for the antenna driver
RX_SEL_REG = 0x17 << 1, // selects internal receiver settings
RX_THRESHOLD_REG = 0x18 << 1, // selects thresholds for the bit decoder
DEMOD_REG = 0x19 << 1, // defines demodulator settings
// 0x1A // reserved for future use
// 0x1B // reserved for future use
MF_TX_REG = 0x1C << 1, // controls some MIFARE communication transmit parameters
MF_RX_REG = 0x1D << 1, // controls some MIFARE communication receive parameters
// 0x1E // reserved for future use
SERIAL_SPEED_REG = 0x1F << 1, // selects the speed of the serial UART interface
// Page 2: Configuration
// 0x20 // reserved for future use
CRC_RESULT_REG_H = 0x21 << 1, // shows the MSB and LSB values of the CRC calculation
CRC_RESULT_REG_L = 0x22 << 1,
// 0x23 // reserved for future use
MOD_WIDTH_REG = 0x24 << 1, // controls the ModWidth setting?
// 0x25 // reserved for future use
RF_CFG_REG = 0x26 << 1, // configures the receiver gain
GS_N_REG = 0x27 << 1, // selects the conductance of the antenna driver pins TX1 and TX2 for modulation
CW_GS_P_REG = 0x28 << 1, // defines the conductance of the p-driver output during periods of no modulation
MOD_GS_P_REG = 0x29 << 1, // defines the conductance of the p-driver output during periods of modulation
T_MODE_REG = 0x2A << 1, // defines settings for the internal timer
T_PRESCALER_REG = 0x2B << 1, // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg.
T_RELOAD_REG_H = 0x2C << 1, // defines the 16-bit timer reload value
T_RELOAD_REG_L = 0x2D << 1,
T_COUNTER_VALUE_REG_H = 0x2E << 1, // shows the 16-bit timer value
T_COUNTER_VALUE_REG_L = 0x2F << 1,
// Page 3: Test Registers
// 0x30 // reserved for future use
TEST_SEL1_REG = 0x31 << 1, // general test signal configuration
TEST_SEL2_REG = 0x32 << 1, // general test signal configuration
TEST_PIN_EN_REG = 0x33 << 1, // enables pin output driver on pins D1 to D7
TEST_PIN_VALUE_REG = 0x34 << 1, // defines the values for D1 to D7 when it is used as an I/O bus
TEST_BUS_REG = 0x35 << 1, // shows the status of the internal test bus
AUTO_TEST_REG = 0x36 << 1, // controls the digital self-test
VERSION_REG = 0x37 << 1, // shows the software version
ANALOG_TEST_REG = 0x38 << 1, // controls the pins AUX1 and AUX2
TEST_DA_C1_REG = 0x39 << 1, // defines the test value for TestDAC1
TEST_DA_C2_REG = 0x3A << 1, // defines the test value for TestDAC2
TEST_ADC_REG = 0x3B << 1 // shows the value of ADC I and Q channels
// 0x3C // reserved for production tests
// 0x3D // reserved for production tests
// 0x3E // reserved for production tests
// 0x3F // reserved for production tests
};
// MFRC522 commands. Described in chapter 10 of the datasheet.
enum PcdCommand : uint8_t {
PCD_IDLE = 0x00, // no action, cancels current command execution
PCD_MEM = 0x01, // stores 25 uint8_ts into the internal buffer
PCD_GENERATE_RANDOM_ID = 0x02, // generates a 10-uint8_t random ID number
PCD_CALC_CRC = 0x03, // activates the CRC coprocessor or performs a self-test
PCD_TRANSMIT = 0x04, // transmits data from the FIFO buffer
PCD_NO_CMD_CHANGE = 0x07, // no command change, can be used to modify the CommandReg register bits without
// affecting the command, for example, the PowerDown bit
PCD_RECEIVE = 0x08, // activates the receiver circuits
PCD_TRANSCEIVE =
0x0C, // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission
PCD_MF_AUTHENT = 0x0E, // performs the MIFARE standard authentication as a reader
PCD_SOFT_RESET = 0x0F // resets the MFRC522
};
// Commands sent to the PICC.
enum PiccCommand : uint8_t {
// The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4)
PICC_CMD_REQA = 0x26, // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for
// anticollision or selection. 7 bit frame.
PICC_CMD_WUPA = 0x52, // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and
// prepare for anticollision or selection. 7 bit frame.
PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during anti collision.
PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1
PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2
PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3
PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT.
PICC_CMD_RATS = 0xE0, // Request command for Answer To Reset.
// The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9)
// Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on
// the sector.
// The read/write commands can also be used for MIFARE Ultralight.
PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A
PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B
PICC_CMD_MF_READ =
0x30, // Reads one 16 uint8_t block from the authenticated sector of the PICC. Also used for MIFARE Ultralight.
PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 uint8_t block to the authenticated sector of the PICC. Called
// "COMPATIBILITY WRITE" for MIFARE Ultralight.
PICC_CMD_MF_DECREMENT =
0xC0, // Decrements the contents of a block and stores the result in the internal data register.
PICC_CMD_MF_INCREMENT =
0xC1, // Increments the contents of a block and stores the result in the internal data register.
PICC_CMD_MF_RESTORE = 0xC2, // Reads the contents of a block into the internal data register.
PICC_CMD_MF_TRANSFER = 0xB0, // Writes the contents of the internal data register to a block.
// The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6)
// The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight.
PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 uint8_t page to the PICC.
};
// Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more.
// last value set to 0xff, then compiler uses less ram, it seems some optimisations are triggered
enum StatusCode : uint8_t {
STATUS_OK, // Success
STATUS_ERROR, // Error in communication
STATUS_COLLISION, // Collission detected
STATUS_TIMEOUT, // Timeout in communication.
STATUS_NO_ROOM, // A buffer is not big enough.
STATUS_INTERNAL_ERROR, // Internal error in the code. Should not happen ;-)
STATUS_INVALID, // Invalid argument.
STATUS_CRC_WRONG, // The CRC_A does not match
STATUS_MIFARE_NACK = 0xff // A MIFARE PICC responded with NAK.
};
// A struct used for passing the UID of a PICC.
using Uid = struct {
uint8_t size; // Number of uint8_ts in the UID. 4, 7 or 10.
uint8_t uiduint8_t[10];
uint8_t sak; // The SAK (Select acknowledge) uint8_t returned from the PICC after successful selection.
};
Uid uid_;
uint32_t update_wait_{0};
void pcd_reset_();
void initialize_();
void pcd_antenna_on_();
uint8_t pcd_read_register_(PcdRegister reg ///< The register to read from. One of the PCD_Register enums.
);
uint8_t pcd_read_register(PcdRegister reg ///< The register to read from. One of the PCD_Register enums.
) override;
/**
* Reads a number of uint8_ts from the specified register in the MFRC522 chip.
* The interface is described in the datasheet section 8.1.2.
*/
void pcd_read_register_(PcdRegister reg, ///< The register to read from. One of the PCD_Register enums.
uint8_t count, ///< The number of uint8_ts to read
uint8_t *values, ///< uint8_t array to store the values in.
uint8_t rx_align ///< Only bit positions rxAlign..7 in values[0] are updated.
);
void pcd_write_register_(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums.
uint8_t value ///< The value to write.
);
void pcd_read_register(PcdRegister reg, ///< The register to read from. One of the PCD_Register enums.
uint8_t count, ///< The number of uint8_ts to read
uint8_t *values, ///< uint8_t array to store the values in.
uint8_t rx_align ///< Only bit positions rxAlign..7 in values[0] are updated.
) override;
void pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums.
uint8_t value ///< The value to write.
) override;
/**
* Writes a number of uint8_ts to the specified register in the MFRC522 chip.
* The interface is described in the datasheet section 8.1.2.
*/
void pcd_write_register_(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums.
uint8_t count, ///< The number of uint8_ts to write to the register
uint8_t *values ///< The values to write. uint8_t array.
);
StatusCode picc_request_a_(
uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in
uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK.
);
StatusCode picc_reqa_or_wupa_(
uint8_t command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA
uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in
uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK.
);
void pcd_set_register_bit_mask_(PcdRegister reg, ///< The register to update. One of the PCD_Register enums.
uint8_t mask ///< The bits to set.
);
void pcd_clear_register_bit_mask_(PcdRegister reg, ///< The register to update. One of the PCD_Register enums.
uint8_t mask ///< The bits to clear.
);
StatusCode pcd_transceive_data_(uint8_t *send_data, uint8_t send_len, uint8_t *back_data, uint8_t *back_len,
uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false);
StatusCode pcd_communicate_with_picc_(uint8_t command, uint8_t wait_i_rq, uint8_t *send_data, uint8_t send_len,
uint8_t *back_data = nullptr, uint8_t *back_len = nullptr,
uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false);
StatusCode pcd_calculate_crc_(
uint8_t *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation.
uint8_t length, ///< In: The number of uint8_ts to transfer.
uint8_t *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low uint8_t first.
);
RC522::StatusCode picc_is_new_card_present_();
bool picc_read_card_serial_();
StatusCode picc_select_(
Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID.
uint8_t valid_bits = 0 ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also
///< supply uid->size.
);
/** Read a data frame from the RC522 and return the result as a vector.
*
* Note that is_ready needs to be checked first before requesting this method.
*
* On failure, an empty vector is returned.
*/
std::vector<uint8_t> r_c522_read_data_();
GPIOPin *reset_pin_{nullptr};
uint8_t reset_count_{0};
uint32_t reset_timeout_{0};
bool initialize_pending_{false};
std::vector<RC522BinarySensor *> binary_sensors_;
std::vector<RC522Trigger *> triggers_;
enum RC522Error {
NONE = 0,
RESET_FAILED,
} error_code_{NONE};
void pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums.
uint8_t count, ///< The number of uint8_ts to write to the register
uint8_t *values ///< The values to write. uint8_t array.
) override;
};
class RC522BinarySensor : public binary_sensor::BinarySensor {
public:
void set_uid(const std::vector<uint8_t> &uid) { uid_ = uid; }
bool process(const uint8_t *data, uint8_t len);
void on_scan_end() {
if (!this->found_) {
this->publish_state(false);
}
this->found_ = false;
}
protected:
std::vector<uint8_t> uid_;
bool found_{false};
};
class RC522Trigger : public Trigger<std::string> {
public:
void process(const uint8_t *uid, uint8_t uid_length);
};
#ifndef MFRC522_SPICLOCK
#define MFRC522_SPICLOCK SPI_CLOCK_DIV4 // MFRC522 accept upto 10MHz
#endif
} // namespace rc522_spi
} // namespace esphome
+24 -18
View File
@@ -201,7 +201,7 @@ mcp23s08:
- id: 'mcp23s08_hub'
cs_pin: GPIO12
deviceaddress: 0
mcp23s17:
- id: 'mcp23s17_hub'
cs_pin: GPIO12
@@ -813,7 +813,7 @@ esp32_touch:
binary_sensor:
- platform: gpio
name: "MCP23S08 Pin #1"
name: 'MCP23S08 Pin #1'
pin:
mcp23s08: mcp23s08_hub
# Use pin number 1
@@ -822,7 +822,7 @@ binary_sensor:
mode: INPUT_PULLUP
inverted: False
- platform: gpio
name: "MCP23S17 Pin #1"
name: 'MCP23S17 Pin #1'
pin:
mcp23s17: mcp23s17_hub
# Use pin number 1
@@ -1391,7 +1391,7 @@ climate:
switch:
- platform: gpio
name: "MCP23S08 Pin #0"
name: 'MCP23S08 Pin #0'
pin:
mcp23s08: mcp23s08_hub
# Use pin number 0
@@ -1399,7 +1399,7 @@ switch:
mode: OUTPUT
inverted: False
- platform: gpio
name: "MCP23S17 Pin #0"
name: 'MCP23S17 Pin #0'
pin:
mcp23s17: mcp23s17_hub
# Use pin number 0
@@ -1823,6 +1823,12 @@ rc522_spi:
- lambda: |-
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:
time:
@@ -1933,7 +1939,7 @@ text_sensor:
value: '0'
- canbus.send:
can_id: 23
data: [ 0x10, 0x20, 0x30 ]
data: [0x10, 0x20, 0x30]
- platform: template
name: Template Text Sensor
id: template_text
@@ -1967,15 +1973,15 @@ canbus:
can_id: 4
bit_rate: 50kbps
on_frame:
- can_id: 500
then:
- lambda: |-
std::string b(x.begin(), x.end());
ESP_LOGD("canid 500", "%s", &b[0] );
- can_id: 23
then:
- if:
condition:
lambda: 'return x[0] == 0x11;'
then:
light.toggle: living_room_lights
- can_id: 500
then:
- lambda: |-
std::string b(x.begin(), x.end());
ESP_LOGD("canid 500", "%s", &b[0] );
- can_id: 23
then:
- if:
condition:
lambda: 'return x[0] == 0x11;'
then:
light.toggle: living_room_lights