mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					828f7946ea | ||
| 
						 | 
					23c663d5d4 | ||
| 
						 | 
					6a99789c92 | ||
| 
						 | 
					52e13164b4 | ||
| 
						 | 
					28f2582256 | ||
| 
						 | 
					652f6058d1 | ||
| 
						 | 
					717aab7c8b | ||
| 
						 | 
					5e799b5284 | ||
| 
						 | 
					9f36b25d4e | ||
| 
						 | 
					c8058e9636 | ||
| 
						 | 
					f2474c5c45 | ||
| 
						 | 
					7acc36d39d | ||
| 
						 | 
					b01db991a5 | 
@@ -37,6 +37,7 @@ esphome/components/globals/* @esphome/core
 | 
			
		||||
esphome/components/gpio/* @esphome/core
 | 
			
		||||
esphome/components/homeassistant/* @OttoWinter
 | 
			
		||||
esphome/components/i2c/* @esphome/core
 | 
			
		||||
esphome/components/inkplate6/* @jesserockz
 | 
			
		||||
esphome/components/integration/* @OttoWinter
 | 
			
		||||
esphome/components/interval/* @esphome/core
 | 
			
		||||
esphome/components/json/* @OttoWinter
 | 
			
		||||
@@ -48,6 +49,7 @@ esphome/components/mcp23s17/* @SenexCrenshaw
 | 
			
		||||
esphome/components/mcp2515/* @danielschramm @mvturnho
 | 
			
		||||
esphome/components/mcp9808/* @k7hpn
 | 
			
		||||
esphome/components/network/* @esphome/core
 | 
			
		||||
esphome/components/nfc/* @jesserockz
 | 
			
		||||
esphome/components/ota/* @esphome/core
 | 
			
		||||
esphome/components/output/* @esphome/core
 | 
			
		||||
esphome/components/pid/* @OttoWinter
 | 
			
		||||
@@ -55,6 +57,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
 | 
			
		||||
 
 | 
			
		||||
@@ -676,8 +676,10 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->client_->add(reinterpret_cast<char *>(header.data()), header.size());
 | 
			
		||||
  this->client_->add(reinterpret_cast<char *>(buffer.get_buffer()->data()), buffer.get_buffer()->size());
 | 
			
		||||
  this->client_->add(reinterpret_cast<char *>(header.data()), header.size(),
 | 
			
		||||
                     ASYNC_WRITE_FLAG_COPY | ASYNC_WRITE_FLAG_MORE);
 | 
			
		||||
  this->client_->add(reinterpret_cast<char *>(buffer.get_buffer()->data()), buffer.get_buffer()->size(),
 | 
			
		||||
                     ASYNC_WRITE_FLAG_COPY);
 | 
			
		||||
  bool ret = this->client_->send();
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,10 @@ void DS1307Component::setup() {
 | 
			
		||||
  if (!this->read_rtc_()) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
  }
 | 
			
		||||
  this->set_interval(15 * 60 * 1000, [&]() { this->read(); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DS1307Component::update() { this->read(); }
 | 
			
		||||
 | 
			
		||||
void DS1307Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "DS1307:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ namespace ds1307 {
 | 
			
		||||
class DS1307Component : public time::RealTimeClock, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void read();
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,12 @@ from .. import gps_ns, GPSListener, CONF_GPS_ID, GPS
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['gps']
 | 
			
		||||
 | 
			
		||||
GPSTime = gps_ns.class_('GPSTime', time_.RealTimeClock, GPSListener)
 | 
			
		||||
GPSTime = gps_ns.class_('GPSTime', cg.PollingComponent, time_.RealTimeClock, GPSListener)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = time_.TIME_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(GPSTime),
 | 
			
		||||
    cv.GenerateID(CONF_GPS_ID): cv.use_id(GPS),
 | 
			
		||||
}).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
}).extend(cv.polling_component_schema('5min'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
 
 | 
			
		||||
@@ -9,13 +9,11 @@ namespace gps {
 | 
			
		||||
 | 
			
		||||
class GPSTime : public time::RealTimeClock, public GPSListener {
 | 
			
		||||
 public:
 | 
			
		||||
  void update() override { this->from_tiny_gps_(this->get_tiny_gps()); };
 | 
			
		||||
  void on_update(TinyGPSPlus &tiny_gps) override {
 | 
			
		||||
    if (!this->has_time_)
 | 
			
		||||
      this->from_tiny_gps_(tiny_gps);
 | 
			
		||||
  }
 | 
			
		||||
  void setup() override {
 | 
			
		||||
    this->set_interval(5 * 60 * 1000, [this]() { this->from_tiny_gps_(this->get_tiny_gps()); });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void from_tiny_gps_(TinyGPSPlus &tiny_gps);
 | 
			
		||||
 
 | 
			
		||||
@@ -10,17 +10,13 @@ void HomeassistantTime::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Home Assistant Time:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Timezone: '%s'", this->timezone_.c_str());
 | 
			
		||||
}
 | 
			
		||||
float HomeassistantTime::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
void HomeassistantTime::setup() {
 | 
			
		||||
  global_homeassistant_time = this;
 | 
			
		||||
 | 
			
		||||
  this->set_interval(15 * 60 * 1000, []() {
 | 
			
		||||
    // re-request time every 15 minutes
 | 
			
		||||
    api::global_api_server->request_time();
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
float HomeassistantTime::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
void HomeassistantTime::setup() { global_homeassistant_time = this; }
 | 
			
		||||
 | 
			
		||||
void HomeassistantTime::update() { api::global_api_server->request_time(); }
 | 
			
		||||
 | 
			
		||||
HomeassistantTime *global_homeassistant_time = nullptr;
 | 
			
		||||
 | 
			
		||||
}  // namespace homeassistant
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ namespace homeassistant {
 | 
			
		||||
class HomeassistantTime : public time::RealTimeClock {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void set_epoch_time(uint32_t epoch) { this->synchronize_epoch_(epoch); }
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								esphome/components/inkplate6/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/inkplate6/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
CODEOWNERS = ['@jesserockz']
 | 
			
		||||
							
								
								
									
										141
									
								
								esphome/components/inkplate6/display.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								esphome/components/inkplate6/display.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.components import display, i2c
 | 
			
		||||
from esphome.const import CONF_FULL_UPDATE_EVERY, CONF_ID, CONF_LAMBDA, CONF_PAGES, \
 | 
			
		||||
    CONF_WAKEUP_PIN, ESP_PLATFORM_ESP32
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
 | 
			
		||||
 | 
			
		||||
CONF_DISPLAY_DATA_0_PIN = 'display_data_0_pin'
 | 
			
		||||
CONF_DISPLAY_DATA_1_PIN = 'display_data_1_pin'
 | 
			
		||||
CONF_DISPLAY_DATA_2_PIN = 'display_data_2_pin'
 | 
			
		||||
CONF_DISPLAY_DATA_3_PIN = 'display_data_3_pin'
 | 
			
		||||
CONF_DISPLAY_DATA_4_PIN = 'display_data_4_pin'
 | 
			
		||||
CONF_DISPLAY_DATA_5_PIN = 'display_data_5_pin'
 | 
			
		||||
CONF_DISPLAY_DATA_6_PIN = 'display_data_6_pin'
 | 
			
		||||
CONF_DISPLAY_DATA_7_PIN = 'display_data_7_pin'
 | 
			
		||||
 | 
			
		||||
CONF_CL_PIN = 'cl_pin'
 | 
			
		||||
CONF_CKV_PIN = 'ckv_pin'
 | 
			
		||||
CONF_GREYSCALE = 'greyscale'
 | 
			
		||||
CONF_GMOD_PIN = 'gmod_pin'
 | 
			
		||||
CONF_GPIO0_ENABLE_PIN = 'gpio0_enable_pin'
 | 
			
		||||
CONF_LE_PIN = 'le_pin'
 | 
			
		||||
CONF_OE_PIN = 'oe_pin'
 | 
			
		||||
CONF_PARTIAL_UPDATING = 'partial_updating'
 | 
			
		||||
CONF_POWERUP_PIN = 'powerup_pin'
 | 
			
		||||
CONF_SPH_PIN = 'sph_pin'
 | 
			
		||||
CONF_SPV_PIN = 'spv_pin'
 | 
			
		||||
CONF_VCOM_PIN = 'vcom_pin'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
inkplate6_ns = cg.esphome_ns.namespace('inkplate6')
 | 
			
		||||
Inkplate6 = inkplate6_ns.class_('Inkplate6', cg.PollingComponent, i2c.I2CDevice,
 | 
			
		||||
                                display.DisplayBuffer)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(display.FULL_DISPLAY_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(Inkplate6),
 | 
			
		||||
    cv.Optional(CONF_GREYSCALE, default=False): cv.boolean,
 | 
			
		||||
    cv.Optional(CONF_PARTIAL_UPDATING, default=True): cv.boolean,
 | 
			
		||||
    cv.Optional(CONF_FULL_UPDATE_EVERY, default=10): cv.uint32_t,
 | 
			
		||||
    # Control pins
 | 
			
		||||
    cv.Required(CONF_CKV_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Required(CONF_GMOD_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Required(CONF_GPIO0_ENABLE_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Required(CONF_OE_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Required(CONF_POWERUP_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Required(CONF_SPH_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Required(CONF_SPV_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Required(CONF_VCOM_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Required(CONF_WAKEUP_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Optional(CONF_CL_PIN, default=0): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
    cv.Optional(CONF_LE_PIN, default=2): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
    # Data pins
 | 
			
		||||
    cv.Optional(CONF_DISPLAY_DATA_0_PIN, default=4): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
    cv.Optional(CONF_DISPLAY_DATA_1_PIN, default=5): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
    cv.Optional(CONF_DISPLAY_DATA_2_PIN, default=18): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
    cv.Optional(CONF_DISPLAY_DATA_3_PIN, default=19): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
    cv.Optional(CONF_DISPLAY_DATA_4_PIN, default=23): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
    cv.Optional(CONF_DISPLAY_DATA_5_PIN, default=25): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
    cv.Optional(CONF_DISPLAY_DATA_6_PIN, default=26): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
    cv.Optional(CONF_DISPLAY_DATA_7_PIN, default=27): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
}).extend(cv.polling_component_schema('5s').extend(i2c.i2c_device_schema(0x48))),
 | 
			
		||||
                       cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield display.register_display(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_LAMBDA in config:
 | 
			
		||||
        lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')],
 | 
			
		||||
                                          return_type=cg.void)
 | 
			
		||||
        cg.add(var.set_writer(lambda_))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_greyscale(config[CONF_GREYSCALE]))
 | 
			
		||||
    cg.add(var.set_partial_updating(config[CONF_PARTIAL_UPDATING]))
 | 
			
		||||
    cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
 | 
			
		||||
 | 
			
		||||
    ckv = yield cg.gpio_pin_expression(config[CONF_CKV_PIN])
 | 
			
		||||
    cg.add(var.set_ckv_pin(ckv))
 | 
			
		||||
 | 
			
		||||
    gmod = yield cg.gpio_pin_expression(config[CONF_GMOD_PIN])
 | 
			
		||||
    cg.add(var.set_gmod_pin(gmod))
 | 
			
		||||
 | 
			
		||||
    gpio0_enable = yield cg.gpio_pin_expression(config[CONF_GPIO0_ENABLE_PIN])
 | 
			
		||||
    cg.add(var.set_gpio0_enable_pin(gpio0_enable))
 | 
			
		||||
 | 
			
		||||
    oe = yield cg.gpio_pin_expression(config[CONF_OE_PIN])
 | 
			
		||||
    cg.add(var.set_oe_pin(oe))
 | 
			
		||||
 | 
			
		||||
    powerup = yield cg.gpio_pin_expression(config[CONF_POWERUP_PIN])
 | 
			
		||||
    cg.add(var.set_powerup_pin(powerup))
 | 
			
		||||
 | 
			
		||||
    sph = yield cg.gpio_pin_expression(config[CONF_SPH_PIN])
 | 
			
		||||
    cg.add(var.set_sph_pin(sph))
 | 
			
		||||
 | 
			
		||||
    spv = yield cg.gpio_pin_expression(config[CONF_SPV_PIN])
 | 
			
		||||
    cg.add(var.set_spv_pin(spv))
 | 
			
		||||
 | 
			
		||||
    vcom = yield cg.gpio_pin_expression(config[CONF_VCOM_PIN])
 | 
			
		||||
    cg.add(var.set_vcom_pin(vcom))
 | 
			
		||||
 | 
			
		||||
    wakeup = yield cg.gpio_pin_expression(config[CONF_WAKEUP_PIN])
 | 
			
		||||
    cg.add(var.set_wakeup_pin(wakeup))
 | 
			
		||||
 | 
			
		||||
    cl = yield cg.gpio_pin_expression(config[CONF_CL_PIN])
 | 
			
		||||
    cg.add(var.set_cl_pin(cl))
 | 
			
		||||
 | 
			
		||||
    le = yield cg.gpio_pin_expression(config[CONF_LE_PIN])
 | 
			
		||||
    cg.add(var.set_le_pin(le))
 | 
			
		||||
 | 
			
		||||
    display_data_0 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_0_PIN])
 | 
			
		||||
    cg.add(var.set_display_data_0_pin(display_data_0))
 | 
			
		||||
 | 
			
		||||
    display_data_1 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_1_PIN])
 | 
			
		||||
    cg.add(var.set_display_data_1_pin(display_data_1))
 | 
			
		||||
 | 
			
		||||
    display_data_2 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_2_PIN])
 | 
			
		||||
    cg.add(var.set_display_data_2_pin(display_data_2))
 | 
			
		||||
 | 
			
		||||
    display_data_3 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_3_PIN])
 | 
			
		||||
    cg.add(var.set_display_data_3_pin(display_data_3))
 | 
			
		||||
 | 
			
		||||
    display_data_4 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_4_PIN])
 | 
			
		||||
    cg.add(var.set_display_data_4_pin(display_data_4))
 | 
			
		||||
 | 
			
		||||
    display_data_5 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_5_PIN])
 | 
			
		||||
    cg.add(var.set_display_data_5_pin(display_data_5))
 | 
			
		||||
 | 
			
		||||
    display_data_6 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_6_PIN])
 | 
			
		||||
    cg.add(var.set_display_data_6_pin(display_data_6))
 | 
			
		||||
 | 
			
		||||
    display_data_7 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_7_PIN])
 | 
			
		||||
    cg.add(var.set_display_data_7_pin(display_data_7))
 | 
			
		||||
 | 
			
		||||
    cg.add_build_flag('-DBOARD_HAS_PSRAM')
 | 
			
		||||
							
								
								
									
										630
									
								
								esphome/components/inkplate6/inkplate.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										630
									
								
								esphome/components/inkplate6/inkplate.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,630 @@
 | 
			
		||||
#include "inkplate.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace inkplate6 {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "inkplate";
 | 
			
		||||
 | 
			
		||||
void Inkplate6::setup() {
 | 
			
		||||
  this->initialize_();
 | 
			
		||||
 | 
			
		||||
  this->vcom_pin_->setup();
 | 
			
		||||
  this->powerup_pin_->setup();
 | 
			
		||||
  this->wakeup_pin_->setup();
 | 
			
		||||
  this->gpio0_enable_pin_->setup();
 | 
			
		||||
  this->gpio0_enable_pin_->digital_write(true);
 | 
			
		||||
 | 
			
		||||
  this->cl_pin_->setup();
 | 
			
		||||
  this->le_pin_->setup();
 | 
			
		||||
  this->ckv_pin_->setup();
 | 
			
		||||
  this->gmod_pin_->setup();
 | 
			
		||||
  this->oe_pin_->setup();
 | 
			
		||||
  this->sph_pin_->setup();
 | 
			
		||||
  this->spv_pin_->setup();
 | 
			
		||||
 | 
			
		||||
  this->display_data_0_pin_->setup();
 | 
			
		||||
  this->display_data_1_pin_->setup();
 | 
			
		||||
  this->display_data_2_pin_->setup();
 | 
			
		||||
  this->display_data_3_pin_->setup();
 | 
			
		||||
  this->display_data_4_pin_->setup();
 | 
			
		||||
  this->display_data_5_pin_->setup();
 | 
			
		||||
  this->display_data_6_pin_->setup();
 | 
			
		||||
  this->display_data_7_pin_->setup();
 | 
			
		||||
 | 
			
		||||
  this->clean();
 | 
			
		||||
  this->display();
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::initialize_() {
 | 
			
		||||
  uint32_t buffer_size = this->get_buffer_length_();
 | 
			
		||||
 | 
			
		||||
  if (this->partial_buffer_ != nullptr) {
 | 
			
		||||
    free(this->partial_buffer_);  // NOLINT
 | 
			
		||||
  }
 | 
			
		||||
  if (this->partial_buffer_2_ != nullptr) {
 | 
			
		||||
    free(this->partial_buffer_2_);  // NOLINT
 | 
			
		||||
  }
 | 
			
		||||
  if (this->buffer_ != nullptr) {
 | 
			
		||||
    free(this->buffer_);  // NOLINT
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->buffer_ = (uint8_t *) ps_malloc(buffer_size);
 | 
			
		||||
  if (this->buffer_ == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "Could not allocate buffer for display!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->greyscale_) {
 | 
			
		||||
    this->partial_buffer_ = (uint8_t *) ps_malloc(buffer_size);
 | 
			
		||||
    if (this->partial_buffer_ == nullptr) {
 | 
			
		||||
      ESP_LOGE(TAG, "Could not allocate partial buffer for display!");
 | 
			
		||||
      this->mark_failed();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this->partial_buffer_2_ = (uint8_t *) ps_malloc(buffer_size * 2);
 | 
			
		||||
    if (this->partial_buffer_2_ == nullptr) {
 | 
			
		||||
      ESP_LOGE(TAG, "Could not allocate partial buffer 2 for display!");
 | 
			
		||||
      this->mark_failed();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    memset(this->partial_buffer_, 0, buffer_size);
 | 
			
		||||
    memset(this->partial_buffer_2_, 0, buffer_size * 2);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  memset(this->buffer_, 0, buffer_size);
 | 
			
		||||
}
 | 
			
		||||
float Inkplate6::get_setup_priority() const { return setup_priority::PROCESSOR; }
 | 
			
		||||
size_t Inkplate6::get_buffer_length_() {
 | 
			
		||||
  if (this->greyscale_) {
 | 
			
		||||
    return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 2u;
 | 
			
		||||
  } else {
 | 
			
		||||
    return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 8u;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::update() {
 | 
			
		||||
  this->do_update_();
 | 
			
		||||
 | 
			
		||||
  if (this->full_update_every_ > 0 && this->partial_updates_ >= this->full_update_every_) {
 | 
			
		||||
    this->block_partial_ = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->display();
 | 
			
		||||
}
 | 
			
		||||
void HOT Inkplate6::draw_absolute_pixel_internal(int x, int y, Color color) {
 | 
			
		||||
  if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (this->greyscale_) {
 | 
			
		||||
    int x1 = x / 2;
 | 
			
		||||
    int x_sub = x % 2;
 | 
			
		||||
    uint32_t pos = (x1 + y * (this->get_width_internal() / 2));
 | 
			
		||||
    uint8_t current = this->buffer_[pos];
 | 
			
		||||
 | 
			
		||||
    // float px = (0.2126 * (color.red / 255.0)) + (0.7152 * (color.green / 255.0)) + (0.0722 * (color.blue / 255.0));
 | 
			
		||||
    // px = pow(px, 1.5);
 | 
			
		||||
    // uint8_t gs = (uint8_t)(px*7);
 | 
			
		||||
 | 
			
		||||
    uint8_t gs = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5;
 | 
			
		||||
    this->buffer_[pos] = (pixelMaskGLUT[x_sub] & current) | (x_sub ? gs : gs << 4);
 | 
			
		||||
 | 
			
		||||
  } else {
 | 
			
		||||
    int x1 = x / 8;
 | 
			
		||||
    int x_sub = x % 8;
 | 
			
		||||
    uint32_t pos = (x1 + y * (this->get_width_internal() / 8));
 | 
			
		||||
    uint8_t current = this->partial_buffer_[pos];
 | 
			
		||||
    this->partial_buffer_[pos] = (~pixelMaskLUT[x_sub] & current) | (color.is_on() ? 0 : pixelMaskLUT[x_sub]);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::dump_config() {
 | 
			
		||||
  LOG_DISPLAY("", "Inkplate", this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Greyscale: %s", YESNO(this->greyscale_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Partial Updating: %s", YESNO(this->partial_updating_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Full Update Every: %d", this->full_update_every_);
 | 
			
		||||
  // Log pins
 | 
			
		||||
  LOG_PIN("  CKV Pin: ", this->ckv_pin_);
 | 
			
		||||
  LOG_PIN("  CL Pin: ", this->cl_pin_);
 | 
			
		||||
  LOG_PIN("  GPIO0 Enable Pin: ", this->gpio0_enable_pin_);
 | 
			
		||||
  LOG_PIN("  GMOD Pin: ", this->gmod_pin_);
 | 
			
		||||
  LOG_PIN("  LE Pin: ", this->le_pin_);
 | 
			
		||||
  LOG_PIN("  OE Pin: ", this->oe_pin_);
 | 
			
		||||
  LOG_PIN("  POWERUP Pin: ", this->powerup_pin_);
 | 
			
		||||
  LOG_PIN("  SPH Pin: ", this->sph_pin_);
 | 
			
		||||
  LOG_PIN("  SPV Pin: ", this->spv_pin_);
 | 
			
		||||
  LOG_PIN("  VCOM Pin: ", this->vcom_pin_);
 | 
			
		||||
  LOG_PIN("  WAKEUP Pin: ", this->wakeup_pin_);
 | 
			
		||||
 | 
			
		||||
  LOG_PIN("  Data 0 Pin: ", this->display_data_0_pin_);
 | 
			
		||||
  LOG_PIN("  Data 1 Pin: ", this->display_data_1_pin_);
 | 
			
		||||
  LOG_PIN("  Data 2 Pin: ", this->display_data_2_pin_);
 | 
			
		||||
  LOG_PIN("  Data 3 Pin: ", this->display_data_3_pin_);
 | 
			
		||||
  LOG_PIN("  Data 4 Pin: ", this->display_data_4_pin_);
 | 
			
		||||
  LOG_PIN("  Data 5 Pin: ", this->display_data_5_pin_);
 | 
			
		||||
  LOG_PIN("  Data 6 Pin: ", this->display_data_6_pin_);
 | 
			
		||||
  LOG_PIN("  Data 7 Pin: ", this->display_data_7_pin_);
 | 
			
		||||
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::eink_off_() {
 | 
			
		||||
  ESP_LOGV(TAG, "Eink off called");
 | 
			
		||||
  unsigned long start_time = millis();
 | 
			
		||||
  if (panel_on_ == 0)
 | 
			
		||||
    return;
 | 
			
		||||
  panel_on_ = 0;
 | 
			
		||||
  this->gmod_pin_->digital_write(false);
 | 
			
		||||
  this->oe_pin_->digital_write(false);
 | 
			
		||||
 | 
			
		||||
  GPIO.out &= ~(get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()) | (1 << this->le_pin_->get_pin()));
 | 
			
		||||
 | 
			
		||||
  this->sph_pin_->digital_write(false);
 | 
			
		||||
  this->spv_pin_->digital_write(false);
 | 
			
		||||
 | 
			
		||||
  this->powerup_pin_->digital_write(false);
 | 
			
		||||
  this->wakeup_pin_->digital_write(false);
 | 
			
		||||
  this->vcom_pin_->digital_write(false);
 | 
			
		||||
 | 
			
		||||
  pins_z_state_();
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::eink_on_() {
 | 
			
		||||
  ESP_LOGV(TAG, "Eink on called");
 | 
			
		||||
  unsigned long start_time = millis();
 | 
			
		||||
  if (panel_on_ == 1)
 | 
			
		||||
    return;
 | 
			
		||||
  panel_on_ = 1;
 | 
			
		||||
  pins_as_outputs_();
 | 
			
		||||
  this->wakeup_pin_->digital_write(true);
 | 
			
		||||
  this->powerup_pin_->digital_write(true);
 | 
			
		||||
  this->vcom_pin_->digital_write(true);
 | 
			
		||||
 | 
			
		||||
  this->write_byte(0x01, 0x3F);
 | 
			
		||||
 | 
			
		||||
  delay(40);
 | 
			
		||||
 | 
			
		||||
  this->write_byte(0x0D, 0x80);
 | 
			
		||||
 | 
			
		||||
  delay(2);
 | 
			
		||||
 | 
			
		||||
  this->read_byte(0x00, &temperature_, 0);
 | 
			
		||||
 | 
			
		||||
  this->le_pin_->digital_write(false);
 | 
			
		||||
  this->oe_pin_->digital_write(false);
 | 
			
		||||
  this->cl_pin_->digital_write(false);
 | 
			
		||||
  this->sph_pin_->digital_write(true);
 | 
			
		||||
  this->gmod_pin_->digital_write(true);
 | 
			
		||||
  this->spv_pin_->digital_write(true);
 | 
			
		||||
  this->ckv_pin_->digital_write(false);
 | 
			
		||||
  this->oe_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::fill(Color color) {
 | 
			
		||||
  ESP_LOGV(TAG, "Fill called");
 | 
			
		||||
  unsigned long start_time = millis();
 | 
			
		||||
 | 
			
		||||
  if (this->greyscale_) {
 | 
			
		||||
    uint8_t fill = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5;
 | 
			
		||||
    for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
 | 
			
		||||
      this->buffer_[i] = (fill << 4) | fill;
 | 
			
		||||
  } else {
 | 
			
		||||
    uint8_t fill = color.is_on() ? 0x00 : 0xFF;
 | 
			
		||||
    for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
 | 
			
		||||
      this->partial_buffer_[i] = fill;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Fill finished (%lums)", millis() - start_time);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::display() {
 | 
			
		||||
  ESP_LOGV(TAG, "Display called");
 | 
			
		||||
  unsigned long start_time = millis();
 | 
			
		||||
 | 
			
		||||
  if (this->greyscale_) {
 | 
			
		||||
    this->display3b_();
 | 
			
		||||
  } else {
 | 
			
		||||
    if (this->partial_updating_ && this->partial_update_()) {
 | 
			
		||||
      ESP_LOGV(TAG, "Display finished (partial) (%lums)", millis() - start_time);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this->display1b_();
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGV(TAG, "Display finished (full) (%lums)", millis() - start_time);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::display1b_() {
 | 
			
		||||
  ESP_LOGV(TAG, "Display1b called");
 | 
			
		||||
  unsigned long start_time = millis();
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i < this->get_buffer_length_(); i++) {
 | 
			
		||||
    this->buffer_[i] &= this->partial_buffer_[i];
 | 
			
		||||
    this->buffer_[i] |= this->partial_buffer_[i];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint16_t pos;
 | 
			
		||||
  uint32_t send;
 | 
			
		||||
  uint8_t data;
 | 
			
		||||
  uint8_t buffer_value;
 | 
			
		||||
  eink_on_();
 | 
			
		||||
  clean_fast_(0, 1);
 | 
			
		||||
  clean_fast_(1, 5);
 | 
			
		||||
  clean_fast_(2, 1);
 | 
			
		||||
  clean_fast_(0, 5);
 | 
			
		||||
  clean_fast_(2, 1);
 | 
			
		||||
  clean_fast_(1, 12);
 | 
			
		||||
  clean_fast_(2, 1);
 | 
			
		||||
  clean_fast_(0, 11);
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Display1b start loops (%lums)", millis() - start_time);
 | 
			
		||||
  for (int k = 0; k < 3; k++) {
 | 
			
		||||
    pos = this->get_buffer_length_() - 1;
 | 
			
		||||
    vscan_start_();
 | 
			
		||||
    for (int i = 0; i < this->get_height_internal(); i++) {
 | 
			
		||||
      buffer_value = this->buffer_[pos];
 | 
			
		||||
      data = LUTB[(buffer_value >> 4) & 0x0F];
 | 
			
		||||
      send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
             (((data & B11100000) >> 5) << 25);
 | 
			
		||||
      hscan_start_(send);
 | 
			
		||||
      data = LUTB[buffer_value & 0x0F];
 | 
			
		||||
      send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
             (((data & B11100000) >> 5) << 25);
 | 
			
		||||
      GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      pos--;
 | 
			
		||||
 | 
			
		||||
      for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
 | 
			
		||||
        buffer_value = this->buffer_[pos];
 | 
			
		||||
        data = LUTB[(buffer_value >> 4) & 0x0F];
 | 
			
		||||
        send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
               (((data & B11100000) >> 5) << 25);
 | 
			
		||||
        GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
        GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
        data = LUTB[buffer_value & 0x0F];
 | 
			
		||||
        send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
               (((data & B11100000) >> 5) << 25);
 | 
			
		||||
        GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
        GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
        pos--;
 | 
			
		||||
      }
 | 
			
		||||
      GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      vscan_end_();
 | 
			
		||||
    }
 | 
			
		||||
    delayMicroseconds(230);
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGV(TAG, "Display1b first loop x %d (%lums)", 3, millis() - start_time);
 | 
			
		||||
 | 
			
		||||
  pos = this->get_buffer_length_() - 1;
 | 
			
		||||
  vscan_start_();
 | 
			
		||||
  for (int i = 0; i < this->get_height_internal(); i++) {
 | 
			
		||||
    buffer_value = this->buffer_[pos];
 | 
			
		||||
    data = LUT2[(buffer_value >> 4) & 0x0F];
 | 
			
		||||
    send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
           (((data & B11100000) >> 5) << 25);
 | 
			
		||||
    hscan_start_(send);
 | 
			
		||||
    data = LUT2[buffer_value & 0x0F];
 | 
			
		||||
    send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
           (((data & B11100000) >> 5) << 25);
 | 
			
		||||
    GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
    GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
    pos--;
 | 
			
		||||
    for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
 | 
			
		||||
      buffer_value = this->buffer_[pos];
 | 
			
		||||
      data = LUT2[(buffer_value >> 4) & 0x0F];
 | 
			
		||||
      send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
             (((data & B11100000) >> 5) << 25);
 | 
			
		||||
      GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      data = LUT2[buffer_value & 0x0F];
 | 
			
		||||
      send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
             (((data & B11100000) >> 5) << 25);
 | 
			
		||||
      GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      pos--;
 | 
			
		||||
    }
 | 
			
		||||
    GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
    GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
    vscan_end_();
 | 
			
		||||
  }
 | 
			
		||||
  delayMicroseconds(230);
 | 
			
		||||
  ESP_LOGV(TAG, "Display1b second loop (%lums)", millis() - start_time);
 | 
			
		||||
 | 
			
		||||
  vscan_start_();
 | 
			
		||||
  for (int i = 0; i < this->get_height_internal(); i++) {
 | 
			
		||||
    buffer_value = this->buffer_[pos];
 | 
			
		||||
    data = 0b00000000;
 | 
			
		||||
    send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
           (((data & B11100000) >> 5) << 25);
 | 
			
		||||
    hscan_start_(send);
 | 
			
		||||
    GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
    GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
    for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
 | 
			
		||||
      GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
    }
 | 
			
		||||
    GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
    GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
    vscan_end_();
 | 
			
		||||
  }
 | 
			
		||||
  delayMicroseconds(230);
 | 
			
		||||
  ESP_LOGV(TAG, "Display1b third loop (%lums)", millis() - start_time);
 | 
			
		||||
 | 
			
		||||
  vscan_start_();
 | 
			
		||||
  eink_off_();
 | 
			
		||||
  this->block_partial_ = false;
 | 
			
		||||
  this->partial_updates_ = 0;
 | 
			
		||||
  ESP_LOGV(TAG, "Display1b finished (%lums)", millis() - start_time);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::display3b_() {
 | 
			
		||||
  ESP_LOGV(TAG, "Display3b called");
 | 
			
		||||
  unsigned long start_time = millis();
 | 
			
		||||
 | 
			
		||||
  eink_on_();
 | 
			
		||||
  clean_fast_(0, 1);
 | 
			
		||||
  clean_fast_(1, 12);
 | 
			
		||||
  clean_fast_(2, 1);
 | 
			
		||||
  clean_fast_(0, 11);
 | 
			
		||||
  clean_fast_(2, 1);
 | 
			
		||||
  clean_fast_(1, 12);
 | 
			
		||||
  clean_fast_(2, 1);
 | 
			
		||||
  clean_fast_(0, 11);
 | 
			
		||||
 | 
			
		||||
  for (int k = 0; k < 8; k++) {
 | 
			
		||||
    uint32_t pos = this->get_buffer_length_() - 1;
 | 
			
		||||
    uint32_t send;
 | 
			
		||||
    uint8_t pix1;
 | 
			
		||||
    uint8_t pix2;
 | 
			
		||||
    uint8_t pix3;
 | 
			
		||||
    uint8_t pix4;
 | 
			
		||||
    uint8_t pixel;
 | 
			
		||||
    uint8_t pixel2;
 | 
			
		||||
 | 
			
		||||
    vscan_start_();
 | 
			
		||||
    for (int i = 0; i < this->get_height_internal(); i++) {
 | 
			
		||||
      pix1 = this->buffer_[pos--];
 | 
			
		||||
      pix2 = this->buffer_[pos--];
 | 
			
		||||
      pix3 = this->buffer_[pos--];
 | 
			
		||||
      pix4 = this->buffer_[pos--];
 | 
			
		||||
      pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) |
 | 
			
		||||
              (waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0);
 | 
			
		||||
      pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) |
 | 
			
		||||
               (waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0);
 | 
			
		||||
 | 
			
		||||
      send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) |
 | 
			
		||||
             (((pixel & B11100000) >> 5) << 25);
 | 
			
		||||
      hscan_start_(send);
 | 
			
		||||
      send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) |
 | 
			
		||||
             (((pixel2 & B11100000) >> 5) << 25);
 | 
			
		||||
      GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
 | 
			
		||||
      for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
 | 
			
		||||
        pix1 = this->buffer_[pos--];
 | 
			
		||||
        pix2 = this->buffer_[pos--];
 | 
			
		||||
        pix3 = this->buffer_[pos--];
 | 
			
		||||
        pix4 = this->buffer_[pos--];
 | 
			
		||||
        pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) |
 | 
			
		||||
                (waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0);
 | 
			
		||||
        pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) |
 | 
			
		||||
                 (waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0);
 | 
			
		||||
 | 
			
		||||
        send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) |
 | 
			
		||||
               (((pixel & B11100000) >> 5) << 25);
 | 
			
		||||
        GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
        GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
 | 
			
		||||
        send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) |
 | 
			
		||||
               (((pixel2 & B11100000) >> 5) << 25);
 | 
			
		||||
        GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
        GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      }
 | 
			
		||||
      GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      vscan_end_();
 | 
			
		||||
    }
 | 
			
		||||
    delayMicroseconds(230);
 | 
			
		||||
  }
 | 
			
		||||
  clean_fast_(2, 1);
 | 
			
		||||
  clean_fast_(3, 1);
 | 
			
		||||
  vscan_start_();
 | 
			
		||||
  eink_off_();
 | 
			
		||||
  ESP_LOGV(TAG, "Display3b finished (%lums)", millis() - start_time);
 | 
			
		||||
}
 | 
			
		||||
bool Inkplate6::partial_update_() {
 | 
			
		||||
  ESP_LOGV(TAG, "Partial update called");
 | 
			
		||||
  unsigned long start_time = millis();
 | 
			
		||||
  if (this->greyscale_)
 | 
			
		||||
    return false;
 | 
			
		||||
  if (this->block_partial_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  this->partial_updates_++;
 | 
			
		||||
 | 
			
		||||
  uint16_t pos = this->get_buffer_length_() - 1;
 | 
			
		||||
  uint32_t send;
 | 
			
		||||
  uint8_t data;
 | 
			
		||||
  uint8_t diffw, diffb;
 | 
			
		||||
  uint32_t n = (this->get_buffer_length_() * 2) - 1;
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i < this->get_height_internal(); i++) {
 | 
			
		||||
    for (int j = 0; j < (this->get_width_internal() / 8); j++) {
 | 
			
		||||
      diffw = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & ~(this->partial_buffer_[pos]);
 | 
			
		||||
      diffb = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & this->partial_buffer_[pos];
 | 
			
		||||
      pos--;
 | 
			
		||||
      this->partial_buffer_2_[n--] = LUTW[diffw >> 4] & LUTB[diffb >> 4];
 | 
			
		||||
      this->partial_buffer_2_[n--] = LUTW[diffw & 0x0F] & LUTB[diffb & 0x0F];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGV(TAG, "Partial update buffer built after (%lums)", millis() - start_time);
 | 
			
		||||
 | 
			
		||||
  eink_on_();
 | 
			
		||||
  for (int k = 0; k < 5; k++) {
 | 
			
		||||
    vscan_start_();
 | 
			
		||||
    n = (this->get_buffer_length_() * 2) - 1;
 | 
			
		||||
    for (int i = 0; i < this->get_height_internal(); i++) {
 | 
			
		||||
      data = this->partial_buffer_2_[n--];
 | 
			
		||||
      send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
             (((data & B11100000) >> 5) << 25);
 | 
			
		||||
      hscan_start_(send);
 | 
			
		||||
      for (int j = 0; j < (this->get_width_internal() / 4) - 1; j++) {
 | 
			
		||||
        data = this->partial_buffer_2_[n--];
 | 
			
		||||
        send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
               (((data & B11100000) >> 5) << 25);
 | 
			
		||||
        GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
        GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      }
 | 
			
		||||
      GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      vscan_end_();
 | 
			
		||||
    }
 | 
			
		||||
    delayMicroseconds(230);
 | 
			
		||||
    ESP_LOGV(TAG, "Partial update loop k=%d (%lums)", k, millis() - start_time);
 | 
			
		||||
  }
 | 
			
		||||
  clean_fast_(2, 2);
 | 
			
		||||
  clean_fast_(3, 1);
 | 
			
		||||
  vscan_start_();
 | 
			
		||||
  eink_off_();
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i < this->get_buffer_length_(); i++) {
 | 
			
		||||
    this->buffer_[i] = this->partial_buffer_[i];
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGV(TAG, "Partial update finished (%lums)", millis() - start_time);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::vscan_start_() {
 | 
			
		||||
  this->ckv_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(7);
 | 
			
		||||
  this->spv_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(10);
 | 
			
		||||
  this->ckv_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(0);
 | 
			
		||||
  this->ckv_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(8);
 | 
			
		||||
  this->spv_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(10);
 | 
			
		||||
  this->ckv_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(0);
 | 
			
		||||
  this->ckv_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(18);
 | 
			
		||||
  this->ckv_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(0);
 | 
			
		||||
  this->ckv_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(18);
 | 
			
		||||
  this->ckv_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(0);
 | 
			
		||||
  this->ckv_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::vscan_write_() {
 | 
			
		||||
  this->ckv_pin_->digital_write(false);
 | 
			
		||||
  this->le_pin_->digital_write(true);
 | 
			
		||||
  this->le_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(0);
 | 
			
		||||
  this->sph_pin_->digital_write(false);
 | 
			
		||||
  this->cl_pin_->digital_write(true);
 | 
			
		||||
  this->cl_pin_->digital_write(false);
 | 
			
		||||
  this->sph_pin_->digital_write(true);
 | 
			
		||||
  this->ckv_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::hscan_start_(uint32_t d) {
 | 
			
		||||
  this->sph_pin_->digital_write(false);
 | 
			
		||||
  GPIO.out_w1ts = (d) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
  GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
  this->sph_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::vscan_end_() {
 | 
			
		||||
  this->ckv_pin_->digital_write(false);
 | 
			
		||||
  this->le_pin_->digital_write(true);
 | 
			
		||||
  this->le_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(1);
 | 
			
		||||
  this->ckv_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::clean() {
 | 
			
		||||
  ESP_LOGV(TAG, "Clean called");
 | 
			
		||||
  unsigned long start_time = millis();
 | 
			
		||||
 | 
			
		||||
  eink_on_();
 | 
			
		||||
  clean_fast_(0, 1);   // White
 | 
			
		||||
  clean_fast_(0, 8);   // White to White
 | 
			
		||||
  clean_fast_(0, 1);   // White to Black
 | 
			
		||||
  clean_fast_(0, 8);   // Black to Black
 | 
			
		||||
  clean_fast_(2, 1);   // Black to White
 | 
			
		||||
  clean_fast_(1, 10);  // White to White
 | 
			
		||||
  ESP_LOGV(TAG, "Clean finished (%lums)", millis() - start_time);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) {
 | 
			
		||||
  ESP_LOGV(TAG, "Clean fast called with: (%d, %d)", c, rep);
 | 
			
		||||
  unsigned long start_time = millis();
 | 
			
		||||
 | 
			
		||||
  eink_on_();
 | 
			
		||||
  uint8_t data = 0;
 | 
			
		||||
  if (c == 0)  // White
 | 
			
		||||
    data = B10101010;
 | 
			
		||||
  else if (c == 1)  // Black
 | 
			
		||||
    data = B01010101;
 | 
			
		||||
  else if (c == 2)  // Discharge
 | 
			
		||||
    data = B00000000;
 | 
			
		||||
  else if (c == 3)  // Skip
 | 
			
		||||
    data = B11111111;
 | 
			
		||||
 | 
			
		||||
  uint32_t send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
 | 
			
		||||
                  (((data & B11100000) >> 5) << 25);
 | 
			
		||||
 | 
			
		||||
  for (int k = 0; k < rep; k++) {
 | 
			
		||||
    vscan_start_();
 | 
			
		||||
    for (int i = 0; i < this->get_height_internal(); i++) {
 | 
			
		||||
      hscan_start_(send);
 | 
			
		||||
      GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      for (int j = 0; j < this->get_width_internal() / 8; j++) {
 | 
			
		||||
        GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
        GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
        GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
        GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      }
 | 
			
		||||
      GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
 | 
			
		||||
      vscan_end_();
 | 
			
		||||
    }
 | 
			
		||||
    delayMicroseconds(230);
 | 
			
		||||
    ESP_LOGV(TAG, "Clean fast rep loop %d finished (%lums)", k, millis() - start_time);
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGV(TAG, "Clean fast finished (%lums)", millis() - start_time);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::pins_z_state_() {
 | 
			
		||||
  this->ckv_pin_->pin_mode(INPUT);
 | 
			
		||||
  this->sph_pin_->pin_mode(INPUT);
 | 
			
		||||
 | 
			
		||||
  this->oe_pin_->pin_mode(INPUT);
 | 
			
		||||
  this->gmod_pin_->pin_mode(INPUT);
 | 
			
		||||
  this->spv_pin_->pin_mode(INPUT);
 | 
			
		||||
 | 
			
		||||
  this->display_data_0_pin_->pin_mode(INPUT);
 | 
			
		||||
  this->display_data_1_pin_->pin_mode(INPUT);
 | 
			
		||||
  this->display_data_2_pin_->pin_mode(INPUT);
 | 
			
		||||
  this->display_data_3_pin_->pin_mode(INPUT);
 | 
			
		||||
  this->display_data_4_pin_->pin_mode(INPUT);
 | 
			
		||||
  this->display_data_5_pin_->pin_mode(INPUT);
 | 
			
		||||
  this->display_data_6_pin_->pin_mode(INPUT);
 | 
			
		||||
  this->display_data_7_pin_->pin_mode(INPUT);
 | 
			
		||||
}
 | 
			
		||||
void Inkplate6::pins_as_outputs_() {
 | 
			
		||||
  this->ckv_pin_->pin_mode(OUTPUT);
 | 
			
		||||
  this->sph_pin_->pin_mode(OUTPUT);
 | 
			
		||||
 | 
			
		||||
  this->oe_pin_->pin_mode(OUTPUT);
 | 
			
		||||
  this->gmod_pin_->pin_mode(OUTPUT);
 | 
			
		||||
  this->spv_pin_->pin_mode(OUTPUT);
 | 
			
		||||
 | 
			
		||||
  this->display_data_0_pin_->pin_mode(OUTPUT);
 | 
			
		||||
  this->display_data_1_pin_->pin_mode(OUTPUT);
 | 
			
		||||
  this->display_data_2_pin_->pin_mode(OUTPUT);
 | 
			
		||||
  this->display_data_3_pin_->pin_mode(OUTPUT);
 | 
			
		||||
  this->display_data_4_pin_->pin_mode(OUTPUT);
 | 
			
		||||
  this->display_data_5_pin_->pin_mode(OUTPUT);
 | 
			
		||||
  this->display_data_6_pin_->pin_mode(OUTPUT);
 | 
			
		||||
  this->display_data_7_pin_->pin_mode(OUTPUT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace inkplate6
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										157
									
								
								esphome/components/inkplate6/inkplate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								esphome/components/inkplate6/inkplate.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,157 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
#include "esphome/components/display/display_buffer.h"
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace inkplate6 {
 | 
			
		||||
 | 
			
		||||
class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  const uint8_t LUT2[16] = {B10101010, B10101001, B10100110, B10100101, B10011010, B10011001, B10010110, B10010101,
 | 
			
		||||
                            B01101010, B01101001, B01100110, B01100101, B01011010, B01011001, B01010110, B01010101};
 | 
			
		||||
  const uint8_t LUTW[16] = {B11111111, B11111110, B11111011, B11111010, B11101111, B11101110, B11101011, B11101010,
 | 
			
		||||
                            B10111111, B10111110, B10111011, B10111010, B10101111, B10101110, B10101011, B10101010};
 | 
			
		||||
  const uint8_t LUTB[16] = {B11111111, B11111101, B11110111, B11110101, B11011111, B11011101, B11010111, B11010101,
 | 
			
		||||
                            B01111111, B01111101, B01110111, B01110101, B01011111, B01011101, B01010111, B01010101};
 | 
			
		||||
  const uint8_t pixelMaskLUT[8] = {B00000001, B00000010, B00000100, B00001000,
 | 
			
		||||
                                   B00010000, B00100000, B01000000, B10000000};
 | 
			
		||||
  const uint8_t pixelMaskGLUT[2] = {B00001111, B11110000};
 | 
			
		||||
  const uint8_t waveform3Bit[8][8] = {{0, 0, 0, 0, 1, 1, 1, 0}, {1, 2, 2, 2, 1, 1, 1, 0}, {0, 1, 2, 1, 1, 2, 1, 0},
 | 
			
		||||
                                      {0, 2, 1, 2, 1, 2, 1, 0}, {0, 0, 0, 1, 1, 1, 2, 0}, {2, 1, 1, 1, 2, 1, 2, 0},
 | 
			
		||||
                                      {1, 1, 1, 2, 1, 2, 2, 0}, {0, 0, 0, 0, 0, 0, 2, 0}};
 | 
			
		||||
  const uint32_t waveform[50] = {
 | 
			
		||||
      0x00000008, 0x00000008, 0x00200408, 0x80281888, 0x60a81898, 0x60a8a8a8, 0x60a8a8a8, 0x6068a868, 0x6868a868,
 | 
			
		||||
      0x6868a868, 0x68686868, 0x6a686868, 0x5a686868, 0x5a686868, 0x5a586a68, 0x5a5a6a68, 0x5a5a6a68, 0x55566a68,
 | 
			
		||||
      0x55565a64, 0x55555654, 0x55555556, 0x55555556, 0x55555556, 0x55555516, 0x55555596, 0x15555595, 0x95955595,
 | 
			
		||||
      0x95959595, 0x95949495, 0x94949495, 0x94949495, 0xa4949494, 0x9494a4a4, 0x84a49494, 0x84948484, 0x84848484,
 | 
			
		||||
      0x84848484, 0x84848484, 0xa5a48484, 0xa9a4a4a8, 0xa9a8a8a8, 0xa5a9a9a4, 0xa5a5a5a4, 0xa1a5a5a1, 0xa9a9a9a9,
 | 
			
		||||
      0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0x15151515, 0x11111111};
 | 
			
		||||
 | 
			
		||||
  void set_greyscale(bool greyscale) {
 | 
			
		||||
    this->greyscale_ = greyscale;
 | 
			
		||||
    this->initialize_();
 | 
			
		||||
    this->block_partial_ = true;
 | 
			
		||||
  }
 | 
			
		||||
  void set_partial_updating(bool partial_updating) { this->partial_updating_ = partial_updating; }
 | 
			
		||||
  void set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; }
 | 
			
		||||
 | 
			
		||||
  void set_display_data_0_pin(GPIOPin *data) { this->display_data_0_pin_ = data; }
 | 
			
		||||
  void set_display_data_1_pin(GPIOPin *data) { this->display_data_1_pin_ = data; }
 | 
			
		||||
  void set_display_data_2_pin(GPIOPin *data) { this->display_data_2_pin_ = data; }
 | 
			
		||||
  void set_display_data_3_pin(GPIOPin *data) { this->display_data_3_pin_ = data; }
 | 
			
		||||
  void set_display_data_4_pin(GPIOPin *data) { this->display_data_4_pin_ = data; }
 | 
			
		||||
  void set_display_data_5_pin(GPIOPin *data) { this->display_data_5_pin_ = data; }
 | 
			
		||||
  void set_display_data_6_pin(GPIOPin *data) { this->display_data_6_pin_ = data; }
 | 
			
		||||
  void set_display_data_7_pin(GPIOPin *data) { this->display_data_7_pin_ = data; }
 | 
			
		||||
 | 
			
		||||
  void set_ckv_pin(GPIOPin *ckv) { this->ckv_pin_ = ckv; }
 | 
			
		||||
  void set_cl_pin(GPIOPin *cl) { this->cl_pin_ = cl; }
 | 
			
		||||
  void set_gpio0_enable_pin(GPIOPin *gpio0_enable) { this->gpio0_enable_pin_ = gpio0_enable; }
 | 
			
		||||
  void set_gmod_pin(GPIOPin *gmod) { this->gmod_pin_ = gmod; }
 | 
			
		||||
  void set_le_pin(GPIOPin *le) { this->le_pin_ = le; }
 | 
			
		||||
  void set_oe_pin(GPIOPin *oe) { this->oe_pin_ = oe; }
 | 
			
		||||
  void set_powerup_pin(GPIOPin *powerup) { this->powerup_pin_ = powerup; }
 | 
			
		||||
  void set_sph_pin(GPIOPin *sph) { this->sph_pin_ = sph; }
 | 
			
		||||
  void set_spv_pin(GPIOPin *spv) { this->spv_pin_ = spv; }
 | 
			
		||||
  void set_vcom_pin(GPIOPin *vcom) { this->vcom_pin_ = vcom; }
 | 
			
		||||
  void set_wakeup_pin(GPIOPin *wakeup) { this->wakeup_pin_ = wakeup; }
 | 
			
		||||
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  void display();
 | 
			
		||||
  void clean();
 | 
			
		||||
  void fill(Color color) override;
 | 
			
		||||
 | 
			
		||||
  void update() override;
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
 | 
			
		||||
  uint8_t get_panel_state() { return this->panel_on_; }
 | 
			
		||||
  bool get_greyscale() { return this->greyscale_; }
 | 
			
		||||
  bool get_partial_updating() { return this->partial_updating_; }
 | 
			
		||||
  uint8_t get_temperature() { return this->temperature_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void draw_absolute_pixel_internal(int x, int y, Color color) override;
 | 
			
		||||
  void display1b_();
 | 
			
		||||
  void display3b_();
 | 
			
		||||
  void initialize_();
 | 
			
		||||
  bool partial_update_();
 | 
			
		||||
  void clean_fast_(uint8_t c, uint8_t rep);
 | 
			
		||||
 | 
			
		||||
  void hscan_start_(uint32_t d);
 | 
			
		||||
  void vscan_end_();
 | 
			
		||||
  void vscan_start_();
 | 
			
		||||
  void vscan_write_();
 | 
			
		||||
 | 
			
		||||
  void eink_off_();
 | 
			
		||||
  void eink_on_();
 | 
			
		||||
 | 
			
		||||
  void setup_pins_();
 | 
			
		||||
  void pins_z_state_();
 | 
			
		||||
  void pins_as_outputs_();
 | 
			
		||||
 | 
			
		||||
  int get_width_internal() override { return 800; }
 | 
			
		||||
 | 
			
		||||
  int get_height_internal() override { return 600; }
 | 
			
		||||
 | 
			
		||||
  size_t get_buffer_length_();
 | 
			
		||||
 | 
			
		||||
  int get_data_pin_mask_() {
 | 
			
		||||
    int data = 0;
 | 
			
		||||
    data |= (1 << this->display_data_0_pin_->get_pin());
 | 
			
		||||
    data |= (1 << this->display_data_1_pin_->get_pin());
 | 
			
		||||
    data |= (1 << this->display_data_2_pin_->get_pin());
 | 
			
		||||
    data |= (1 << this->display_data_3_pin_->get_pin());
 | 
			
		||||
    data |= (1 << this->display_data_4_pin_->get_pin());
 | 
			
		||||
    data |= (1 << this->display_data_5_pin_->get_pin());
 | 
			
		||||
    data |= (1 << this->display_data_6_pin_->get_pin());
 | 
			
		||||
    data |= (1 << this->display_data_7_pin_->get_pin());
 | 
			
		||||
    return data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t panel_on_ = 0;
 | 
			
		||||
  uint8_t temperature_;
 | 
			
		||||
 | 
			
		||||
  uint8_t *partial_buffer_{nullptr};
 | 
			
		||||
  uint8_t *partial_buffer_2_{nullptr};
 | 
			
		||||
 | 
			
		||||
  uint32_t full_update_every_;
 | 
			
		||||
  uint32_t partial_updates_{0};
 | 
			
		||||
 | 
			
		||||
  bool block_partial_;
 | 
			
		||||
  bool greyscale_;
 | 
			
		||||
  bool partial_updating_;
 | 
			
		||||
 | 
			
		||||
  GPIOPin *display_data_0_pin_;
 | 
			
		||||
  GPIOPin *display_data_1_pin_;
 | 
			
		||||
  GPIOPin *display_data_2_pin_;
 | 
			
		||||
  GPIOPin *display_data_3_pin_;
 | 
			
		||||
  GPIOPin *display_data_4_pin_;
 | 
			
		||||
  GPIOPin *display_data_5_pin_;
 | 
			
		||||
  GPIOPin *display_data_6_pin_;
 | 
			
		||||
  GPIOPin *display_data_7_pin_;
 | 
			
		||||
 | 
			
		||||
  GPIOPin *ckv_pin_;
 | 
			
		||||
  GPIOPin *cl_pin_;
 | 
			
		||||
  GPIOPin *gpio0_enable_pin_;
 | 
			
		||||
  GPIOPin *gmod_pin_;
 | 
			
		||||
  GPIOPin *le_pin_;
 | 
			
		||||
  GPIOPin *oe_pin_;
 | 
			
		||||
  GPIOPin *powerup_pin_;
 | 
			
		||||
  GPIOPin *sph_pin_;
 | 
			
		||||
  GPIOPin *spv_pin_;
 | 
			
		||||
  GPIOPin *vcom_pin_;
 | 
			
		||||
  GPIOPin *wakeup_pin_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace inkplate6
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -15,19 +15,18 @@ void MCP3008::setup() {
 | 
			
		||||
 | 
			
		||||
void MCP3008::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "MCP3008:");
 | 
			
		||||
  LOG_PIN("  CS Pin: ", this->cs_);
 | 
			
		||||
  LOG_PIN("  CS Pin:", this->cs_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float MCP3008::read_data_(uint8_t pin) {
 | 
			
		||||
  uint8_t data_msb = 0;
 | 
			
		||||
  uint8_t data_lsb = 0;
 | 
			
		||||
float MCP3008::read_data(uint8_t pin) {
 | 
			
		||||
  uint8_t data_msb, data_lsb = 0;
 | 
			
		||||
 | 
			
		||||
  uint8_t command = ((0x01 << 7) |          // start bit
 | 
			
		||||
                     ((pin & 0x07) << 4));  // channel number
 | 
			
		||||
 | 
			
		||||
  this->enable();
 | 
			
		||||
 | 
			
		||||
  this->transfer_byte(0x01);
 | 
			
		||||
 | 
			
		||||
  data_msb = this->transfer_byte(command) & 0x03;
 | 
			
		||||
  data_lsb = this->transfer_byte(0x00);
 | 
			
		||||
 | 
			
		||||
@@ -35,18 +34,29 @@ float MCP3008::read_data_(uint8_t pin) {
 | 
			
		||||
 | 
			
		||||
  int data = data_msb << 8 | data_lsb;
 | 
			
		||||
 | 
			
		||||
  return data / 1024.0f;
 | 
			
		||||
  return data / 1023.0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MCP3008Sensor::MCP3008Sensor(MCP3008 *parent, std::string name, uint8_t pin)
 | 
			
		||||
MCP3008Sensor::MCP3008Sensor(MCP3008 *parent, std::string name, uint8_t pin, float reference_voltage)
 | 
			
		||||
    : PollingComponent(1000), parent_(parent), pin_(pin) {
 | 
			
		||||
  this->set_name(name);
 | 
			
		||||
  this->reference_voltage_ = reference_voltage;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float MCP3008Sensor::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
void MCP3008Sensor::setup() { LOG_SENSOR("", "Setting up MCP3008 Sensor '%s'...", this); }
 | 
			
		||||
void MCP3008Sensor::update() {
 | 
			
		||||
  float value_v = this->parent_->read_data_(pin_);
 | 
			
		||||
  this->publish_state(value_v);
 | 
			
		||||
void MCP3008Sensor::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "MCP3008Sensor:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Pin: %u", this->pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Reference Voltage: %.2fV", this->reference_voltage_);
 | 
			
		||||
}
 | 
			
		||||
float MCP3008Sensor::sample() {
 | 
			
		||||
  float value_v = this->parent_->read_data(pin_);
 | 
			
		||||
  value_v = (value_v * this->reference_voltage_);
 | 
			
		||||
  return value_v;
 | 
			
		||||
}
 | 
			
		||||
void MCP3008Sensor::update() { this->publish_state(this->sample()); }
 | 
			
		||||
 | 
			
		||||
}  // namespace mcp3008
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -4,38 +4,41 @@
 | 
			
		||||
#include "esphome/core/esphal.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/spi/spi.h"
 | 
			
		||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace mcp3008 {
 | 
			
		||||
 | 
			
		||||
class MCP3008Sensor;
 | 
			
		||||
 | 
			
		||||
class MCP3008 : public Component,
 | 
			
		||||
                public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
 | 
			
		||||
                                      spi::DATA_RATE_1MHZ> {  // At 3.3V 2MHz is too fast 1.35MHz is about right
 | 
			
		||||
                                      spi::DATA_RATE_75KHZ> {  // Running at the slowest max speed supported by the
 | 
			
		||||
                                                               // mcp3008. 2.7v = 75ksps
 | 
			
		||||
 public:
 | 
			
		||||
  MCP3008() = default;
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  float read_data(uint8_t pin);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  float read_data_(uint8_t pin);
 | 
			
		||||
 | 
			
		||||
  friend class MCP3008Sensor;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class MCP3008Sensor : public PollingComponent, public sensor::Sensor {
 | 
			
		||||
class MCP3008Sensor : public PollingComponent, public sensor::Sensor, public voltage_sampler::VoltageSampler {
 | 
			
		||||
 public:
 | 
			
		||||
  MCP3008Sensor(MCP3008 *parent, std::string name, uint8_t pin);
 | 
			
		||||
  MCP3008Sensor(MCP3008 *parent, std::string name, uint8_t pin, float reference_voltage);
 | 
			
		||||
 | 
			
		||||
  void set_reference_voltage(float reference_voltage) { reference_voltage_ = reference_voltage; }
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  float sample() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  MCP3008 *parent_;
 | 
			
		||||
  uint8_t pin_;
 | 
			
		||||
  float reference_voltage_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace mcp3008
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,29 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor
 | 
			
		||||
from esphome.components import sensor, voltage_sampler
 | 
			
		||||
from esphome.const import CONF_ID, CONF_NUMBER, CONF_NAME
 | 
			
		||||
from . import mcp3008_ns, MCP3008
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ['voltage_sampler']
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['mcp3008']
 | 
			
		||||
 | 
			
		||||
MCP3008Sensor = mcp3008_ns.class_('MCP3008Sensor', sensor.Sensor, cg.PollingComponent)
 | 
			
		||||
 | 
			
		||||
MCP3008Sensor = mcp3008_ns.class_('MCP3008Sensor', sensor.Sensor, cg.PollingComponent,
 | 
			
		||||
                                  voltage_sampler.VoltageSampler)
 | 
			
		||||
CONF_REFERENCE_VOLTAGE = 'reference_voltage'
 | 
			
		||||
CONF_MCP3008_ID = 'mcp3008_id'
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(MCP3008Sensor),
 | 
			
		||||
    cv.GenerateID(CONF_MCP3008_ID): cv.use_id(MCP3008),
 | 
			
		||||
    cv.Required(CONF_NUMBER): cv.int_,
 | 
			
		||||
    cv.Optional(CONF_REFERENCE_VOLTAGE, default='3.3V'): cv.voltage,
 | 
			
		||||
}).extend(cv.polling_component_schema('1s'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    parent = yield cg.get_variable(config[CONF_MCP3008_ID])
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID], parent, config[CONF_NAME], config[CONF_NUMBER])
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID], parent, config[CONF_NAME],
 | 
			
		||||
                           config[CONF_NUMBER], config[CONF_REFERENCE_VOLTAGE])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield sensor.register_sensor(var, config)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								esphome/components/nfc/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								esphome/components/nfc/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ['@jesserockz']
 | 
			
		||||
 | 
			
		||||
nfc_ns = cg.esphome_ns.namespace('nfc')
 | 
			
		||||
 | 
			
		||||
NfcTag = nfc_ns.class_('NfcTag')
 | 
			
		||||
							
								
								
									
										106
									
								
								esphome/components/nfc/ndef_message.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								esphome/components/nfc/ndef_message.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
			
		||||
#include "ndef_message.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace nfc {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "nfc.ndef_message";
 | 
			
		||||
 | 
			
		||||
NdefMessage::NdefMessage(std::vector<uint8_t> &data) {
 | 
			
		||||
  ESP_LOGV(TAG, "Building NdefMessage with %zu bytes", data.size());
 | 
			
		||||
  uint8_t index = 0;
 | 
			
		||||
  while (index <= data.size()) {
 | 
			
		||||
    uint8_t tnf_byte = data[index++];
 | 
			
		||||
    bool me = tnf_byte & 0x40;
 | 
			
		||||
    bool sr = tnf_byte & 0x10;
 | 
			
		||||
    bool il = tnf_byte & 0x08;
 | 
			
		||||
    uint8_t tnf = tnf_byte & 0x07;
 | 
			
		||||
 | 
			
		||||
    ESP_LOGVV(TAG, "me=%s, sr=%s, il=%s, tnf=%d", YESNO(me), YESNO(sr), YESNO(il), tnf);
 | 
			
		||||
 | 
			
		||||
    auto record = new NdefRecord();
 | 
			
		||||
    record->set_tnf(tnf);
 | 
			
		||||
 | 
			
		||||
    uint8_t type_length = data[index++];
 | 
			
		||||
    uint32_t payload_length = 0;
 | 
			
		||||
    if (sr) {
 | 
			
		||||
      payload_length = data[index++];
 | 
			
		||||
    } else {
 | 
			
		||||
      payload_length = (static_cast<uint32_t>(data[index]) << 24) | (static_cast<uint32_t>(data[index + 1]) << 16) |
 | 
			
		||||
                       (static_cast<uint32_t>(data[index + 2]) << 8) | static_cast<uint32_t>(data[index + 3]);
 | 
			
		||||
      index += 4;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t id_length = 0;
 | 
			
		||||
    if (il) {
 | 
			
		||||
      id_length = data[index++];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ESP_LOGVV(TAG, "Lengths: type=%d, payload=%d, id=%d", type_length, payload_length, id_length);
 | 
			
		||||
 | 
			
		||||
    std::string type_str(data.begin() + index, data.begin() + index + type_length);
 | 
			
		||||
    record->set_type(type_str);
 | 
			
		||||
    index += type_length;
 | 
			
		||||
 | 
			
		||||
    if (il) {
 | 
			
		||||
      std::string id_str(data.begin() + index, data.begin() + index + id_length);
 | 
			
		||||
      record->set_id(id_str);
 | 
			
		||||
      index += id_length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t payload_identifier = 0x00;
 | 
			
		||||
    if (type_str == "U") {
 | 
			
		||||
      payload_identifier = data[index++];
 | 
			
		||||
      payload_length -= 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string payload_str(data.begin() + index, data.begin() + index + payload_length);
 | 
			
		||||
 | 
			
		||||
    if (payload_identifier > 0x00 && payload_identifier <= PAYLOAD_IDENTIFIERS_COUNT) {
 | 
			
		||||
      payload_str.insert(0, PAYLOAD_IDENTIFIERS[payload_identifier]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    record->set_payload(payload_str);
 | 
			
		||||
    index += payload_length;
 | 
			
		||||
 | 
			
		||||
    this->add_record(record);
 | 
			
		||||
    ESP_LOGV(TAG, "Adding record type %s = %s", record->get_type().c_str(), record->get_payload().c_str());
 | 
			
		||||
 | 
			
		||||
    if (me)
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool NdefMessage::add_record(NdefRecord *record) {
 | 
			
		||||
  if (this->records_.size() >= MAX_NDEF_RECORDS) {
 | 
			
		||||
    ESP_LOGE(TAG, "Too many records. Max: %d", MAX_NDEF_RECORDS);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  this->records_.push_back(record);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool NdefMessage::add_text_record(const std::string &text) { return this->add_text_record(text, "en"); };
 | 
			
		||||
 | 
			
		||||
bool NdefMessage::add_text_record(const std::string &text, const std::string &encoding) {
 | 
			
		||||
  std::string payload = to_string(text.length()) + encoding + text;
 | 
			
		||||
  auto r = new NdefRecord(TNF_WELL_KNOWN, "T", payload);
 | 
			
		||||
  return this->add_record(r);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool NdefMessage::add_uri_record(const std::string &uri) {
 | 
			
		||||
  auto r = new NdefRecord(TNF_WELL_KNOWN, "U", uri);
 | 
			
		||||
  return this->add_record(r);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> NdefMessage::encode() {
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
 | 
			
		||||
  for (uint8_t i = 0; i < this->records_.size(); i++) {
 | 
			
		||||
    auto encoded_record = this->records_[i]->encode(i == 0, (i + 1) == this->records_.size());
 | 
			
		||||
    data.insert(data.end(), encoded_record.begin(), encoded_record.end());
 | 
			
		||||
  }
 | 
			
		||||
  return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace nfc
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										31
									
								
								esphome/components/nfc/ndef_message.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								esphome/components/nfc/ndef_message.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "ndef_record.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace nfc {
 | 
			
		||||
 | 
			
		||||
static const uint8_t MAX_NDEF_RECORDS = 4;
 | 
			
		||||
 | 
			
		||||
class NdefMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  NdefMessage(){};
 | 
			
		||||
  NdefMessage(std::vector<uint8_t> &data);
 | 
			
		||||
 | 
			
		||||
  std::vector<NdefRecord *> get_records() { return this->records_; };
 | 
			
		||||
 | 
			
		||||
  bool add_record(NdefRecord *record);
 | 
			
		||||
  bool add_text_record(const std::string &text);
 | 
			
		||||
  bool add_text_record(const std::string &text, const std::string &encoding);
 | 
			
		||||
  bool add_uri_record(const std::string &uri);
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> encode();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::vector<NdefRecord *> records_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace nfc
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										85
									
								
								esphome/components/nfc/ndef_record.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								esphome/components/nfc/ndef_record.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
#include "ndef_record.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace nfc {
 | 
			
		||||
 | 
			
		||||
static const char* TAG = "nfc.ndef_record";
 | 
			
		||||
 | 
			
		||||
uint32_t NdefRecord::get_encoded_size() {
 | 
			
		||||
  uint32_t size = 2;
 | 
			
		||||
  if (this->payload_.length() > 255) {
 | 
			
		||||
    size += 4;
 | 
			
		||||
  } else {
 | 
			
		||||
    size += 1;
 | 
			
		||||
  }
 | 
			
		||||
  if (this->id_.length()) {
 | 
			
		||||
    size += 1;
 | 
			
		||||
  }
 | 
			
		||||
  size += (this->type_.length() + this->payload_.length() + this->id_.length());
 | 
			
		||||
  return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> NdefRecord::encode(bool first, bool last) {
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
 | 
			
		||||
  data.push_back(this->get_tnf_byte(first, last));
 | 
			
		||||
 | 
			
		||||
  data.push_back(this->type_.length());
 | 
			
		||||
 | 
			
		||||
  uint8_t payload_prefix = 0x00;
 | 
			
		||||
  uint8_t payload_prefix_length = 0x00;
 | 
			
		||||
  for (uint8_t i = 1; i < PAYLOAD_IDENTIFIERS_COUNT; i++) {
 | 
			
		||||
    std::string prefix = PAYLOAD_IDENTIFIERS[i];
 | 
			
		||||
    if (this->payload_.substr(0, prefix.length()).find(prefix) != std::string::npos) {
 | 
			
		||||
      payload_prefix = i;
 | 
			
		||||
      payload_prefix_length = prefix.length();
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint32_t payload_length = this->payload_.length() - payload_prefix_length + 1;
 | 
			
		||||
 | 
			
		||||
  if (payload_length <= 255) {
 | 
			
		||||
    data.push_back(payload_length);
 | 
			
		||||
  } else {
 | 
			
		||||
    data.push_back(0);
 | 
			
		||||
    data.push_back(0);
 | 
			
		||||
    data.push_back((payload_length >> 8) & 0xFF);
 | 
			
		||||
    data.push_back(payload_length & 0xFF);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->id_.length()) {
 | 
			
		||||
    data.push_back(this->id_.length());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  data.insert(data.end(), this->type_.begin(), this->type_.end());
 | 
			
		||||
 | 
			
		||||
  if (this->id_.length()) {
 | 
			
		||||
    data.insert(data.end(), this->id_.begin(), this->id_.end());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  data.push_back(payload_prefix);
 | 
			
		||||
 | 
			
		||||
  data.insert(data.end(), this->payload_.begin() + payload_prefix_length, this->payload_.end());
 | 
			
		||||
  return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t NdefRecord::get_tnf_byte(bool first, bool last) {
 | 
			
		||||
  uint8_t value = this->tnf_;
 | 
			
		||||
  if (first) {
 | 
			
		||||
    value = value | 0x80;
 | 
			
		||||
  }
 | 
			
		||||
  if (last) {
 | 
			
		||||
    value = value | 0x40;
 | 
			
		||||
  }
 | 
			
		||||
  if (this->payload_.length() <= 255) {
 | 
			
		||||
    value = value | 0x10;
 | 
			
		||||
  }
 | 
			
		||||
  if (this->id_.length()) {
 | 
			
		||||
    value = value | 0x08;
 | 
			
		||||
  }
 | 
			
		||||
  return value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace nfc
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										101
									
								
								esphome/components/nfc/ndef_record.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								esphome/components/nfc/ndef_record.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace nfc {
 | 
			
		||||
 | 
			
		||||
static const uint8_t TNF_EMPTY = 0x00;
 | 
			
		||||
static const uint8_t TNF_WELL_KNOWN = 0x01;
 | 
			
		||||
static const uint8_t TNF_MIME_MEDIA = 0x02;
 | 
			
		||||
static const uint8_t TNF_ABSOLUTE_URI = 0x03;
 | 
			
		||||
static const uint8_t TNF_EXTERNAL_TYPE = 0x04;
 | 
			
		||||
static const uint8_t TNF_UNKNOWN = 0x05;
 | 
			
		||||
static const uint8_t TNF_UNCHANGED = 0x06;
 | 
			
		||||
static const uint8_t TNF_RESERVED = 0x07;
 | 
			
		||||
 | 
			
		||||
static const uint8_t PAYLOAD_IDENTIFIERS_COUNT = 0x23;
 | 
			
		||||
static const char *PAYLOAD_IDENTIFIERS[] = {"",
 | 
			
		||||
                                            "http://www.",
 | 
			
		||||
                                            "https://www.",
 | 
			
		||||
                                            "http://",
 | 
			
		||||
                                            "https://",
 | 
			
		||||
                                            "tel:",
 | 
			
		||||
                                            "mailto:",
 | 
			
		||||
                                            "ftp://anonymous:anonymous@",
 | 
			
		||||
                                            "ftp://ftp.",
 | 
			
		||||
                                            "ftps://",
 | 
			
		||||
                                            "sftp://",
 | 
			
		||||
                                            "smb://",
 | 
			
		||||
                                            "nfs://",
 | 
			
		||||
                                            "ftp://",
 | 
			
		||||
                                            "dav://",
 | 
			
		||||
                                            "news:",
 | 
			
		||||
                                            "telnet://",
 | 
			
		||||
                                            "imap:",
 | 
			
		||||
                                            "rtsp://",
 | 
			
		||||
                                            "urn:",
 | 
			
		||||
                                            "pop:",
 | 
			
		||||
                                            "sip:",
 | 
			
		||||
                                            "sips:",
 | 
			
		||||
                                            "tftp:",
 | 
			
		||||
                                            "btspp://",
 | 
			
		||||
                                            "btl2cap://",
 | 
			
		||||
                                            "btgoep://",
 | 
			
		||||
                                            "tcpobex://",
 | 
			
		||||
                                            "irdaobex://",
 | 
			
		||||
                                            "file://",
 | 
			
		||||
                                            "urn:epc:id:",
 | 
			
		||||
                                            "urn:epc:tag:",
 | 
			
		||||
                                            "urn:epc:pat:",
 | 
			
		||||
                                            "urn:epc:raw:",
 | 
			
		||||
                                            "urn:epc:",
 | 
			
		||||
                                            "urn:nfc:"};
 | 
			
		||||
 | 
			
		||||
class NdefRecord {
 | 
			
		||||
 public:
 | 
			
		||||
  NdefRecord(){};
 | 
			
		||||
  NdefRecord(uint8_t tnf, const std::string &type, const std::string &payload) {
 | 
			
		||||
    this->tnf_ = tnf;
 | 
			
		||||
    this->type_ = type;
 | 
			
		||||
    this->set_payload(payload);
 | 
			
		||||
  };
 | 
			
		||||
  NdefRecord(uint8_t tnf, const std::string &type, const std::string &payload, const std::string &id) {
 | 
			
		||||
    this->tnf_ = tnf;
 | 
			
		||||
    this->type_ = type;
 | 
			
		||||
    this->set_payload(payload);
 | 
			
		||||
    this->id_ = id;
 | 
			
		||||
  };
 | 
			
		||||
  NdefRecord(const NdefRecord &rhs) {
 | 
			
		||||
    this->tnf_ = rhs.tnf_;
 | 
			
		||||
    this->type_ = rhs.type_;
 | 
			
		||||
    this->payload_ = rhs.payload_;
 | 
			
		||||
    this->payload_identifier_ = rhs.payload_identifier_;
 | 
			
		||||
    this->id_ = rhs.id_;
 | 
			
		||||
  };
 | 
			
		||||
  void set_tnf(uint8_t tnf) { this->tnf_ = tnf; };
 | 
			
		||||
  void set_type(const std::string &type) { this->type_ = type; };
 | 
			
		||||
  void set_payload_identifier(uint8_t payload_identifier) { this->payload_identifier_ = payload_identifier; };
 | 
			
		||||
  void set_payload(const std::string &payload) { this->payload_ = payload; };
 | 
			
		||||
  void set_id(const std::string &id) { this->id_ = id; };
 | 
			
		||||
 | 
			
		||||
  uint32_t get_encoded_size();
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> encode(bool first, bool last);
 | 
			
		||||
  uint8_t get_tnf_byte(bool first, bool last);
 | 
			
		||||
 | 
			
		||||
  const std::string &get_type() { return this->type_; };
 | 
			
		||||
  const std::string &get_id() { return this->id_; };
 | 
			
		||||
  const std::string &get_payload() { return this->payload_; };
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint8_t tnf_;
 | 
			
		||||
  std::string type_;
 | 
			
		||||
  uint8_t payload_identifier_;
 | 
			
		||||
  std::string payload_;
 | 
			
		||||
  std::string id_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace nfc
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										107
									
								
								esphome/components/nfc/nfc.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								esphome/components/nfc/nfc.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
#include "nfc.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace nfc {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "nfc";
 | 
			
		||||
 | 
			
		||||
std::string format_uid(std::vector<uint8_t> &uid) {
 | 
			
		||||
  char buf[(uid.size() * 2) + uid.size() - 1];
 | 
			
		||||
  int offset = 0;
 | 
			
		||||
  for (uint8_t i = 0; i < uid.size(); i++) {
 | 
			
		||||
    const char *format = "%02X";
 | 
			
		||||
    if (i + 1 < uid.size())
 | 
			
		||||
      format = "%02X-";
 | 
			
		||||
    offset += sprintf(buf + offset, format, uid[i]);
 | 
			
		||||
  }
 | 
			
		||||
  return std::string(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string format_bytes(std::vector<uint8_t> &bytes) {
 | 
			
		||||
  char buf[(bytes.size() * 2) + bytes.size() - 1];
 | 
			
		||||
  int offset = 0;
 | 
			
		||||
  for (uint8_t i = 0; i < bytes.size(); i++) {
 | 
			
		||||
    const char *format = "%02X";
 | 
			
		||||
    if (i + 1 < bytes.size())
 | 
			
		||||
      format = "%02X ";
 | 
			
		||||
    offset += sprintf(buf + offset, format, bytes[i]);
 | 
			
		||||
  }
 | 
			
		||||
  return std::string(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t guess_tag_type(uint8_t uid_length) {
 | 
			
		||||
  if (uid_length == 4) {
 | 
			
		||||
    return TAG_TYPE_MIFARE_CLASSIC;
 | 
			
		||||
  } else {
 | 
			
		||||
    return TAG_TYPE_2;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t get_mifare_classic_ndef_start_index(std::vector<uint8_t> &data) {
 | 
			
		||||
  for (uint8_t i = 0; i < MIFARE_CLASSIC_BLOCK_SIZE; i++) {
 | 
			
		||||
    if (data[i] == 0x00) {
 | 
			
		||||
      // Do nothing, skip
 | 
			
		||||
    } else if (data[i] == 0x03) {
 | 
			
		||||
      return i;
 | 
			
		||||
    } else {
 | 
			
		||||
      return -2;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool decode_mifare_classic_tlv(std::vector<uint8_t> &data, uint32_t &message_length, uint8_t &message_start_index) {
 | 
			
		||||
  uint8_t i = get_mifare_classic_ndef_start_index(data);
 | 
			
		||||
  if (i < 0 || data[i] != 0x03) {
 | 
			
		||||
    ESP_LOGE(TAG, "Error, Can't decode message length.");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (data[i + 1] == 0xFF) {
 | 
			
		||||
    message_length = ((0xFF & data[i + 2]) << 8) | (0xFF & data[i + 3]);
 | 
			
		||||
    message_start_index = i + MIFARE_CLASSIC_LONG_TLV_SIZE;
 | 
			
		||||
  } else {
 | 
			
		||||
    message_length = data[i + 1];
 | 
			
		||||
    message_start_index = i + MIFARE_CLASSIC_SHORT_TLV_SIZE;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t get_mifare_ultralight_buffer_size(uint32_t message_length) {
 | 
			
		||||
  uint32_t buffer_size = message_length + 2 + 1;
 | 
			
		||||
  if (buffer_size % MIFARE_ULTRALIGHT_READ_SIZE != 0)
 | 
			
		||||
    buffer_size = ((buffer_size / MIFARE_ULTRALIGHT_READ_SIZE) + 1) * MIFARE_ULTRALIGHT_READ_SIZE;
 | 
			
		||||
  return buffer_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t get_mifare_classic_buffer_size(uint32_t message_length) {
 | 
			
		||||
  uint32_t buffer_size = message_length;
 | 
			
		||||
  if (message_length < 255) {
 | 
			
		||||
    buffer_size += MIFARE_CLASSIC_SHORT_TLV_SIZE + 1;
 | 
			
		||||
  } else {
 | 
			
		||||
    buffer_size += MIFARE_CLASSIC_LONG_TLV_SIZE + 1;
 | 
			
		||||
  }
 | 
			
		||||
  if (buffer_size % MIFARE_CLASSIC_BLOCK_SIZE != 0) {
 | 
			
		||||
    buffer_size = ((buffer_size / MIFARE_CLASSIC_BLOCK_SIZE) + 1) * MIFARE_CLASSIC_BLOCK_SIZE;
 | 
			
		||||
  }
 | 
			
		||||
  return buffer_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mifare_classic_is_first_block(uint8_t block_num) {
 | 
			
		||||
  if (block_num < 128) {
 | 
			
		||||
    return (block_num % 4 == 0);
 | 
			
		||||
  } else {
 | 
			
		||||
    return (block_num % 16 == 0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mifare_classic_is_trailer_block(uint8_t block_num) {
 | 
			
		||||
  if (block_num < 128) {
 | 
			
		||||
    return ((block_num + 1) % 4 == 0);
 | 
			
		||||
  } else {
 | 
			
		||||
    return ((block_num + 1) % 16 == 0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace nfc
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										57
									
								
								esphome/components/nfc/nfc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								esphome/components/nfc/nfc.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "ndef_record.h"
 | 
			
		||||
#include "ndef_message.h"
 | 
			
		||||
#include "nfc_tag.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace nfc {
 | 
			
		||||
 | 
			
		||||
static const uint8_t MIFARE_CLASSIC_BLOCK_SIZE = 16;
 | 
			
		||||
static const uint8_t MIFARE_CLASSIC_LONG_TLV_SIZE = 4;
 | 
			
		||||
static const uint8_t MIFARE_CLASSIC_SHORT_TLV_SIZE = 2;
 | 
			
		||||
 | 
			
		||||
static const uint8_t MIFARE_ULTRALIGHT_PAGE_SIZE = 4;
 | 
			
		||||
static const uint8_t MIFARE_ULTRALIGHT_READ_SIZE = 4;
 | 
			
		||||
static const uint8_t MIFARE_ULTRALIGHT_DATA_START_PAGE = 4;
 | 
			
		||||
static const uint8_t MIFARE_ULTRALIGHT_MAX_PAGE = 63;
 | 
			
		||||
 | 
			
		||||
static const uint8_t TAG_TYPE_MIFARE_CLASSIC = 0;
 | 
			
		||||
static const uint8_t TAG_TYPE_1 = 1;
 | 
			
		||||
static const uint8_t TAG_TYPE_2 = 2;
 | 
			
		||||
static const uint8_t TAG_TYPE_3 = 3;
 | 
			
		||||
static const uint8_t TAG_TYPE_4 = 4;
 | 
			
		||||
static const uint8_t TAG_TYPE_UNKNOWN = 99;
 | 
			
		||||
 | 
			
		||||
// Mifare Commands
 | 
			
		||||
static const uint8_t MIFARE_CMD_AUTH_A = 0x60;
 | 
			
		||||
static const uint8_t MIFARE_CMD_AUTH_B = 0x61;
 | 
			
		||||
static const uint8_t MIFARE_CMD_READ = 0x30;
 | 
			
		||||
static const uint8_t MIFARE_CMD_WRITE = 0xA0;
 | 
			
		||||
static const uint8_t MIFARE_CMD_WRITE_ULTRALIGHT = 0xA2;
 | 
			
		||||
 | 
			
		||||
static const char *MIFARE_CLASSIC = "Mifare Classic";
 | 
			
		||||
static const char *NFC_FORUM_TYPE_2 = "NFC Forum Type 2";
 | 
			
		||||
static const char *ERROR = "Error";
 | 
			
		||||
 | 
			
		||||
static const uint8_t DEFAULT_KEY[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 | 
			
		||||
static const uint8_t NDEF_KEY[6] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7};
 | 
			
		||||
static const uint8_t MAD_KEY[6] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
 | 
			
		||||
 | 
			
		||||
std::string format_uid(std::vector<uint8_t> &uid);
 | 
			
		||||
std::string format_bytes(std::vector<uint8_t> &bytes);
 | 
			
		||||
 | 
			
		||||
uint8_t guess_tag_type(uint8_t uid_length);
 | 
			
		||||
uint8_t get_mifare_classic_ndef_start_index(std::vector<uint8_t> &data);
 | 
			
		||||
bool decode_mifare_classic_tlv(std::vector<uint8_t> &data, uint32_t &message_length, uint8_t &message_start_index);
 | 
			
		||||
uint32_t get_mifare_classic_buffer_size(uint32_t message_length);
 | 
			
		||||
 | 
			
		||||
bool mifare_classic_is_first_block(uint8_t block_num);
 | 
			
		||||
bool mifare_classic_is_trailer_block(uint8_t block_num);
 | 
			
		||||
 | 
			
		||||
uint32_t get_mifare_ultralight_buffer_size(uint32_t message_length);
 | 
			
		||||
 | 
			
		||||
}  // namespace nfc
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										9
									
								
								esphome/components/nfc/nfc_tag.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								esphome/components/nfc/nfc_tag.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
#include "nfc_tag.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace nfc {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "nfc.tag";
 | 
			
		||||
 | 
			
		||||
}  // namespace nfc
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										47
									
								
								esphome/components/nfc/nfc_tag.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								esphome/components/nfc/nfc_tag.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "ndef_message.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace nfc {
 | 
			
		||||
 | 
			
		||||
class NfcTag {
 | 
			
		||||
 public:
 | 
			
		||||
  NfcTag() {
 | 
			
		||||
    this->uid_ = {};
 | 
			
		||||
    this->tag_type_ = "Unknown";
 | 
			
		||||
  };
 | 
			
		||||
  NfcTag(std::vector<uint8_t> &uid) {
 | 
			
		||||
    this->uid_ = uid;
 | 
			
		||||
    this->tag_type_ = "Unknown";
 | 
			
		||||
  };
 | 
			
		||||
  NfcTag(std::vector<uint8_t> &uid, const std::string &tag_type) {
 | 
			
		||||
    this->uid_ = uid;
 | 
			
		||||
    this->tag_type_ = tag_type;
 | 
			
		||||
  };
 | 
			
		||||
  NfcTag(std::vector<uint8_t> &uid, const std::string &tag_type, nfc::NdefMessage *ndef_message) {
 | 
			
		||||
    this->uid_ = uid;
 | 
			
		||||
    this->tag_type_ = tag_type;
 | 
			
		||||
    this->ndef_message_ = ndef_message;
 | 
			
		||||
  };
 | 
			
		||||
  NfcTag(std::vector<uint8_t> &uid, const std::string &tag_type, std::vector<uint8_t> &ndef_data) {
 | 
			
		||||
    this->uid_ = uid;
 | 
			
		||||
    this->tag_type_ = tag_type;
 | 
			
		||||
    this->ndef_message_ = new NdefMessage(ndef_data);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> &get_uid() { return this->uid_; };
 | 
			
		||||
  const std::string &get_tag_type() { return this->tag_type_; };
 | 
			
		||||
  bool has_ndef_message() { return this->ndef_message_ != nullptr; };
 | 
			
		||||
  NdefMessage *get_ndef_message() { return this->ndef_message_; };
 | 
			
		||||
  void set_ndef_message(NdefMessage *ndef_message) { this->ndef_message_ = ndef_message; };
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::vector<uint8_t> uid_;
 | 
			
		||||
  std::string tag_type_;
 | 
			
		||||
  NdefMessage *ndef_message_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace nfc
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,24 +1,34 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.const import CONF_ON_TAG, CONF_TRIGGER_ID
 | 
			
		||||
from esphome.components import nfc
 | 
			
		||||
from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID
 | 
			
		||||
from esphome.core import coroutine
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ['@OttoWinter', '@jesserockz']
 | 
			
		||||
AUTO_LOAD = ['binary_sensor']
 | 
			
		||||
AUTO_LOAD = ['binary_sensor', 'nfc']
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
CONF_PN532_ID = 'pn532_id'
 | 
			
		||||
CONF_ON_FINISHED_WRITE = 'on_finished_write'
 | 
			
		||||
 | 
			
		||||
pn532_ns = cg.esphome_ns.namespace('pn532')
 | 
			
		||||
PN532 = pn532_ns.class_('PN532', cg.PollingComponent)
 | 
			
		||||
 | 
			
		||||
PN532Trigger = pn532_ns.class_('PN532Trigger', automation.Trigger.template(cg.std_string))
 | 
			
		||||
PN532OnTagTrigger = pn532_ns.class_('PN532OnTagTrigger',
 | 
			
		||||
                                    automation.Trigger.template(cg.std_string, nfc.NfcTag))
 | 
			
		||||
PN532OnFinishedWriteTrigger = pn532_ns.class_('PN532OnFinishedWriteTrigger',
 | 
			
		||||
                                              automation.Trigger.template())
 | 
			
		||||
 | 
			
		||||
PN532IsWritingCondition = pn532_ns.class_('PN532IsWritingCondition', automation.Condition)
 | 
			
		||||
 | 
			
		||||
PN532_SCHEMA = cv.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(PN532),
 | 
			
		||||
    cv.Optional(CONF_ON_TAG): automation.validate_automation({
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PN532Trigger),
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PN532OnTagTrigger),
 | 
			
		||||
    }),
 | 
			
		||||
    cv.Optional(CONF_ON_FINISHED_WRITE): automation.validate_automation({
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PN532OnFinishedWriteTrigger),
 | 
			
		||||
    }),
 | 
			
		||||
}).extend(cv.polling_component_schema('1s'))
 | 
			
		||||
 | 
			
		||||
@@ -36,4 +46,18 @@ def setup_pn532(var, config):
 | 
			
		||||
    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)
 | 
			
		||||
        yield automation.build_automation(trigger, [(cg.std_string, 'x'), (nfc.NfcTag, 'tag')],
 | 
			
		||||
                                          conf)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_FINISHED_WRITE, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        yield automation.build_automation(trigger, [], conf)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_condition('pn532.is_writing', PN532IsWritingCondition, cv.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.use_id(PN532),
 | 
			
		||||
}))
 | 
			
		||||
def pn532_is_writing_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    var = cg.new_Pvariable(condition_id, template_arg)
 | 
			
		||||
    yield cg.register_parented(var, config[CONF_ID])
 | 
			
		||||
    yield var
 | 
			
		||||
 
 | 
			
		||||
@@ -11,18 +11,6 @@ namespace pn532 {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "pn532";
 | 
			
		||||
 | 
			
		||||
std::string format_uid(std::vector<uint8_t> &uid) {
 | 
			
		||||
  char buf[32];
 | 
			
		||||
  int offset = 0;
 | 
			
		||||
  for (uint8_t i = 0; i < uid.size(); i++) {
 | 
			
		||||
    const char *format = "%02X";
 | 
			
		||||
    if (i + 1 < uid.size())
 | 
			
		||||
      format = "%02X-";
 | 
			
		||||
    offset += sprintf(buf + offset, format, uid[i]);
 | 
			
		||||
  }
 | 
			
		||||
  return std::string(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PN532::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up PN532...");
 | 
			
		||||
 | 
			
		||||
@@ -152,23 +140,56 @@ void PN532::loop() {
 | 
			
		||||
 | 
			
		||||
  this->current_uid_ = nfcid;
 | 
			
		||||
 | 
			
		||||
  for (auto *trigger : this->triggers_)
 | 
			
		||||
    trigger->process(nfcid);
 | 
			
		||||
  if (next_task_ == READ) {
 | 
			
		||||
    auto tag = this->read_tag_(nfcid);
 | 
			
		||||
    for (auto *trigger : this->triggers_)
 | 
			
		||||
      trigger->process(tag);
 | 
			
		||||
 | 
			
		||||
  if (report) {
 | 
			
		||||
    ESP_LOGD(TAG, "Found new tag '%s'", format_uid(nfcid).c_str());
 | 
			
		||||
    if (report) {
 | 
			
		||||
      ESP_LOGD(TAG, "Found new tag '%s'", nfc::format_uid(nfcid).c_str());
 | 
			
		||||
      if (tag->has_ndef_message()) {
 | 
			
		||||
        auto message = tag->get_ndef_message();
 | 
			
		||||
        auto records = message->get_records();
 | 
			
		||||
        ESP_LOGD(TAG, "  NDEF formatted records:");
 | 
			
		||||
        for (auto &record : records) {
 | 
			
		||||
          ESP_LOGD(TAG, "    %s - %s", record->get_type().c_str(), record->get_payload().c_str());
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else if (next_task_ == CLEAN) {
 | 
			
		||||
    ESP_LOGD(TAG, "  Tag cleaning...");
 | 
			
		||||
    if (!this->clean_tag_(nfcid)) {
 | 
			
		||||
      ESP_LOGE(TAG, "  Tag was not fully cleaned successfully");
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGD(TAG, "  Tag cleaned!");
 | 
			
		||||
  } else if (next_task_ == FORMAT) {
 | 
			
		||||
    ESP_LOGD(TAG, "  Tag formatting...");
 | 
			
		||||
    if (!this->format_tag_(nfcid)) {
 | 
			
		||||
      ESP_LOGE(TAG, "Error formatting tag as NDEF");
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGD(TAG, "  Tag formatted!");
 | 
			
		||||
  } else if (next_task_ == WRITE) {
 | 
			
		||||
    if (this->next_task_message_to_write_ != nullptr) {
 | 
			
		||||
      ESP_LOGD(TAG, "  Tag writing...");
 | 
			
		||||
      ESP_LOGD(TAG, "  Tag formatting...");
 | 
			
		||||
      if (!this->format_tag_(nfcid)) {
 | 
			
		||||
        ESP_LOGE(TAG, "  Tag could not be formatted for writing");
 | 
			
		||||
      } else {
 | 
			
		||||
        ESP_LOGD(TAG, "  Writing NDEF data");
 | 
			
		||||
        if (!this->write_tag_(nfcid, this->next_task_message_to_write_)) {
 | 
			
		||||
          ESP_LOGE(TAG, "  Failed to write message to tag");
 | 
			
		||||
        }
 | 
			
		||||
        ESP_LOGD(TAG, "  Finished writing NDEF data");
 | 
			
		||||
        delete this->next_task_message_to_write_;
 | 
			
		||||
        this->next_task_message_to_write_ = nullptr;
 | 
			
		||||
        this->on_finished_write_callback_.call();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->turn_off_rf_();
 | 
			
		||||
}
 | 
			
		||||
  this->read_mode();
 | 
			
		||||
 | 
			
		||||
void PN532::turn_off_rf_() {
 | 
			
		||||
  ESP_LOGVV(TAG, "Turning RF field OFF");
 | 
			
		||||
  this->write_command_({
 | 
			
		||||
      PN532_COMMAND_RFCONFIGURATION,
 | 
			
		||||
      0x1,  // RF Field
 | 
			
		||||
      0x0   // Off
 | 
			
		||||
  });
 | 
			
		||||
  this->turn_off_rf_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::write_command_(const std::vector<uint8_t> &data) {
 | 
			
		||||
@@ -208,6 +229,22 @@ bool PN532::write_command_(const std::vector<uint8_t> &data) {
 | 
			
		||||
  return this->read_ack_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::read_ack_() {
 | 
			
		||||
  ESP_LOGVV(TAG, "Reading ACK...");
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
  if (!this->read_data(data, 6)) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool matches = (data[1] == 0x00 &&                     // preamble
 | 
			
		||||
                  data[2] == 0x00 &&                     // start of packet
 | 
			
		||||
                  data[3] == 0xFF && data[4] == 0x00 &&  // ACK packet code
 | 
			
		||||
                  data[5] == 0xFF && data[6] == 0x00);   // postamble
 | 
			
		||||
  ESP_LOGVV(TAG, "ACK valid: %s", YESNO(matches));
 | 
			
		||||
  return matches;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::read_response_(uint8_t command, std::vector<uint8_t> &data) {
 | 
			
		||||
  ESP_LOGV(TAG, "Reading response");
 | 
			
		||||
  uint8_t len = this->read_response_length_();
 | 
			
		||||
@@ -258,13 +295,6 @@ bool PN532::read_response_(uint8_t command, std::vector<uint8_t> &data) {
 | 
			
		||||
  data.erase(data.begin(), data.begin() + 2);  // Remove TFI and command code
 | 
			
		||||
  data.erase(data.end() - 2, data.end());      // Remove checksum and postamble
 | 
			
		||||
 | 
			
		||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
 | 
			
		||||
  ESP_LOGD(TAG, "PN532 Data Frame: (%u)", data.size());  // NOLINT
 | 
			
		||||
  for (uint8_t dat : data) {
 | 
			
		||||
    ESP_LOGD(TAG, "  0x%02X", dat);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -299,20 +329,81 @@ uint8_t PN532::read_response_length_() {
 | 
			
		||||
  return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::read_ack_() {
 | 
			
		||||
  ESP_LOGVV(TAG, "Reading ACK...");
 | 
			
		||||
void PN532::turn_off_rf_() {
 | 
			
		||||
  ESP_LOGVV(TAG, "Turning RF field OFF");
 | 
			
		||||
  this->write_command_({
 | 
			
		||||
      PN532_COMMAND_RFCONFIGURATION,
 | 
			
		||||
      0x01,  // RF Field
 | 
			
		||||
      0x00,  // Off
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
  if (!this->read_data(data, 6)) {
 | 
			
		||||
    return false;
 | 
			
		||||
nfc::NfcTag *PN532::read_tag_(std::vector<uint8_t> &uid) {
 | 
			
		||||
  uint8_t type = nfc::guess_tag_type(uid.size());
 | 
			
		||||
 | 
			
		||||
  if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
 | 
			
		||||
    ESP_LOGD(TAG, "Mifare classic");
 | 
			
		||||
    return this->read_mifare_classic_tag_(uid);
 | 
			
		||||
  } else if (type == nfc::TAG_TYPE_2) {
 | 
			
		||||
    ESP_LOGD(TAG, "Mifare ultralight");
 | 
			
		||||
    return this->read_mifare_ultralight_tag_(uid);
 | 
			
		||||
  } else if (type == nfc::TAG_TYPE_UNKNOWN) {
 | 
			
		||||
    ESP_LOGV(TAG, "Cannot determine tag type");
 | 
			
		||||
    return new nfc::NfcTag(uid);
 | 
			
		||||
  } else {
 | 
			
		||||
    return new nfc::NfcTag(uid);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  bool matches = (data[1] == 0x00 &&                     // preamble
 | 
			
		||||
                  data[2] == 0x00 &&                     // start of packet
 | 
			
		||||
                  data[3] == 0xFF && data[4] == 0x00 &&  // ACK packet code
 | 
			
		||||
                  data[5] == 0xFF && data[6] == 0x00);   // postamble
 | 
			
		||||
  ESP_LOGVV(TAG, "ACK valid: %s", YESNO(matches));
 | 
			
		||||
  return matches;
 | 
			
		||||
void PN532::read_mode() {
 | 
			
		||||
  this->next_task_ = READ;
 | 
			
		||||
  ESP_LOGD(TAG, "Waiting to read next tag");
 | 
			
		||||
}
 | 
			
		||||
void PN532::clean_mode() {
 | 
			
		||||
  this->next_task_ = CLEAN;
 | 
			
		||||
  ESP_LOGD(TAG, "Waiting to clean next tag");
 | 
			
		||||
}
 | 
			
		||||
void PN532::format_mode() {
 | 
			
		||||
  this->next_task_ = FORMAT;
 | 
			
		||||
  ESP_LOGD(TAG, "Waiting to format next tag");
 | 
			
		||||
}
 | 
			
		||||
void PN532::write_mode(nfc::NdefMessage *message) {
 | 
			
		||||
  this->next_task_ = WRITE;
 | 
			
		||||
  this->next_task_message_to_write_ = message;
 | 
			
		||||
  ESP_LOGD(TAG, "Waiting to write next tag");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::clean_tag_(std::vector<uint8_t> &uid) {
 | 
			
		||||
  uint8_t type = nfc::guess_tag_type(uid.size());
 | 
			
		||||
  if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
 | 
			
		||||
    return this->format_mifare_classic_mifare_(uid);
 | 
			
		||||
  } else if (type == nfc::TAG_TYPE_2) {
 | 
			
		||||
    return this->clean_mifare_ultralight_();
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGE(TAG, "Unsupported Tag for formatting");
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::format_tag_(std::vector<uint8_t> &uid) {
 | 
			
		||||
  uint8_t type = nfc::guess_tag_type(uid.size());
 | 
			
		||||
  if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
 | 
			
		||||
    return this->format_mifare_classic_ndef_(uid);
 | 
			
		||||
  } else if (type == nfc::TAG_TYPE_2) {
 | 
			
		||||
    return this->clean_mifare_ultralight_();
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGE(TAG, "Unsupported Tag for formatting");
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::write_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
 | 
			
		||||
  uint8_t type = nfc::guess_tag_type(uid.size());
 | 
			
		||||
  if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
 | 
			
		||||
    return this->write_mifare_classic_tag_(uid, message);
 | 
			
		||||
  } else if (type == nfc::TAG_TYPE_2) {
 | 
			
		||||
    return this->write_mifare_ultralight_tag_(uid, message);
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGE(TAG, "Unsupported Tag for formatting");
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float PN532::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
@@ -350,7 +441,7 @@ bool PN532BinarySensor::process(std::vector<uint8_t> &data) {
 | 
			
		||||
  this->found_ = true;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
void PN532Trigger::process(std::vector<uint8_t> &data) { this->trigger(format_uid(data)); }
 | 
			
		||||
void PN532OnTagTrigger::process(nfc::NfcTag *tag) { this->trigger(nfc::format_uid(tag->get_uid()), *tag); }
 | 
			
		||||
 | 
			
		||||
}  // namespace pn532
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,8 @@
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#include "esphome/components/nfc/nfc_tag.h"
 | 
			
		||||
#include "esphome/components/nfc/nfc.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace pn532 {
 | 
			
		||||
@@ -14,7 +16,7 @@ static const uint8_t PN532_COMMAND_INDATAEXCHANGE = 0x40;
 | 
			
		||||
static const uint8_t PN532_COMMAND_INLISTPASSIVETARGET = 0x4A;
 | 
			
		||||
 | 
			
		||||
class PN532BinarySensor;
 | 
			
		||||
class PN532Trigger;
 | 
			
		||||
class PN532OnTagTrigger;
 | 
			
		||||
 | 
			
		||||
class PN532 : public PollingComponent {
 | 
			
		||||
 public:
 | 
			
		||||
@@ -28,7 +30,18 @@ class PN532 : public PollingComponent {
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void register_tag(PN532BinarySensor *tag) { this->binary_sensors_.push_back(tag); }
 | 
			
		||||
  void register_trigger(PN532Trigger *trig) { this->triggers_.push_back(trig); }
 | 
			
		||||
  void register_trigger(PN532OnTagTrigger *trig) { this->triggers_.push_back(trig); }
 | 
			
		||||
 | 
			
		||||
  void add_on_finished_write_callback(std::function<void()> callback) {
 | 
			
		||||
    this->on_finished_write_callback_.add(std::move(callback));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool is_writing() { return this->next_task_ != READ; };
 | 
			
		||||
 | 
			
		||||
  void read_mode();
 | 
			
		||||
  void clean_mode();
 | 
			
		||||
  void format_mode();
 | 
			
		||||
  void write_mode(nfc::NdefMessage *message);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void turn_off_rf_();
 | 
			
		||||
@@ -40,15 +53,46 @@ class PN532 : public PollingComponent {
 | 
			
		||||
  virtual bool write_data(const std::vector<uint8_t> &data) = 0;
 | 
			
		||||
  virtual bool read_data(std::vector<uint8_t> &data, uint8_t len) = 0;
 | 
			
		||||
 | 
			
		||||
  nfc::NfcTag *read_tag_(std::vector<uint8_t> &uid);
 | 
			
		||||
 | 
			
		||||
  bool format_tag_(std::vector<uint8_t> &uid);
 | 
			
		||||
  bool clean_tag_(std::vector<uint8_t> &uid);
 | 
			
		||||
  bool write_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);
 | 
			
		||||
 | 
			
		||||
  nfc::NfcTag *read_mifare_classic_tag_(std::vector<uint8_t> &uid);
 | 
			
		||||
  bool read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data);
 | 
			
		||||
  bool write_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data);
 | 
			
		||||
  bool auth_mifare_classic_block_(std::vector<uint8_t> &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key);
 | 
			
		||||
  bool format_mifare_classic_mifare_(std::vector<uint8_t> &uid);
 | 
			
		||||
  bool format_mifare_classic_ndef_(std::vector<uint8_t> &uid);
 | 
			
		||||
  bool write_mifare_classic_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);
 | 
			
		||||
 | 
			
		||||
  nfc::NfcTag *read_mifare_ultralight_tag_(std::vector<uint8_t> &uid);
 | 
			
		||||
  bool read_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &data);
 | 
			
		||||
  bool is_mifare_ultralight_formatted_();
 | 
			
		||||
  uint16_t read_mifare_ultralight_capacity_();
 | 
			
		||||
  bool find_mifare_ultralight_ndef_(uint8_t &message_length, uint8_t &message_start_index);
 | 
			
		||||
  bool write_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &write_data);
 | 
			
		||||
  bool write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);
 | 
			
		||||
  bool clean_mifare_ultralight_();
 | 
			
		||||
 | 
			
		||||
  bool requested_read_{false};
 | 
			
		||||
  std::vector<PN532BinarySensor *> binary_sensors_;
 | 
			
		||||
  std::vector<PN532Trigger *> triggers_;
 | 
			
		||||
  std::vector<PN532OnTagTrigger *> triggers_;
 | 
			
		||||
  std::vector<uint8_t> current_uid_;
 | 
			
		||||
  nfc::NdefMessage *next_task_message_to_write_;
 | 
			
		||||
  enum NfcTask {
 | 
			
		||||
    READ = 0,
 | 
			
		||||
    CLEAN,
 | 
			
		||||
    FORMAT,
 | 
			
		||||
    WRITE,
 | 
			
		||||
  } next_task_{READ};
 | 
			
		||||
  enum PN532Error {
 | 
			
		||||
    NONE = 0,
 | 
			
		||||
    WAKEUP_FAILED,
 | 
			
		||||
    SAM_COMMAND_FAILED,
 | 
			
		||||
  } error_code_{NONE};
 | 
			
		||||
  CallbackManager<void()> on_finished_write_callback_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PN532BinarySensor : public binary_sensor::BinarySensor {
 | 
			
		||||
@@ -69,9 +113,21 @@ class PN532BinarySensor : public binary_sensor::BinarySensor {
 | 
			
		||||
  bool found_{false};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PN532Trigger : public Trigger<std::string> {
 | 
			
		||||
class PN532OnTagTrigger : public Trigger<std::string, nfc::NfcTag> {
 | 
			
		||||
 public:
 | 
			
		||||
  void process(std::vector<uint8_t> &data);
 | 
			
		||||
  void process(nfc::NfcTag *tag);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PN532OnFinishedWriteTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit PN532OnFinishedWriteTrigger(PN532 *parent) {
 | 
			
		||||
    parent->add_on_finished_write_callback([this]() { this->trigger(); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class PN532IsWritingCondition : public Condition<Ts...>, public Parented<PN532> {
 | 
			
		||||
 public:
 | 
			
		||||
  bool check(Ts... x) override { return this->parent_->is_writing(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace pn532
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										249
									
								
								esphome/components/pn532/pn532_mifare_classic.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								esphome/components/pn532/pn532_mifare_classic.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,249 @@
 | 
			
		||||
#include "pn532.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace pn532 {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "pn532.mifare_classic";
 | 
			
		||||
 | 
			
		||||
nfc::NfcTag *PN532::read_mifare_classic_tag_(std::vector<uint8_t> &uid) {
 | 
			
		||||
  uint8_t current_block = 4;
 | 
			
		||||
  uint8_t message_start_index = 0;
 | 
			
		||||
  uint32_t message_length = 0;
 | 
			
		||||
 | 
			
		||||
  if (this->auth_mifare_classic_block_(uid, current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY)) {
 | 
			
		||||
    std::vector<uint8_t> data;
 | 
			
		||||
    if (this->read_mifare_classic_block_(current_block, data)) {
 | 
			
		||||
      if (!nfc::decode_mifare_classic_tlv(data, message_length, message_start_index)) {
 | 
			
		||||
        return new nfc::NfcTag(uid, nfc::ERROR);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      ESP_LOGE(TAG, "Failed to read block %d", current_block);
 | 
			
		||||
      return new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGV(TAG, "Tag is not NDEF formatted");
 | 
			
		||||
    return new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint32_t index = 0;
 | 
			
		||||
  uint32_t buffer_size = nfc::get_mifare_classic_buffer_size(message_length);
 | 
			
		||||
  std::vector<uint8_t> buffer;
 | 
			
		||||
 | 
			
		||||
  while (index < buffer_size) {
 | 
			
		||||
    if (nfc::mifare_classic_is_first_block(current_block)) {
 | 
			
		||||
      if (!this->auth_mifare_classic_block_(uid, current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY)) {
 | 
			
		||||
        ESP_LOGE(TAG, "Error, Block authentication failed for %d", current_block);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    std::vector<uint8_t> block_data;
 | 
			
		||||
    if (this->read_mifare_classic_block_(current_block, block_data)) {
 | 
			
		||||
      buffer.insert(buffer.end(), block_data.begin(), block_data.end());
 | 
			
		||||
    } else {
 | 
			
		||||
      ESP_LOGE(TAG, "Error reading block %d", current_block);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    index += nfc::MIFARE_CLASSIC_BLOCK_SIZE;
 | 
			
		||||
    current_block++;
 | 
			
		||||
 | 
			
		||||
    if (nfc::mifare_classic_is_trailer_block(current_block)) {
 | 
			
		||||
      current_block++;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  buffer.erase(buffer.begin(), buffer.begin() + message_start_index);
 | 
			
		||||
  return new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC, buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data) {
 | 
			
		||||
  if (!this->write_command_({
 | 
			
		||||
          PN532_COMMAND_INDATAEXCHANGE,
 | 
			
		||||
          0x01,  // One card
 | 
			
		||||
          nfc::MIFARE_CMD_READ,
 | 
			
		||||
          block_num,
 | 
			
		||||
      })) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, data) || data[0] != 0x00) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  data.erase(data.begin());
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, " Block %d: %s", block_num, nfc::format_bytes(data).c_str());
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::auth_mifare_classic_block_(std::vector<uint8_t> &uid, uint8_t block_num, uint8_t key_num,
 | 
			
		||||
                                       const uint8_t *key) {
 | 
			
		||||
  std::vector<uint8_t> data({
 | 
			
		||||
      PN532_COMMAND_INDATAEXCHANGE,
 | 
			
		||||
      0x01,       // One card
 | 
			
		||||
      key_num,    // Mifare Key slot
 | 
			
		||||
      block_num,  // Block number
 | 
			
		||||
  });
 | 
			
		||||
  data.insert(data.end(), key, key + 6);
 | 
			
		||||
  data.insert(data.end(), uid.begin(), uid.end());
 | 
			
		||||
  if (!this->write_command_(data)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Authentication failed - Block %d", block_num);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> response;
 | 
			
		||||
  if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, response) || response[0] != 0x00) {
 | 
			
		||||
    ESP_LOGE(TAG, "Authentication failed - Block 0x%02x", block_num);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::format_mifare_classic_mifare_(std::vector<uint8_t> &uid) {
 | 
			
		||||
  std::vector<uint8_t> blank_buffer(
 | 
			
		||||
      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
 | 
			
		||||
  std::vector<uint8_t> trailer_buffer(
 | 
			
		||||
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
 | 
			
		||||
 | 
			
		||||
  bool error = false;
 | 
			
		||||
 | 
			
		||||
  for (int block = 0; block < 64; block += 4) {
 | 
			
		||||
    if (!this->auth_mifare_classic_block_(uid, block + 3, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY)) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (block != 0) {
 | 
			
		||||
      if (!this->write_mifare_classic_block_(block, blank_buffer)) {
 | 
			
		||||
        ESP_LOGE(TAG, "Unable to write block %d", block);
 | 
			
		||||
        error = true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (!this->write_mifare_classic_block_(block + 1, blank_buffer)) {
 | 
			
		||||
      ESP_LOGE(TAG, "Unable to write block %d", block + 1);
 | 
			
		||||
      error = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (!this->write_mifare_classic_block_(block + 2, blank_buffer)) {
 | 
			
		||||
      ESP_LOGE(TAG, "Unable to write block %d", block + 2);
 | 
			
		||||
      error = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (!this->write_mifare_classic_block_(block + 3, trailer_buffer)) {
 | 
			
		||||
      ESP_LOGE(TAG, "Unable to write block %d", block + 3);
 | 
			
		||||
      error = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return !error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::format_mifare_classic_ndef_(std::vector<uint8_t> &uid) {
 | 
			
		||||
  std::vector<uint8_t> empty_ndef_message(
 | 
			
		||||
      {0x03, 0x03, 0xD0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
 | 
			
		||||
  std::vector<uint8_t> blank_block(
 | 
			
		||||
      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
 | 
			
		||||
  std::vector<uint8_t> block_1_data(
 | 
			
		||||
      {0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1});
 | 
			
		||||
  std::vector<uint8_t> block_2_data(
 | 
			
		||||
      {0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1});
 | 
			
		||||
  std::vector<uint8_t> block_3_trailer(
 | 
			
		||||
      {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
 | 
			
		||||
  std::vector<uint8_t> ndef_trailer(
 | 
			
		||||
      {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
 | 
			
		||||
 | 
			
		||||
  if (!this->auth_mifare_classic_block_(uid, 0, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Unable to authenticate block 0 for formatting!");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->write_mifare_classic_block_(1, block_1_data))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!this->write_mifare_classic_block_(2, block_2_data))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!this->write_mifare_classic_block_(3, block_3_trailer))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Sector 0 formatted to NDEF");
 | 
			
		||||
 | 
			
		||||
  for (int block = 4; block < 64; block += 4) {
 | 
			
		||||
    if (!this->auth_mifare_classic_block_(uid, block + 3, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY)) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    if (block == 4) {
 | 
			
		||||
      if (!this->write_mifare_classic_block_(block, empty_ndef_message))
 | 
			
		||||
        ESP_LOGE(TAG, "Unable to write block %d", block);
 | 
			
		||||
    } else {
 | 
			
		||||
      if (!this->write_mifare_classic_block_(block, blank_block))
 | 
			
		||||
        ESP_LOGE(TAG, "Unable to write block %d", block);
 | 
			
		||||
    }
 | 
			
		||||
    if (!this->write_mifare_classic_block_(block + 1, blank_block))
 | 
			
		||||
      ESP_LOGE(TAG, "Unable to write block %d", block + 1);
 | 
			
		||||
    if (!this->write_mifare_classic_block_(block + 2, blank_block))
 | 
			
		||||
      ESP_LOGE(TAG, "Unable to write block %d", block + 2);
 | 
			
		||||
    if (!this->write_mifare_classic_block_(block + 3, ndef_trailer))
 | 
			
		||||
      ESP_LOGE(TAG, "Unable to write trailer block %d", block + 3);
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::write_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &write_data) {
 | 
			
		||||
  std::vector<uint8_t> data({
 | 
			
		||||
      PN532_COMMAND_INDATAEXCHANGE,
 | 
			
		||||
      0x01,  // One card
 | 
			
		||||
      nfc::MIFARE_CMD_WRITE,
 | 
			
		||||
      block_num,
 | 
			
		||||
  });
 | 
			
		||||
  data.insert(data.end(), write_data.begin(), write_data.end());
 | 
			
		||||
  if (!this->write_command_(data)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Error writing block %d", block_num);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> response;
 | 
			
		||||
  if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, response)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Error writing block %d", block_num);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::write_mifare_classic_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
 | 
			
		||||
  auto encoded = message->encode();
 | 
			
		||||
 | 
			
		||||
  uint32_t message_length = encoded.size();
 | 
			
		||||
  uint32_t buffer_length = nfc::get_mifare_classic_buffer_size(message_length);
 | 
			
		||||
 | 
			
		||||
  encoded.insert(encoded.begin(), 0x03);
 | 
			
		||||
  if (message_length < 255) {
 | 
			
		||||
    encoded.insert(encoded.begin() + 1, message_length);
 | 
			
		||||
  } else {
 | 
			
		||||
    encoded.insert(encoded.begin() + 1, 0xFF);
 | 
			
		||||
    encoded.insert(encoded.begin() + 2, (message_length >> 8) & 0xFF);
 | 
			
		||||
    encoded.insert(encoded.begin() + 3, message_length & 0xFF);
 | 
			
		||||
  }
 | 
			
		||||
  encoded.push_back(0xFE);
 | 
			
		||||
 | 
			
		||||
  encoded.resize(buffer_length, 0);
 | 
			
		||||
 | 
			
		||||
  uint32_t index = 0;
 | 
			
		||||
  uint8_t current_block = 4;
 | 
			
		||||
 | 
			
		||||
  while (index < buffer_length) {
 | 
			
		||||
    if (nfc::mifare_classic_is_first_block(current_block)) {
 | 
			
		||||
      if (!this->auth_mifare_classic_block_(uid, current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY)) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> data(encoded.begin() + index, encoded.begin() + index + nfc::MIFARE_CLASSIC_BLOCK_SIZE);
 | 
			
		||||
    if (!this->write_mifare_classic_block_(current_block, data)) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    index += nfc::MIFARE_CLASSIC_BLOCK_SIZE;
 | 
			
		||||
    current_block++;
 | 
			
		||||
 | 
			
		||||
    if (nfc::mifare_classic_is_trailer_block(current_block)) {
 | 
			
		||||
      // Skipping as cannot write to trailer
 | 
			
		||||
      current_block++;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace pn532
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										180
									
								
								esphome/components/pn532/pn532_mifare_ultralight.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								esphome/components/pn532/pn532_mifare_ultralight.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
			
		||||
#include "pn532.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace pn532 {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "pn532.mifare_ultralight";
 | 
			
		||||
 | 
			
		||||
nfc::NfcTag *PN532::read_mifare_ultralight_tag_(std::vector<uint8_t> &uid) {
 | 
			
		||||
  if (!this->is_mifare_ultralight_formatted_()) {
 | 
			
		||||
    ESP_LOGD(TAG, "Not NDEF formatted");
 | 
			
		||||
    return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t message_length;
 | 
			
		||||
  uint8_t message_start_index;
 | 
			
		||||
  if (!this->find_mifare_ultralight_ndef_(message_length, message_start_index)) {
 | 
			
		||||
    return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (message_length == 0) {
 | 
			
		||||
    return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2);
 | 
			
		||||
  }
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
  uint8_t index = 0;
 | 
			
		||||
  for (uint8_t page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; page < nfc::MIFARE_ULTRALIGHT_MAX_PAGE; page++) {
 | 
			
		||||
    std::vector<uint8_t> page_data;
 | 
			
		||||
    if (!this->read_mifare_ultralight_page_(page, page_data)) {
 | 
			
		||||
      ESP_LOGE(TAG, "Error reading page %d", page);
 | 
			
		||||
      return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2);
 | 
			
		||||
    }
 | 
			
		||||
    data.insert(data.end(), page_data.begin(), page_data.end());
 | 
			
		||||
 | 
			
		||||
    if (index >= (message_length + message_start_index))
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    index += page_data.size();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  data.erase(data.begin(), data.begin() + message_start_index);
 | 
			
		||||
 | 
			
		||||
  return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::read_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &data) {
 | 
			
		||||
  if (!this->write_command_({
 | 
			
		||||
          PN532_COMMAND_INDATAEXCHANGE,
 | 
			
		||||
          0x01,  // One card
 | 
			
		||||
          nfc::MIFARE_CMD_READ,
 | 
			
		||||
          page_num,
 | 
			
		||||
      })) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, data) || data[0] != 0x00) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  data.erase(data.begin());
 | 
			
		||||
  // We only want 1 page of data but the PN532 returns 4 at once.
 | 
			
		||||
  data.erase(data.begin() + 4, data.end());
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, "Pages %d-%d: %s", page_num, page_num + 4, nfc::format_bytes(data).c_str());
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::is_mifare_ultralight_formatted_() {
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
  if (this->read_mifare_ultralight_page_(4, data)) {
 | 
			
		||||
    return !(data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF);
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t PN532::read_mifare_ultralight_capacity_() {
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
  if (this->read_mifare_ultralight_page_(3, data)) {
 | 
			
		||||
    return data[2] * 8U;
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::find_mifare_ultralight_ndef_(uint8_t &message_length, uint8_t &message_start_index) {
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
  for (int page = 4; page < 6; page++) {
 | 
			
		||||
    std::vector<uint8_t> page_data;
 | 
			
		||||
    if (!this->read_mifare_ultralight_page_(page, page_data)) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    data.insert(data.end(), page_data.begin(), page_data.end());
 | 
			
		||||
  }
 | 
			
		||||
  if (data[0] == 0x03) {
 | 
			
		||||
    message_length = data[1];
 | 
			
		||||
    message_start_index = 2;
 | 
			
		||||
    return true;
 | 
			
		||||
  } else if (data[5] == 0x03) {
 | 
			
		||||
    message_length = data[6];
 | 
			
		||||
    message_start_index = 7;
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
 | 
			
		||||
  uint32_t capacity = this->read_mifare_ultralight_capacity_();
 | 
			
		||||
 | 
			
		||||
  auto encoded = message->encode();
 | 
			
		||||
 | 
			
		||||
  uint32_t message_length = encoded.size();
 | 
			
		||||
  uint32_t buffer_length = nfc::get_mifare_ultralight_buffer_size(message_length);
 | 
			
		||||
 | 
			
		||||
  if (buffer_length > capacity) {
 | 
			
		||||
    ESP_LOGE(TAG, "Message length exceeds tag capacity %d > %d", buffer_length, capacity);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encoded.insert(encoded.begin(), 0x03);
 | 
			
		||||
  if (message_length < 255) {
 | 
			
		||||
    encoded.insert(encoded.begin() + 1, message_length);
 | 
			
		||||
  } else {
 | 
			
		||||
    encoded.insert(encoded.begin() + 1, 0xFF);
 | 
			
		||||
    encoded.insert(encoded.begin() + 2, (message_length >> 8) & 0xFF);
 | 
			
		||||
    encoded.insert(encoded.begin() + 2, message_length & 0xFF);
 | 
			
		||||
  }
 | 
			
		||||
  encoded.push_back(0xFE);
 | 
			
		||||
 | 
			
		||||
  encoded.resize(buffer_length, 0);
 | 
			
		||||
 | 
			
		||||
  uint32_t index = 0;
 | 
			
		||||
  uint8_t current_page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE;
 | 
			
		||||
 | 
			
		||||
  while (index < buffer_length) {
 | 
			
		||||
    std::vector<uint8_t> data(encoded.begin() + index, encoded.begin() + index + nfc::MIFARE_ULTRALIGHT_PAGE_SIZE);
 | 
			
		||||
    if (!this->write_mifare_ultralight_page_(current_page, data)) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    index += nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
 | 
			
		||||
    current_page++;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::clean_mifare_ultralight_() {
 | 
			
		||||
  uint32_t capacity = this->read_mifare_ultralight_capacity_();
 | 
			
		||||
  uint8_t pages = (capacity / nfc::MIFARE_ULTRALIGHT_PAGE_SIZE) + nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE;
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> blank_data = {0x00, 0x00, 0x00, 0x00};
 | 
			
		||||
 | 
			
		||||
  for (int i = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; i < pages; i++) {
 | 
			
		||||
    if (!this->write_mifare_ultralight_page_(i, blank_data)) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PN532::write_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &write_data) {
 | 
			
		||||
  std::vector<uint8_t> data({
 | 
			
		||||
      PN532_COMMAND_INDATAEXCHANGE,
 | 
			
		||||
      0x01,  // One card
 | 
			
		||||
      nfc::MIFARE_CMD_WRITE_ULTRALIGHT,
 | 
			
		||||
      page_num,
 | 
			
		||||
  });
 | 
			
		||||
  data.insert(data.end(), write_data.begin(), write_data.end());
 | 
			
		||||
  if (!this->write_command_(data)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Error writing page %d", page_num);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> response;
 | 
			
		||||
  if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, response)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Error writing page %d", page_num);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace pn532
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -14,7 +14,7 @@ static const char *TAG = "pn532_i2c";
 | 
			
		||||
bool PN532I2C::write_data(const std::vector<uint8_t> &data) { return this->write_bytes_raw(data.data(), data.size()); }
 | 
			
		||||
 | 
			
		||||
bool PN532I2C::read_data(std::vector<uint8_t> &data, uint8_t len) {
 | 
			
		||||
  delay(5);
 | 
			
		||||
  delay(1);
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> ready;
 | 
			
		||||
  ready.resize(1);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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.config_validation as cv
 | 
			
		||||
from esphome import automation, pins
 | 
			
		||||
from esphome.components import spi
 | 
			
		||||
from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID, CONF_RESET_PIN, CONF_CS_PIN
 | 
			
		||||
from esphome.components import spi, rc522
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ['@glmnet']
 | 
			
		||||
DEPENDENCIES = ['spi']
 | 
			
		||||
AUTO_LOAD = ['binary_sensor']
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ['rc522']
 | 
			
		||||
 | 
			
		||||
rc522_spi_ns = cg.esphome_ns.namespace('rc522_spi')
 | 
			
		||||
RC522 = rc522_spi_ns.class_('RC522', cg.PollingComponent, spi.SPIDevice)
 | 
			
		||||
RC522Trigger = rc522_spi_ns.class_('RC522Trigger', automation.Trigger.template(cg.std_string))
 | 
			
		||||
RC522Spi = rc522_spi_ns.class_('RC522Spi', rc522.RC522, spi.SPIDevice)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(RC522),
 | 
			
		||||
    cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Optional(CONF_ON_TAG): automation.validate_automation({
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RC522Trigger),
 | 
			
		||||
    }),
 | 
			
		||||
}).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema())
 | 
			
		||||
CONFIG_SCHEMA = cv.All(rc522.RC522_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(RC522Spi),
 | 
			
		||||
}).extend(spi.spi_device_schema(cs_pin_required=True)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield rc522.setup_rc522(var, config)
 | 
			
		||||
    yield spi.register_spi_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_RESET_PIN in config:
 | 
			
		||||
        reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN])
 | 
			
		||||
        cg.add(var.set_reset_pin(reset))
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_TAG, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
 | 
			
		||||
        cg.add(var.register_trigger(trigger))
 | 
			
		||||
        yield automation.build_automation(trigger, [(cg.std_string, 'x')], conf)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,44 +1,9 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import binary_sensor
 | 
			
		||||
from esphome.const import CONF_UID, CONF_ID
 | 
			
		||||
from esphome.core import HexInt
 | 
			
		||||
from . import rc522_spi_ns, RC522
 | 
			
		||||
import esphome.components.rc522.binary_sensor as rc522_binary_sensor
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['rc522_spi']
 | 
			
		||||
DEPENDENCIES = ['rc522']
 | 
			
		||||
 | 
			
		||||
CONF_RC522_ID = 'rc522_id'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_uid(value):
 | 
			
		||||
    value = cv.string_strict(value)
 | 
			
		||||
    for x in value.split('-'):
 | 
			
		||||
        if len(x) != 2:
 | 
			
		||||
            raise cv.Invalid("Each part (separated by '-') of the UID must be two characters "
 | 
			
		||||
                             "long.")
 | 
			
		||||
        try:
 | 
			
		||||
            x = int(x, 16)
 | 
			
		||||
        except ValueError as err:
 | 
			
		||||
            raise cv.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.") from err
 | 
			
		||||
        if x < 0 or x > 255:
 | 
			
		||||
            raise cv.Invalid("Valid values for UID parts (separated by '-') are 00 to FF")
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RC522BinarySensor = rc522_spi_ns.class_('RC522BinarySensor', binary_sensor.BinarySensor)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(RC522BinarySensor),
 | 
			
		||||
    cv.GenerateID(CONF_RC522_ID): cv.use_id(RC522),
 | 
			
		||||
    cv.Required(CONF_UID): validate_uid,
 | 
			
		||||
})
 | 
			
		||||
CONFIG_SCHEMA = rc522_binary_sensor.CONFIG_SCHEMA
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield binary_sensor.register_binary_sensor(var, config)
 | 
			
		||||
 | 
			
		||||
    hub = yield cg.get_variable(config[CONF_RC522_ID])
 | 
			
		||||
    cg.add(hub.register_tag(var))
 | 
			
		||||
    addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')]
 | 
			
		||||
    cg.add(var.set_uid(addr))
 | 
			
		||||
    yield rc522_binary_sensor.to_code(config)
 | 
			
		||||
 
 | 
			
		||||
@@ -9,218 +9,30 @@ namespace rc522_spi {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "rc522_spi";
 | 
			
		||||
 | 
			
		||||
static const uint8_t RESET_COUNT = 5;
 | 
			
		||||
void RC522Spi::setup() {
 | 
			
		||||
  ESP_LOGI(TAG, "SPI Setup");
 | 
			
		||||
  this->spi_setup();
 | 
			
		||||
 | 
			
		||||
void format_uid(char *buf, const uint8_t *uid, uint8_t uid_length) {
 | 
			
		||||
  int offset = 0;
 | 
			
		||||
  for (uint8_t i = 0; i < uid_length; i++) {
 | 
			
		||||
    const char *format = "%02X";
 | 
			
		||||
    if (i + 1 < uid_length)
 | 
			
		||||
      format = "%02X-";
 | 
			
		||||
    offset += sprintf(buf + offset, format, uid[i]);
 | 
			
		||||
  }
 | 
			
		||||
  RC522::setup();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RC522::setup() {
 | 
			
		||||
  spi_setup();
 | 
			
		||||
  initialize_pending_ = true;
 | 
			
		||||
  // Pull device out of power down / reset state.
 | 
			
		||||
 | 
			
		||||
  // First set the resetPowerDownPin as digital input, to check the MFRC522 power down mode.
 | 
			
		||||
  if (reset_pin_ != nullptr) {
 | 
			
		||||
    reset_pin_->pin_mode(INPUT);
 | 
			
		||||
 | 
			
		||||
    if (reset_pin_->digital_read() == LOW) {  // The MFRC522 chip is in power down mode.
 | 
			
		||||
      ESP_LOGV(TAG, "Power down mode detected. Hard resetting...");
 | 
			
		||||
      reset_pin_->pin_mode(OUTPUT);     // Now set the resetPowerDownPin as digital output.
 | 
			
		||||
      reset_pin_->digital_write(LOW);   // Make sure we have a clean LOW state.
 | 
			
		||||
      delayMicroseconds(2);             // 8.8.1 Reset timing requirements says about 100ns. Let us be generous: 2μsl
 | 
			
		||||
      reset_pin_->digital_write(HIGH);  // Exit power down mode. This triggers a hard reset.
 | 
			
		||||
      // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs.
 | 
			
		||||
      // Let us be generous: 50ms.
 | 
			
		||||
      reset_timeout_ = millis();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Setup a soft reset
 | 
			
		||||
  reset_count_ = RESET_COUNT;
 | 
			
		||||
  reset_timeout_ = millis();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RC522::initialize_() {
 | 
			
		||||
  // Per originall code, wait 50 ms
 | 
			
		||||
  if (millis() - reset_timeout_ < 50)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  // Reset baud rates
 | 
			
		||||
  ESP_LOGV(TAG, "Initialize");
 | 
			
		||||
 | 
			
		||||
  pcd_write_register_(TX_MODE_REG, 0x00);
 | 
			
		||||
  pcd_write_register_(RX_MODE_REG, 0x00);
 | 
			
		||||
  // Reset ModWidthReg
 | 
			
		||||
  pcd_write_register_(MOD_WIDTH_REG, 0x26);
 | 
			
		||||
 | 
			
		||||
  // When communicating with a PICC we need a timeout if something goes wrong.
 | 
			
		||||
  // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo].
 | 
			
		||||
  // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg.
 | 
			
		||||
  pcd_write_register_(T_MODE_REG, 0x80);  // TAuto=1; timer starts automatically at the end of the transmission in all
 | 
			
		||||
                                          // communication modes at all speeds
 | 
			
		||||
 | 
			
		||||
  // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25μs.
 | 
			
		||||
  pcd_write_register_(T_PRESCALER_REG, 0xA9);
 | 
			
		||||
  pcd_write_register_(T_RELOAD_REG_H, 0x03);  // Reload timer with 0x3E8 = 1000, ie 25ms before timeout.
 | 
			
		||||
  pcd_write_register_(T_RELOAD_REG_L, 0xE8);
 | 
			
		||||
 | 
			
		||||
  // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
 | 
			
		||||
  pcd_write_register_(TX_ASK_REG, 0x40);
 | 
			
		||||
  pcd_write_register_(MODE_REG, 0x3D);  // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC
 | 
			
		||||
                                        // command to 0x6363 (ISO 14443-3 part 6.2.4)
 | 
			
		||||
  pcd_antenna_on_();                    // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset)
 | 
			
		||||
 | 
			
		||||
  initialize_pending_ = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RC522::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "RC522:");
 | 
			
		||||
  switch (this->error_code_) {
 | 
			
		||||
    case NONE:
 | 
			
		||||
      break;
 | 
			
		||||
    case RESET_FAILED:
 | 
			
		||||
      ESP_LOGE(TAG, "Reset command failed!");
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
void RC522Spi::dump_config() {
 | 
			
		||||
  RC522::dump_config();
 | 
			
		||||
  LOG_PIN("  CS Pin: ", this->cs_);
 | 
			
		||||
  LOG_PIN("  RESET Pin: ", this->reset_pin_);
 | 
			
		||||
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
 | 
			
		||||
  for (auto *child : this->binary_sensors_) {
 | 
			
		||||
    LOG_BINARY_SENSOR("  ", "Tag", child);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RC522::loop() {
 | 
			
		||||
  // First check reset is needed
 | 
			
		||||
  if (reset_count_ > 0) {
 | 
			
		||||
    pcd_reset_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (initialize_pending_) {
 | 
			
		||||
    initialize_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (millis() - update_wait_ < this->update_interval_)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  auto status = picc_is_new_card_present_();
 | 
			
		||||
 | 
			
		||||
  if (status == STATUS_ERROR)  // No card
 | 
			
		||||
  {
 | 
			
		||||
    ESP_LOGE(TAG, "Error");
 | 
			
		||||
    // mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status != STATUS_OK)  // We can receive STATUS_TIMEOUT when no card, or unexpected status.
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  // Try process card
 | 
			
		||||
  if (!picc_read_card_serial_()) {
 | 
			
		||||
    ESP_LOGW(TAG, "Requesting tag read failed!");
 | 
			
		||||
    return;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (uid_.size < 4) {
 | 
			
		||||
    return;
 | 
			
		||||
    ESP_LOGW(TAG, "Read serial size: %d", uid_.size);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  update_wait_ = millis();
 | 
			
		||||
 | 
			
		||||
  bool report = true;
 | 
			
		||||
  // 1. Go through all triggers
 | 
			
		||||
  for (auto *trigger : this->triggers_)
 | 
			
		||||
    trigger->process(uid_.uiduint8_t, uid_.size);
 | 
			
		||||
 | 
			
		||||
  // 2. Find a binary sensor
 | 
			
		||||
  for (auto *tag : this->binary_sensors_) {
 | 
			
		||||
    if (tag->process(uid_.uiduint8_t, uid_.size)) {
 | 
			
		||||
      // 2.1 if found, do not dump
 | 
			
		||||
      report = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (report) {
 | 
			
		||||
    char buf[32];
 | 
			
		||||
    format_uid(buf, uid_.uiduint8_t, uid_.size);
 | 
			
		||||
    ESP_LOGD(TAG, "Found new tag '%s'", buf);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RC522::update() {
 | 
			
		||||
  for (auto *obj : this->binary_sensors_)
 | 
			
		||||
    obj->on_scan_end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Performs a soft reset on the MFRC522 chip and waits for it to be ready again.
 | 
			
		||||
 */
 | 
			
		||||
void RC522::pcd_reset_() {
 | 
			
		||||
  // The datasheet does not mention how long the SoftRest command takes to complete.
 | 
			
		||||
  // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg)
 | 
			
		||||
  // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. Let
 | 
			
		||||
  // us be generous: 50ms.
 | 
			
		||||
 | 
			
		||||
  if (millis() - reset_timeout_ < 50)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (reset_count_ == RESET_COUNT) {
 | 
			
		||||
    ESP_LOGV(TAG, "Soft reset...");
 | 
			
		||||
    // Issue the SoftReset command.
 | 
			
		||||
    pcd_write_register_(COMMAND_REG, PCD_SOFT_RESET);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Expect the PowerDown bit in CommandReg to be cleared (max 3x50ms)
 | 
			
		||||
  if ((pcd_read_register_(COMMAND_REG) & (1 << 4)) == 0) {
 | 
			
		||||
    reset_count_ = 0;
 | 
			
		||||
    ESP_LOGI(TAG, "Device online.");
 | 
			
		||||
    // Wait for initialize
 | 
			
		||||
    reset_timeout_ = millis();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (--reset_count_ == 0) {
 | 
			
		||||
    ESP_LOGE(TAG, "Unable to reset RC522.");
 | 
			
		||||
    mark_failed();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Turns the antenna on by enabling pins TX1 and TX2.
 | 
			
		||||
 * After a reset these pins are disabled.
 | 
			
		||||
 */
 | 
			
		||||
void RC522::pcd_antenna_on_() {
 | 
			
		||||
  uint8_t value = pcd_read_register_(TX_CONTROL_REG);
 | 
			
		||||
  if ((value & 0x03) != 0x03) {
 | 
			
		||||
    pcd_write_register_(TX_CONTROL_REG, value | 0x03);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Reads a uint8_t from the specified register in the MFRC522 chip.
 | 
			
		||||
 * The interface is described in the datasheet section 8.1.2.
 | 
			
		||||
 */
 | 
			
		||||
uint8_t RC522::pcd_read_register_(PcdRegister reg  ///< The register to read from. One of the PCD_Register enums.
 | 
			
		||||
uint8_t RC522Spi::pcd_read_register(PcdRegister reg  ///< The register to read from. One of the PCD_Register enums.
 | 
			
		||||
) {
 | 
			
		||||
  uint8_t value;
 | 
			
		||||
  enable();
 | 
			
		||||
  transfer_byte(0x80 | reg);
 | 
			
		||||
  value = read_byte();
 | 
			
		||||
  disable();
 | 
			
		||||
  ESP_LOGVV(TAG, "read_register_(%x) -> %x", reg, value);
 | 
			
		||||
  ESP_LOGV(TAG, "read_register_(%x) -> %x", reg, value);
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -228,10 +40,10 @@ uint8_t RC522::pcd_read_register_(PcdRegister reg  ///< The register to read fro
 | 
			
		||||
 * Reads a number of uint8_ts from the specified register in the MFRC522 chip.
 | 
			
		||||
 * The interface is described in the datasheet section 8.1.2.
 | 
			
		||||
 */
 | 
			
		||||
void RC522::pcd_read_register_(PcdRegister reg,  ///< The register to read from. One of the PCD_Register enums.
 | 
			
		||||
                               uint8_t count,    ///< The number of uint8_ts to read
 | 
			
		||||
                               uint8_t *values,  ///< uint8_t array to store the values in.
 | 
			
		||||
                               uint8_t rx_align  ///< Only bit positions rxAlign..7 in values[0] are updated.
 | 
			
		||||
void RC522Spi::pcd_read_register(PcdRegister reg,  ///< The register to read from. One of the PCD_Register enums.
 | 
			
		||||
                                 uint8_t count,    ///< The number of uint8_ts to read
 | 
			
		||||
                                 uint8_t *values,  ///< uint8_t array to store the values in.
 | 
			
		||||
                                 uint8_t rx_align  ///< Only bit positions rxAlign..7 in values[0] are updated.
 | 
			
		||||
) {
 | 
			
		||||
  std::string buf;
 | 
			
		||||
  buf = "Rx";
 | 
			
		||||
@@ -278,8 +90,8 @@ void RC522::pcd_read_register_(PcdRegister reg,  ///< The register to read from.
 | 
			
		||||
  disable();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RC522::pcd_write_register_(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums.
 | 
			
		||||
                                uint8_t value     ///< The value to write.
 | 
			
		||||
void RC522Spi::pcd_write_register(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums.
 | 
			
		||||
                                  uint8_t value     ///< The value to write.
 | 
			
		||||
) {
 | 
			
		||||
  enable();
 | 
			
		||||
  // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3.
 | 
			
		||||
@@ -292,9 +104,9 @@ void RC522::pcd_write_register_(PcdRegister reg,  ///< The register to write to.
 | 
			
		||||
 * Writes a number of uint8_ts to the specified register in the MFRC522 chip.
 | 
			
		||||
 * The interface is described in the datasheet section 8.1.2.
 | 
			
		||||
 */
 | 
			
		||||
void RC522::pcd_write_register_(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums.
 | 
			
		||||
                                uint8_t count,    ///< The number of uint8_ts to write to the register
 | 
			
		||||
                                uint8_t *values   ///< The values to write. uint8_t array.
 | 
			
		||||
void RC522Spi::pcd_write_register(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums.
 | 
			
		||||
                                  uint8_t count,    ///< The number of uint8_ts to write to the register
 | 
			
		||||
                                  uint8_t *values   ///< The values to write. uint8_t array.
 | 
			
		||||
) {
 | 
			
		||||
  std::string buf;
 | 
			
		||||
  buf = "Tx";
 | 
			
		||||
@@ -313,545 +125,5 @@ void RC522::pcd_write_register_(PcdRegister reg,  ///< The register to write to.
 | 
			
		||||
  ESP_LOGVV(TAG, "write_register_(%x, %d) -> %s", reg, count, buf.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or
 | 
			
		||||
 * selection. 7 bit frame. Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT -
 | 
			
		||||
 * probably due do bad antenna design.
 | 
			
		||||
 *
 | 
			
		||||
 * @return STATUS_OK on success, STATUS_??? otherwise.
 | 
			
		||||
 */
 | 
			
		||||
RC522::StatusCode RC522::picc_request_a_(
 | 
			
		||||
    uint8_t *buffer_atqa,  ///< The buffer to store the ATQA (Answer to request) in
 | 
			
		||||
    uint8_t *buffer_size   ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK.
 | 
			
		||||
) {
 | 
			
		||||
  return picc_reqa_or_wupa_(PICC_CMD_REQA, buffer_atqa, buffer_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Transmits REQA or WUPA commands.
 | 
			
		||||
 * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna
 | 
			
		||||
 * design.
 | 
			
		||||
 *
 | 
			
		||||
 * @return STATUS_OK on success, STATUS_??? otherwise.
 | 
			
		||||
 */
 | 
			
		||||
RC522::StatusCode RC522::picc_reqa_or_wupa_(
 | 
			
		||||
    uint8_t command,       ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA
 | 
			
		||||
    uint8_t *buffer_atqa,  ///< The buffer to store the ATQA (Answer to request) in
 | 
			
		||||
    uint8_t *buffer_size   ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK.
 | 
			
		||||
) {
 | 
			
		||||
  uint8_t valid_bits;
 | 
			
		||||
  RC522::StatusCode status;
 | 
			
		||||
 | 
			
		||||
  if (buffer_atqa == nullptr || *buffer_size < 2) {  // The ATQA response is 2 uint8_ts long.
 | 
			
		||||
    return STATUS_NO_ROOM;
 | 
			
		||||
  }
 | 
			
		||||
  pcd_clear_register_bit_mask_(COLL_REG, 0x80);  // ValuesAfterColl=1 => Bits received after collision are cleared.
 | 
			
		||||
  valid_bits = 7;  // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only)
 | 
			
		||||
                   // uint8_t. TxLastBits = BitFramingReg[2..0]
 | 
			
		||||
  status = pcd_transceive_data_(&command, 1, buffer_atqa, buffer_size, &valid_bits);
 | 
			
		||||
  if (status != STATUS_OK)
 | 
			
		||||
    return status;
 | 
			
		||||
  if (*buffer_size != 2 || valid_bits != 0) {  // ATQA must be exactly 16 bits.
 | 
			
		||||
    ESP_LOGVV(TAG, "picc_reqa_or_wupa_() -> STATUS_ERROR");
 | 
			
		||||
    return STATUS_ERROR;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return STATUS_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Sets the bits given in mask in register reg.
 | 
			
		||||
 */
 | 
			
		||||
void RC522::pcd_set_register_bit_mask_(PcdRegister reg,  ///< The register to update. One of the PCD_Register enums.
 | 
			
		||||
                                       uint8_t mask      ///< The bits to set.
 | 
			
		||||
) {
 | 
			
		||||
  uint8_t tmp = pcd_read_register_(reg);
 | 
			
		||||
  pcd_write_register_(reg, tmp | mask);  // set bit mask
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Clears the bits given in mask from register reg.
 | 
			
		||||
 */
 | 
			
		||||
void RC522::pcd_clear_register_bit_mask_(PcdRegister reg,  ///< The register to update. One of the PCD_Register enums.
 | 
			
		||||
                                         uint8_t mask      ///< The bits to clear.
 | 
			
		||||
) {
 | 
			
		||||
  uint8_t tmp = pcd_read_register_(reg);
 | 
			
		||||
  pcd_write_register_(reg, tmp & (~mask));  // clear bit mask
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Executes the Transceive command.
 | 
			
		||||
 * CRC validation can only be done if backData and backLen are specified.
 | 
			
		||||
 *
 | 
			
		||||
 * @return STATUS_OK on success, STATUS_??? otherwise.
 | 
			
		||||
 */
 | 
			
		||||
RC522::StatusCode RC522::pcd_transceive_data_(
 | 
			
		||||
    uint8_t *send_data,  ///< Pointer to the data to transfer to the FIFO.
 | 
			
		||||
    uint8_t send_len,    ///< Number of uint8_ts to transfer to the FIFO.
 | 
			
		||||
    uint8_t *back_data,  ///< nullptr or pointer to buffer if data should be read back after executing the command.
 | 
			
		||||
    uint8_t *back_len,   ///< In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned.
 | 
			
		||||
    uint8_t
 | 
			
		||||
        *valid_bits,   ///< In/Out: The number of valid bits in the last uint8_t. 0 for 8 valid bits. Default nullptr.
 | 
			
		||||
    uint8_t rx_align,  ///< In: Defines the bit position in backData[0] for the first bit received. Default 0.
 | 
			
		||||
    bool check_crc     ///< In: True => The last two uint8_ts of the response is assumed to be a CRC_A that must be
 | 
			
		||||
                       ///< validated.
 | 
			
		||||
) {
 | 
			
		||||
  uint8_t wait_i_rq = 0x30;  // RxIRq and IdleIRq
 | 
			
		||||
  auto ret = pcd_communicate_with_picc_(PCD_TRANSCEIVE, wait_i_rq, send_data, send_len, back_data, back_len, valid_bits,
 | 
			
		||||
                                        rx_align, check_crc);
 | 
			
		||||
 | 
			
		||||
  if (ret == STATUS_OK && *back_len == 5)
 | 
			
		||||
    ESP_LOGVV(TAG, "pcd_transceive_data_(..., %d,  ) -> %d [%x, %x, %x, %x, %x]", send_len, ret, back_data[0],
 | 
			
		||||
              back_data[1], back_data[2], back_data[3], back_data[4]);
 | 
			
		||||
  else
 | 
			
		||||
    ESP_LOGVV(TAG, "pcd_transceive_data_(..., %d, ... ) -> %d", send_len, ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO.
 | 
			
		||||
 * CRC validation can only be done if backData and backLen are specified.
 | 
			
		||||
 *
 | 
			
		||||
 * @return STATUS_OK on success, STATUS_??? otherwise.
 | 
			
		||||
 */
 | 
			
		||||
RC522::StatusCode RC522::pcd_communicate_with_picc_(
 | 
			
		||||
    uint8_t command,      ///< The command to execute. One of the PCD_Command enums.
 | 
			
		||||
    uint8_t wait_i_rq,    ///< The bits in the ComIrqReg register that signals successful completion of the command.
 | 
			
		||||
    uint8_t *send_data,   ///< Pointer to the data to transfer to the FIFO.
 | 
			
		||||
    uint8_t send_len,     ///< Number of uint8_ts to transfer to the FIFO.
 | 
			
		||||
    uint8_t *back_data,   ///< nullptr or pointer to buffer if data should be read back after executing the command.
 | 
			
		||||
    uint8_t *back_len,    ///< In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned.
 | 
			
		||||
    uint8_t *valid_bits,  ///< In/Out: The number of valid bits in the last uint8_t. 0 for 8 valid bits.
 | 
			
		||||
    uint8_t rx_align,     ///< In: Defines the bit position in backData[0] for the first bit received. Default 0.
 | 
			
		||||
    bool check_crc        ///< In: True => The last two uint8_ts of the response is assumed to be a CRC_A that must be
 | 
			
		||||
                          ///< validated.
 | 
			
		||||
) {
 | 
			
		||||
  ESP_LOGVV(TAG, "pcd_communicate_with_picc_(%d, %d,... %d)", command, wait_i_rq, check_crc);
 | 
			
		||||
 | 
			
		||||
  // Prepare values for BitFramingReg
 | 
			
		||||
  uint8_t tx_last_bits = valid_bits ? *valid_bits : 0;
 | 
			
		||||
  uint8_t bit_framing =
 | 
			
		||||
      (rx_align << 4) + tx_last_bits;  // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0]
 | 
			
		||||
 | 
			
		||||
  pcd_write_register_(COMMAND_REG, PCD_IDLE);               // Stop any active command.
 | 
			
		||||
  pcd_write_register_(COM_IRQ_REG, 0x7F);                   // Clear all seven interrupt request bits
 | 
			
		||||
  pcd_write_register_(FIFO_LEVEL_REG, 0x80);                // FlushBuffer = 1, FIFO initialization
 | 
			
		||||
  pcd_write_register_(FIFO_DATA_REG, send_len, send_data);  // Write sendData to the FIFO
 | 
			
		||||
  pcd_write_register_(BIT_FRAMING_REG, bit_framing);        // Bit adjustments
 | 
			
		||||
  pcd_write_register_(COMMAND_REG, command);                // Execute the command
 | 
			
		||||
  if (command == PCD_TRANSCEIVE) {
 | 
			
		||||
    pcd_set_register_bit_mask_(BIT_FRAMING_REG, 0x80);  // StartSend=1, transmission of data starts
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Wait for the command to complete.
 | 
			
		||||
  // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops
 | 
			
		||||
  // transmitting. Each iteration of the do-while-loop takes 17.86μs.
 | 
			
		||||
  // TODO check/modify for other architectures than Arduino Uno 16bit
 | 
			
		||||
  uint16_t i;
 | 
			
		||||
  for (i = 2000; i > 0; i--) {
 | 
			
		||||
    uint8_t n = pcd_read_register_(
 | 
			
		||||
        COM_IRQ_REG);     // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq
 | 
			
		||||
    if (n & wait_i_rq) {  // One of the interrupts that signal success has been set.
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    if (n & 0x01) {  // Timer interrupt - nothing received in 25ms
 | 
			
		||||
      return STATUS_TIMEOUT;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // 35.7ms and nothing happend. Communication with the MFRC522 might be down.
 | 
			
		||||
  if (i == 0) {
 | 
			
		||||
    return STATUS_TIMEOUT;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Stop now if any errors except collisions were detected.
 | 
			
		||||
  uint8_t error_reg_value = pcd_read_register_(
 | 
			
		||||
      ERROR_REG);  // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr
 | 
			
		||||
  if (error_reg_value & 0x13) {  // BufferOvfl ParityErr ProtocolErr
 | 
			
		||||
    return STATUS_ERROR;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t valid_bits_local = 0;
 | 
			
		||||
 | 
			
		||||
  // If the caller wants data back, get it from the MFRC522.
 | 
			
		||||
  if (back_data && back_len) {
 | 
			
		||||
    uint8_t n = pcd_read_register_(FIFO_LEVEL_REG);  // Number of uint8_ts in the FIFO
 | 
			
		||||
    if (n > *back_len) {
 | 
			
		||||
      return STATUS_NO_ROOM;
 | 
			
		||||
    }
 | 
			
		||||
    *back_len = n;                                              // Number of uint8_ts returned
 | 
			
		||||
    pcd_read_register_(FIFO_DATA_REG, n, back_data, rx_align);  // Get received data from FIFO
 | 
			
		||||
    valid_bits_local =
 | 
			
		||||
        pcd_read_register_(CONTROL_REG) & 0x07;  // RxLastBits[2:0] indicates the number of valid bits in the last
 | 
			
		||||
                                                 // received uint8_t. If this value is 000b, the whole uint8_t is valid.
 | 
			
		||||
    if (valid_bits) {
 | 
			
		||||
      *valid_bits = valid_bits_local;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Tell about collisions
 | 
			
		||||
  if (error_reg_value & 0x08) {  // CollErr
 | 
			
		||||
    return STATUS_COLLISION;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Perform CRC_A validation if requested.
 | 
			
		||||
  if (back_data && back_len && check_crc) {
 | 
			
		||||
    // In this case a MIFARE Classic NAK is not OK.
 | 
			
		||||
    if (*back_len == 1 && valid_bits_local == 4) {
 | 
			
		||||
      return STATUS_MIFARE_NACK;
 | 
			
		||||
    }
 | 
			
		||||
    // We need at least the CRC_A value and all 8 bits of the last uint8_t must be received.
 | 
			
		||||
    if (*back_len < 2 || valid_bits_local != 0) {
 | 
			
		||||
      return STATUS_CRC_WRONG;
 | 
			
		||||
    }
 | 
			
		||||
    // Verify CRC_A - do our own calculation and store the control in controlBuffer.
 | 
			
		||||
    uint8_t control_buffer[2];
 | 
			
		||||
    RC522::StatusCode status = pcd_calculate_crc_(&back_data[0], *back_len - 2, &control_buffer[0]);
 | 
			
		||||
    if (status != STATUS_OK) {
 | 
			
		||||
      return status;
 | 
			
		||||
    }
 | 
			
		||||
    if ((back_data[*back_len - 2] != control_buffer[0]) || (back_data[*back_len - 1] != control_buffer[1])) {
 | 
			
		||||
      return STATUS_CRC_WRONG;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return STATUS_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A.
 | 
			
		||||
 *
 | 
			
		||||
 * @return STATUS_OK on success, STATUS_??? otherwise.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
RC522::StatusCode RC522::pcd_calculate_crc_(
 | 
			
		||||
    uint8_t *data,   ///< In: Pointer to the data to transfer to the FIFO for CRC calculation.
 | 
			
		||||
    uint8_t length,  ///< In: The number of uint8_ts to transfer.
 | 
			
		||||
    uint8_t *result  ///< Out: Pointer to result buffer. Result is written to result[0..1], low uint8_t first.
 | 
			
		||||
) {
 | 
			
		||||
  ESP_LOGVV(TAG, "pcd_calculate_crc_(..., %d, ...)", length);
 | 
			
		||||
  pcd_write_register_(COMMAND_REG, PCD_IDLE);        // Stop any active command.
 | 
			
		||||
  pcd_write_register_(DIV_IRQ_REG, 0x04);            // Clear the CRCIRq interrupt request bit
 | 
			
		||||
  pcd_write_register_(FIFO_LEVEL_REG, 0x80);         // FlushBuffer = 1, FIFO initialization
 | 
			
		||||
  pcd_write_register_(FIFO_DATA_REG, length, data);  // Write data to the FIFO
 | 
			
		||||
  pcd_write_register_(COMMAND_REG, PCD_CALC_CRC);    // Start the calculation
 | 
			
		||||
 | 
			
		||||
  // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73μs.
 | 
			
		||||
  // TODO check/modify for other architectures than Arduino Uno 16bit
 | 
			
		||||
 | 
			
		||||
  // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73us.
 | 
			
		||||
  for (uint16_t i = 5000; i > 0; i--) {
 | 
			
		||||
    // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved
 | 
			
		||||
    uint8_t n = pcd_read_register_(DIV_IRQ_REG);
 | 
			
		||||
    if (n & 0x04) {                                // CRCIRq bit set - calculation done
 | 
			
		||||
      pcd_write_register_(COMMAND_REG, PCD_IDLE);  // Stop calculating CRC for new content in the FIFO.
 | 
			
		||||
      // Transfer the result from the registers to the result buffer
 | 
			
		||||
      result[0] = pcd_read_register_(CRC_RESULT_REG_L);
 | 
			
		||||
      result[1] = pcd_read_register_(CRC_RESULT_REG_H);
 | 
			
		||||
 | 
			
		||||
      ESP_LOGVV(TAG, "pcd_calculate_crc_() STATUS_OK");
 | 
			
		||||
      return STATUS_OK;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGVV(TAG, "pcd_calculate_crc_() TIMEOUT");
 | 
			
		||||
  // 89ms passed and nothing happend. Communication with the MFRC522 might be down.
 | 
			
		||||
  return STATUS_TIMEOUT;
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * Returns STATUS_OK if a PICC responds to PICC_CMD_REQA.
 | 
			
		||||
 * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored.
 | 
			
		||||
 *
 | 
			
		||||
 * @return  STATUS_OK on success, STATUS_??? otherwise.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
RC522::StatusCode RC522::picc_is_new_card_present_() {
 | 
			
		||||
  uint8_t buffer_atqa[2];
 | 
			
		||||
  uint8_t buffer_size = sizeof(buffer_atqa);
 | 
			
		||||
 | 
			
		||||
  // Reset baud rates
 | 
			
		||||
  pcd_write_register_(TX_MODE_REG, 0x00);
 | 
			
		||||
  pcd_write_register_(RX_MODE_REG, 0x00);
 | 
			
		||||
  // Reset ModWidthReg
 | 
			
		||||
  pcd_write_register_(MOD_WIDTH_REG, 0x26);
 | 
			
		||||
 | 
			
		||||
  auto result = picc_request_a_(buffer_atqa, &buffer_size);
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "picc_is_new_card_present_() -> %d", result);
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Simple wrapper around PICC_Select.
 | 
			
		||||
 * Returns true if a UID could be read.
 | 
			
		||||
 * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first.
 | 
			
		||||
 * The read UID is available in the class variable uid.
 | 
			
		||||
 *
 | 
			
		||||
 * @return bool
 | 
			
		||||
 */
 | 
			
		||||
bool RC522::picc_read_card_serial_() {
 | 
			
		||||
  RC522::StatusCode result = picc_select_(&this->uid_);
 | 
			
		||||
  ESP_LOGVV(TAG, "picc_select_(...) -> %d", result);
 | 
			
		||||
  return (result == STATUS_OK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Transmits SELECT/ANTICOLLISION commands to select a single PICC.
 | 
			
		||||
 * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or
 | 
			
		||||
 * PICC_WakeupA(). On success:
 | 
			
		||||
 *     - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the
 | 
			
		||||
 * ISO/IEC 14443-3 draft.)
 | 
			
		||||
 *     - The UID size and value of the chosen PICC is returned in *uid along with the SAK.
 | 
			
		||||
 *
 | 
			
		||||
 * A PICC UID consists of 4, 7 or 10 uint8_ts.
 | 
			
		||||
 * Only 4 uint8_ts can be specified in a SELECT command, so for the longer UIDs two or three iterations are used:
 | 
			
		||||
 *     UID size  Number of UID uint8_ts    Cascade levels    Example of PICC
 | 
			
		||||
 *     ========  ===================    ==============    ===============
 | 
			
		||||
 *     single         4            1        MIFARE Classic
 | 
			
		||||
 *     double         7            2        MIFARE Ultralight
 | 
			
		||||
 *     triple        10            3        Not currently in use?
 | 
			
		||||
 *
 | 
			
		||||
 * @return STATUS_OK on success, STATUS_??? otherwise.
 | 
			
		||||
 */
 | 
			
		||||
RC522::StatusCode RC522::picc_select_(
 | 
			
		||||
    Uid *uid,           ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID.
 | 
			
		||||
    uint8_t valid_bits  ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply
 | 
			
		||||
                        ///< uid->size.
 | 
			
		||||
) {
 | 
			
		||||
  bool uid_complete;
 | 
			
		||||
  bool select_done;
 | 
			
		||||
  bool use_cascade_tag;
 | 
			
		||||
  uint8_t cascade_level = 1;
 | 
			
		||||
  RC522::StatusCode result;
 | 
			
		||||
  uint8_t count;
 | 
			
		||||
  uint8_t check_bit;
 | 
			
		||||
  uint8_t index;
 | 
			
		||||
  uint8_t uid_index;                // The first index in uid->uiduint8_t[] that is used in the current Cascade Level.
 | 
			
		||||
  int8_t current_level_known_bits;  // The number of known UID bits in the current Cascade Level.
 | 
			
		||||
  uint8_t buffer[9];    // The SELECT/ANTICOLLISION commands uses a 7 uint8_t standard frame + 2 uint8_ts CRC_A
 | 
			
		||||
  uint8_t buffer_used;  // The number of uint8_ts used in the buffer, ie the number of uint8_ts to transfer to the FIFO.
 | 
			
		||||
  uint8_t rx_align;     // Used in BitFramingReg. Defines the bit position for the first bit received.
 | 
			
		||||
  uint8_t tx_last_bits;  // Used in BitFramingReg. The number of valid bits in the last transmitted uint8_t.
 | 
			
		||||
  uint8_t *response_buffer;
 | 
			
		||||
  uint8_t response_length;
 | 
			
		||||
 | 
			
		||||
  // Description of buffer structure:
 | 
			
		||||
  //    uint8_t 0: SEL         Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
 | 
			
		||||
  //    uint8_t 1: NVB          Number of Valid Bits (in complete command, not just the UID): High nibble: complete
 | 
			
		||||
  // uint8_ts,
 | 
			
		||||
  // Low nibble: Extra bits.     uint8_t 2: UID-data or CT    See explanation below. CT means Cascade Tag.     uint8_t
 | 
			
		||||
  // 3: UID-data uint8_t 4: UID-data     uint8_t 5: UID-data     uint8_t 6: BCC          Block Check Character - XOR of
 | 
			
		||||
  // uint8_ts 2-5 uint8_t 7: CRC_A uint8_t 8: CRC_A The BCC and CRC_A are only transmitted if we know all the UID bits
 | 
			
		||||
  // of the current Cascade Level.
 | 
			
		||||
  //
 | 
			
		||||
  // Description of uint8_ts 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels)
 | 
			
		||||
  //    UID size  Cascade level  uint8_t2  uint8_t3  uint8_t4  uint8_t5
 | 
			
		||||
  //    ========  =============  =====  =====  =====  =====
 | 
			
		||||
  //     4 uint8_ts    1      uid0  uid1  uid2  uid3
 | 
			
		||||
  //     7 uint8_ts    1      CT    uid0  uid1  uid2
 | 
			
		||||
  //            2      uid3  uid4  uid5  uid6
 | 
			
		||||
  //    10 uint8_ts    1      CT    uid0  uid1  uid2
 | 
			
		||||
  //            2      CT    uid3  uid4  uid5
 | 
			
		||||
  //            3      uid6  uid7  uid8  uid9
 | 
			
		||||
 | 
			
		||||
  // Sanity checks
 | 
			
		||||
  if (valid_bits > 80) {
 | 
			
		||||
    return STATUS_INVALID;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, "picc_select_(&, %d)", valid_bits);
 | 
			
		||||
 | 
			
		||||
  // Prepare MFRC522
 | 
			
		||||
  pcd_clear_register_bit_mask_(COLL_REG, 0x80);  // ValuesAfterColl=1 => Bits received after collision are cleared.
 | 
			
		||||
 | 
			
		||||
  // Repeat Cascade Level loop until we have a complete UID.
 | 
			
		||||
  uid_complete = false;
 | 
			
		||||
  while (!uid_complete) {
 | 
			
		||||
    // Set the Cascade Level in the SEL uint8_t, find out if we need to use the Cascade Tag in uint8_t 2.
 | 
			
		||||
    switch (cascade_level) {
 | 
			
		||||
      case 1:
 | 
			
		||||
        buffer[0] = PICC_CMD_SEL_CL1;
 | 
			
		||||
        uid_index = 0;
 | 
			
		||||
        use_cascade_tag = valid_bits && uid->size > 4;  // When we know that the UID has more than 4 uint8_ts
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case 2:
 | 
			
		||||
        buffer[0] = PICC_CMD_SEL_CL2;
 | 
			
		||||
        uid_index = 3;
 | 
			
		||||
        use_cascade_tag = valid_bits && uid->size > 7;  // When we know that the UID has more than 7 uint8_ts
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case 3:
 | 
			
		||||
        buffer[0] = PICC_CMD_SEL_CL3;
 | 
			
		||||
        uid_index = 6;
 | 
			
		||||
        use_cascade_tag = false;  // Never used in CL3.
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      default:
 | 
			
		||||
        return STATUS_INTERNAL_ERROR;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // How many UID bits are known in this Cascade Level?
 | 
			
		||||
    current_level_known_bits = valid_bits - (8 * uid_index);
 | 
			
		||||
    if (current_level_known_bits < 0) {
 | 
			
		||||
      current_level_known_bits = 0;
 | 
			
		||||
    }
 | 
			
		||||
    // Copy the known bits from uid->uiduint8_t[] to buffer[]
 | 
			
		||||
    index = 2;  // destination index in buffer[]
 | 
			
		||||
    if (use_cascade_tag) {
 | 
			
		||||
      buffer[index++] = PICC_CMD_CT;
 | 
			
		||||
    }
 | 
			
		||||
    uint8_t uint8_ts_to_copy = current_level_known_bits / 8 +
 | 
			
		||||
                               (current_level_known_bits % 8
 | 
			
		||||
                                    ? 1
 | 
			
		||||
                                    : 0);  // The number of uint8_ts needed to represent the known bits for this level.
 | 
			
		||||
    if (uint8_ts_to_copy) {
 | 
			
		||||
      uint8_t maxuint8_ts =
 | 
			
		||||
          use_cascade_tag ? 3 : 4;  // Max 4 uint8_ts in each Cascade Level. Only 3 left if we use the Cascade Tag
 | 
			
		||||
      if (uint8_ts_to_copy > maxuint8_ts) {
 | 
			
		||||
        uint8_ts_to_copy = maxuint8_ts;
 | 
			
		||||
      }
 | 
			
		||||
      for (count = 0; count < uint8_ts_to_copy; count++) {
 | 
			
		||||
        buffer[index++] = uid->uiduint8_t[uid_index + count];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits
 | 
			
		||||
    if (use_cascade_tag) {
 | 
			
		||||
      current_level_known_bits += 8;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations.
 | 
			
		||||
    select_done = false;
 | 
			
		||||
    while (!select_done) {
 | 
			
		||||
      // Find out how many bits and uint8_ts to send and receive.
 | 
			
		||||
      if (current_level_known_bits >= 32) {  // All UID bits in this Cascade Level are known. This is a SELECT.
 | 
			
		||||
 | 
			
		||||
        if (response_length < 4) {
 | 
			
		||||
          ESP_LOGW(TAG, "Not enough data received.");
 | 
			
		||||
          return STATUS_INVALID;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC);
 | 
			
		||||
        buffer[1] = 0x70;  // NVB - Number of Valid Bits: Seven whole uint8_ts
 | 
			
		||||
        // Calculate BCC - Block Check Character
 | 
			
		||||
        buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5];
 | 
			
		||||
        // Calculate CRC_A
 | 
			
		||||
        result = pcd_calculate_crc_(buffer, 7, &buffer[7]);
 | 
			
		||||
        if (result != STATUS_OK) {
 | 
			
		||||
          return result;
 | 
			
		||||
        }
 | 
			
		||||
        tx_last_bits = 0;  // 0 => All 8 bits are valid.
 | 
			
		||||
        buffer_used = 9;
 | 
			
		||||
        // Store response in the last 3 uint8_ts of buffer (BCC and CRC_A - not needed after tx)
 | 
			
		||||
        response_buffer = &buffer[6];
 | 
			
		||||
        response_length = 3;
 | 
			
		||||
      } else {  // This is an ANTICOLLISION.
 | 
			
		||||
        // Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC);
 | 
			
		||||
        tx_last_bits = current_level_known_bits % 8;
 | 
			
		||||
        count = current_level_known_bits / 8;     // Number of whole uint8_ts in the UID part.
 | 
			
		||||
        index = 2 + count;                        // Number of whole uint8_ts: SEL + NVB + UIDs
 | 
			
		||||
        buffer[1] = (index << 4) + tx_last_bits;  // NVB - Number of Valid Bits
 | 
			
		||||
        buffer_used = index + (tx_last_bits ? 1 : 0);
 | 
			
		||||
        // Store response in the unused part of buffer
 | 
			
		||||
        response_buffer = &buffer[index];
 | 
			
		||||
        response_length = sizeof(buffer) - index;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Set bit adjustments
 | 
			
		||||
      rx_align = tx_last_bits;  // Having a separate variable is overkill. But it makes the next line easier to read.
 | 
			
		||||
      pcd_write_register_(
 | 
			
		||||
          BIT_FRAMING_REG,
 | 
			
		||||
          (rx_align << 4) + tx_last_bits);  // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0]
 | 
			
		||||
 | 
			
		||||
      // Transmit the buffer and receive the response.
 | 
			
		||||
      result = pcd_transceive_data_(buffer, buffer_used, response_buffer, &response_length, &tx_last_bits, rx_align);
 | 
			
		||||
      if (result == STATUS_COLLISION) {  // More than one PICC in the field => collision.
 | 
			
		||||
        uint8_t value_of_coll_reg = pcd_read_register_(
 | 
			
		||||
            COLL_REG);  // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0]
 | 
			
		||||
        if (value_of_coll_reg & 0x20) {  // CollPosNotValid
 | 
			
		||||
          return STATUS_COLLISION;       // Without a valid collision position we cannot continue
 | 
			
		||||
        }
 | 
			
		||||
        uint8_t collision_pos = value_of_coll_reg & 0x1F;  // Values 0-31, 0 means bit 32.
 | 
			
		||||
        if (collision_pos == 0) {
 | 
			
		||||
          collision_pos = 32;
 | 
			
		||||
        }
 | 
			
		||||
        if (collision_pos <= current_level_known_bits) {  // No progress - should not happen
 | 
			
		||||
          return STATUS_INTERNAL_ERROR;
 | 
			
		||||
        }
 | 
			
		||||
        // Choose the PICC with the bit set.
 | 
			
		||||
        current_level_known_bits = collision_pos;
 | 
			
		||||
        count = current_level_known_bits % 8;  // The bit to modify
 | 
			
		||||
        check_bit = (current_level_known_bits - 1) % 8;
 | 
			
		||||
        index = 1 + (current_level_known_bits / 8) + (count ? 1 : 0);  // First uint8_t is index 0.
 | 
			
		||||
        if (response_length > 2)  // Note: Otherwise buffer[index] might be not initialized
 | 
			
		||||
          buffer[index] |= (1 << check_bit);
 | 
			
		||||
      } else if (result != STATUS_OK) {
 | 
			
		||||
        return result;
 | 
			
		||||
      } else {                                 // STATUS_OK
 | 
			
		||||
        if (current_level_known_bits >= 32) {  // This was a SELECT.
 | 
			
		||||
          select_done = true;                  // No more anticollision
 | 
			
		||||
                                               // We continue below outside the while.
 | 
			
		||||
        } else {                               // This was an ANTICOLLISION.
 | 
			
		||||
          // We now have all 32 bits of the UID in this Cascade Level
 | 
			
		||||
          current_level_known_bits = 32;
 | 
			
		||||
          // Run loop again to do the SELECT.
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }  // End of while (!selectDone)
 | 
			
		||||
 | 
			
		||||
    // We do not check the CBB - it was constructed by us above.
 | 
			
		||||
 | 
			
		||||
    // Copy the found UID uint8_ts from buffer[] to uid->uiduint8_t[]
 | 
			
		||||
    index = (buffer[2] == PICC_CMD_CT) ? 3 : 2;  // source index in buffer[]
 | 
			
		||||
    uint8_ts_to_copy = (buffer[2] == PICC_CMD_CT) ? 3 : 4;
 | 
			
		||||
    for (count = 0; count < uint8_ts_to_copy; count++) {
 | 
			
		||||
      uid->uiduint8_t[uid_index + count] = buffer[index++];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check response SAK (Select Acknowledge)
 | 
			
		||||
    if (response_length != 3 || tx_last_bits != 0) {  // SAK must be exactly 24 bits (1 uint8_t + CRC_A).
 | 
			
		||||
      return STATUS_ERROR;
 | 
			
		||||
    }
 | 
			
		||||
    // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those uint8_ts are not needed
 | 
			
		||||
    // anymore.
 | 
			
		||||
    result = pcd_calculate_crc_(response_buffer, 1, &buffer[2]);
 | 
			
		||||
    if (result != STATUS_OK) {
 | 
			
		||||
      return result;
 | 
			
		||||
    }
 | 
			
		||||
    if ((buffer[2] != response_buffer[1]) || (buffer[3] != response_buffer[2])) {
 | 
			
		||||
      return STATUS_CRC_WRONG;
 | 
			
		||||
    }
 | 
			
		||||
    if (response_buffer[0] & 0x04) {  // Cascade bit set - UID not complete yes
 | 
			
		||||
      cascade_level++;
 | 
			
		||||
    } else {
 | 
			
		||||
      uid_complete = true;
 | 
			
		||||
      uid->sak = response_buffer[0];
 | 
			
		||||
    }
 | 
			
		||||
  }  // End of while (!uidComplete)
 | 
			
		||||
 | 
			
		||||
  // Set correct uid->size
 | 
			
		||||
  uid->size = 3 * cascade_level + 1;
 | 
			
		||||
 | 
			
		||||
  return STATUS_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RC522BinarySensor::process(const uint8_t *data, uint8_t len) {
 | 
			
		||||
  if (len != this->uid_.size())
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  for (uint8_t i = 0; i < len; i++) {
 | 
			
		||||
    if (data[i] != this->uid_[i])
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->publish_state(true);
 | 
			
		||||
  this->found_ = true;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
void RC522Trigger::process(const uint8_t *uid, uint8_t uid_length) {
 | 
			
		||||
  char buf[32];
 | 
			
		||||
  format_uid(buf, uid, uid_length);
 | 
			
		||||
  this->trigger(std::string(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace rc522_spi
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -10,292 +10,46 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#include "esphome/components/rc522/rc522.h"
 | 
			
		||||
#include "esphome/components/spi/spi.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace rc522_spi {
 | 
			
		||||
 | 
			
		||||
class RC522BinarySensor;
 | 
			
		||||
class RC522Trigger;
 | 
			
		||||
 | 
			
		||||
class RC522 : public PollingComponent,
 | 
			
		||||
              public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
 | 
			
		||||
                                    spi::DATA_RATE_4MHZ> {
 | 
			
		||||
class RC522Spi : public rc522::RC522,
 | 
			
		||||
                 public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
 | 
			
		||||
                                       spi::DATA_RATE_4MHZ> {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  void update() override;
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; };
 | 
			
		||||
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void register_tag(RC522BinarySensor *tag) { this->binary_sensors_.push_back(tag); }
 | 
			
		||||
  void register_trigger(RC522Trigger *trig) { this->triggers_.push_back(trig); }
 | 
			
		||||
 | 
			
		||||
  void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  enum PcdRegister : uint8_t {
 | 
			
		||||
    // Page 0: Command and status
 | 
			
		||||
    // 0x00      // reserved for future use
 | 
			
		||||
    COMMAND_REG = 0x01 << 1,      // starts and stops command execution
 | 
			
		||||
    COM_I_EN_REG = 0x02 << 1,     // enable and disable interrupt request control bits
 | 
			
		||||
    DIV_I_EN_REG = 0x03 << 1,     // enable and disable interrupt request control bits
 | 
			
		||||
    COM_IRQ_REG = 0x04 << 1,      // interrupt request bits
 | 
			
		||||
    DIV_IRQ_REG = 0x05 << 1,      // interrupt request bits
 | 
			
		||||
    ERROR_REG = 0x06 << 1,        // error bits showing the error status of the last command executed
 | 
			
		||||
    STATUS1_REG = 0x07 << 1,      // communication status bits
 | 
			
		||||
    STATUS2_REG = 0x08 << 1,      // receiver and transmitter status bits
 | 
			
		||||
    FIFO_DATA_REG = 0x09 << 1,    // input and output of 64 uint8_t FIFO buffer
 | 
			
		||||
    FIFO_LEVEL_REG = 0x0A << 1,   // number of uint8_ts stored in the FIFO buffer
 | 
			
		||||
    WATER_LEVEL_REG = 0x0B << 1,  // level for FIFO underflow and overflow warning
 | 
			
		||||
    CONTROL_REG = 0x0C << 1,      // miscellaneous control registers
 | 
			
		||||
    BIT_FRAMING_REG = 0x0D << 1,  // adjustments for bit-oriented frames
 | 
			
		||||
    COLL_REG = 0x0E << 1,         // bit position of the first bit-collision detected on the RF interface
 | 
			
		||||
    //                 0x0F     // reserved for future use
 | 
			
		||||
 | 
			
		||||
    // Page 1: Command
 | 
			
		||||
    //               0x10      // reserved for future use
 | 
			
		||||
    MODE_REG = 0x11 << 1,          // defines general modes for transmitting and receiving
 | 
			
		||||
    TX_MODE_REG = 0x12 << 1,       // defines transmission data rate and framing
 | 
			
		||||
    RX_MODE_REG = 0x13 << 1,       // defines reception data rate and framing
 | 
			
		||||
    TX_CONTROL_REG = 0x14 << 1,    // controls the logical behavior of the antenna driver pins TX1 and TX2
 | 
			
		||||
    TX_ASK_REG = 0x15 << 1,        // controls the setting of the transmission modulation
 | 
			
		||||
    TX_SEL_REG = 0x16 << 1,        // selects the internal sources for the antenna driver
 | 
			
		||||
    RX_SEL_REG = 0x17 << 1,        // selects internal receiver settings
 | 
			
		||||
    RX_THRESHOLD_REG = 0x18 << 1,  // selects thresholds for the bit decoder
 | 
			
		||||
    DEMOD_REG = 0x19 << 1,         // defines demodulator settings
 | 
			
		||||
    //               0x1A      // reserved for future use
 | 
			
		||||
    //               0x1B      // reserved for future use
 | 
			
		||||
    MF_TX_REG = 0x1C << 1,  // controls some MIFARE communication transmit parameters
 | 
			
		||||
    MF_RX_REG = 0x1D << 1,  // controls some MIFARE communication receive parameters
 | 
			
		||||
    //               0x1E      // reserved for future use
 | 
			
		||||
    SERIAL_SPEED_REG = 0x1F << 1,  // selects the speed of the serial UART interface
 | 
			
		||||
 | 
			
		||||
    // Page 2: Configuration
 | 
			
		||||
    //               0x20      // reserved for future use
 | 
			
		||||
    CRC_RESULT_REG_H = 0x21 << 1,  // shows the MSB and LSB values of the CRC calculation
 | 
			
		||||
    CRC_RESULT_REG_L = 0x22 << 1,
 | 
			
		||||
    //               0x23      // reserved for future use
 | 
			
		||||
    MOD_WIDTH_REG = 0x24 << 1,  // controls the ModWidth setting?
 | 
			
		||||
    //               0x25      // reserved for future use
 | 
			
		||||
    RF_CFG_REG = 0x26 << 1,       // configures the receiver gain
 | 
			
		||||
    GS_N_REG = 0x27 << 1,         // selects the conductance of the antenna driver pins TX1 and TX2 for modulation
 | 
			
		||||
    CW_GS_P_REG = 0x28 << 1,      // defines the conductance of the p-driver output during periods of no modulation
 | 
			
		||||
    MOD_GS_P_REG = 0x29 << 1,     // defines the conductance of the p-driver output during periods of modulation
 | 
			
		||||
    T_MODE_REG = 0x2A << 1,       // defines settings for the internal timer
 | 
			
		||||
    T_PRESCALER_REG = 0x2B << 1,  // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg.
 | 
			
		||||
    T_RELOAD_REG_H = 0x2C << 1,   // defines the 16-bit timer reload value
 | 
			
		||||
    T_RELOAD_REG_L = 0x2D << 1,
 | 
			
		||||
    T_COUNTER_VALUE_REG_H = 0x2E << 1,  // shows the 16-bit timer value
 | 
			
		||||
    T_COUNTER_VALUE_REG_L = 0x2F << 1,
 | 
			
		||||
 | 
			
		||||
    // Page 3: Test Registers
 | 
			
		||||
    //               0x30      // reserved for future use
 | 
			
		||||
    TEST_SEL1_REG = 0x31 << 1,       // general test signal configuration
 | 
			
		||||
    TEST_SEL2_REG = 0x32 << 1,       // general test signal configuration
 | 
			
		||||
    TEST_PIN_EN_REG = 0x33 << 1,     // enables pin output driver on pins D1 to D7
 | 
			
		||||
    TEST_PIN_VALUE_REG = 0x34 << 1,  // defines the values for D1 to D7 when it is used as an I/O bus
 | 
			
		||||
    TEST_BUS_REG = 0x35 << 1,        // shows the status of the internal test bus
 | 
			
		||||
    AUTO_TEST_REG = 0x36 << 1,       // controls the digital self-test
 | 
			
		||||
    VERSION_REG = 0x37 << 1,         // shows the software version
 | 
			
		||||
    ANALOG_TEST_REG = 0x38 << 1,     // controls the pins AUX1 and AUX2
 | 
			
		||||
    TEST_DA_C1_REG = 0x39 << 1,      // defines the test value for TestDAC1
 | 
			
		||||
    TEST_DA_C2_REG = 0x3A << 1,      // defines the test value for TestDAC2
 | 
			
		||||
    TEST_ADC_REG = 0x3B << 1         // shows the value of ADC I and Q channels
 | 
			
		||||
                                     //               0x3C      // reserved for production tests
 | 
			
		||||
                                     //               0x3D      // reserved for production tests
 | 
			
		||||
                                     //               0x3E      // reserved for production tests
 | 
			
		||||
                                     //               0x3F      // reserved for production tests
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // MFRC522 commands. Described in chapter 10 of the datasheet.
 | 
			
		||||
  enum PcdCommand : uint8_t {
 | 
			
		||||
    PCD_IDLE = 0x00,                // no action, cancels current command execution
 | 
			
		||||
    PCD_MEM = 0x01,                 // stores 25 uint8_ts into the internal buffer
 | 
			
		||||
    PCD_GENERATE_RANDOM_ID = 0x02,  // generates a 10-uint8_t random ID number
 | 
			
		||||
    PCD_CALC_CRC = 0x03,            // activates the CRC coprocessor or performs a self-test
 | 
			
		||||
    PCD_TRANSMIT = 0x04,            // transmits data from the FIFO buffer
 | 
			
		||||
    PCD_NO_CMD_CHANGE = 0x07,       // no command change, can be used to modify the CommandReg register bits without
 | 
			
		||||
                                    // affecting the command, for example, the PowerDown bit
 | 
			
		||||
    PCD_RECEIVE = 0x08,             // activates the receiver circuits
 | 
			
		||||
    PCD_TRANSCEIVE =
 | 
			
		||||
        0x0C,  // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission
 | 
			
		||||
    PCD_MF_AUTHENT = 0x0E,  // performs the MIFARE standard authentication as a reader
 | 
			
		||||
    PCD_SOFT_RESET = 0x0F   // resets the MFRC522
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Commands sent to the PICC.
 | 
			
		||||
  enum PiccCommand : uint8_t {
 | 
			
		||||
    // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4)
 | 
			
		||||
    PICC_CMD_REQA = 0x26,     // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for
 | 
			
		||||
                              // anticollision or selection. 7 bit frame.
 | 
			
		||||
    PICC_CMD_WUPA = 0x52,     // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and
 | 
			
		||||
                              // prepare for anticollision or selection. 7 bit frame.
 | 
			
		||||
    PICC_CMD_CT = 0x88,       // Cascade Tag. Not really a command, but used during anti collision.
 | 
			
		||||
    PICC_CMD_SEL_CL1 = 0x93,  // Anti collision/Select, Cascade Level 1
 | 
			
		||||
    PICC_CMD_SEL_CL2 = 0x95,  // Anti collision/Select, Cascade Level 2
 | 
			
		||||
    PICC_CMD_SEL_CL3 = 0x97,  // Anti collision/Select, Cascade Level 3
 | 
			
		||||
    PICC_CMD_HLTA = 0x50,     // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT.
 | 
			
		||||
    PICC_CMD_RATS = 0xE0,     // Request command for Answer To Reset.
 | 
			
		||||
    // The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9)
 | 
			
		||||
    // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on
 | 
			
		||||
    // the sector.
 | 
			
		||||
    // The read/write commands can also be used for MIFARE Ultralight.
 | 
			
		||||
    PICC_CMD_MF_AUTH_KEY_A = 0x60,  // Perform authentication with Key A
 | 
			
		||||
    PICC_CMD_MF_AUTH_KEY_B = 0x61,  // Perform authentication with Key B
 | 
			
		||||
    PICC_CMD_MF_READ =
 | 
			
		||||
        0x30,  // Reads one 16 uint8_t block from the authenticated sector of the PICC. Also used for MIFARE Ultralight.
 | 
			
		||||
    PICC_CMD_MF_WRITE = 0xA0,  // Writes one 16 uint8_t block to the authenticated sector of the PICC. Called
 | 
			
		||||
                               // "COMPATIBILITY WRITE" for MIFARE Ultralight.
 | 
			
		||||
    PICC_CMD_MF_DECREMENT =
 | 
			
		||||
        0xC0,  // Decrements the contents of a block and stores the result in the internal data register.
 | 
			
		||||
    PICC_CMD_MF_INCREMENT =
 | 
			
		||||
        0xC1,  // Increments the contents of a block and stores the result in the internal data register.
 | 
			
		||||
    PICC_CMD_MF_RESTORE = 0xC2,   // Reads the contents of a block into the internal data register.
 | 
			
		||||
    PICC_CMD_MF_TRANSFER = 0xB0,  // Writes the contents of the internal data register to a block.
 | 
			
		||||
    // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6)
 | 
			
		||||
    // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight.
 | 
			
		||||
    PICC_CMD_UL_WRITE = 0xA2  // Writes one 4 uint8_t page to the PICC.
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more.
 | 
			
		||||
  // last value set to 0xff, then compiler uses less ram, it seems some optimisations are triggered
 | 
			
		||||
  enum StatusCode : uint8_t {
 | 
			
		||||
    STATUS_OK,                 // Success
 | 
			
		||||
    STATUS_ERROR,              // Error in communication
 | 
			
		||||
    STATUS_COLLISION,          // Collission detected
 | 
			
		||||
    STATUS_TIMEOUT,            // Timeout in communication.
 | 
			
		||||
    STATUS_NO_ROOM,            // A buffer is not big enough.
 | 
			
		||||
    STATUS_INTERNAL_ERROR,     // Internal error in the code. Should not happen ;-)
 | 
			
		||||
    STATUS_INVALID,            // Invalid argument.
 | 
			
		||||
    STATUS_CRC_WRONG,          // The CRC_A does not match
 | 
			
		||||
    STATUS_MIFARE_NACK = 0xff  // A MIFARE PICC responded with NAK.
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // A struct used for passing the UID of a PICC.
 | 
			
		||||
  using Uid = struct {
 | 
			
		||||
    uint8_t size;  // Number of uint8_ts in the UID. 4, 7 or 10.
 | 
			
		||||
    uint8_t uiduint8_t[10];
 | 
			
		||||
    uint8_t sak;  // The SAK (Select acknowledge) uint8_t returned from the PICC after successful selection.
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Uid uid_;
 | 
			
		||||
  uint32_t update_wait_{0};
 | 
			
		||||
 | 
			
		||||
  void pcd_reset_();
 | 
			
		||||
  void initialize_();
 | 
			
		||||
  void pcd_antenna_on_();
 | 
			
		||||
  uint8_t pcd_read_register_(PcdRegister reg  ///< The register to read from. One of the PCD_Register enums.
 | 
			
		||||
  );
 | 
			
		||||
  uint8_t pcd_read_register(PcdRegister reg  ///< The register to read from. One of the PCD_Register enums.
 | 
			
		||||
                            ) override;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Reads a number of uint8_ts from the specified register in the MFRC522 chip.
 | 
			
		||||
   * The interface is described in the datasheet section 8.1.2.
 | 
			
		||||
   */
 | 
			
		||||
  void pcd_read_register_(PcdRegister reg,  ///< The register to read from. One of the PCD_Register enums.
 | 
			
		||||
                          uint8_t count,    ///< The number of uint8_ts to read
 | 
			
		||||
                          uint8_t *values,  ///< uint8_t array to store the values in.
 | 
			
		||||
                          uint8_t rx_align  ///< Only bit positions rxAlign..7 in values[0] are updated.
 | 
			
		||||
  );
 | 
			
		||||
  void pcd_write_register_(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums.
 | 
			
		||||
                           uint8_t value     ///< The value to write.
 | 
			
		||||
  );
 | 
			
		||||
  void pcd_read_register(PcdRegister reg,  ///< The register to read from. One of the PCD_Register enums.
 | 
			
		||||
                         uint8_t count,    ///< The number of uint8_ts to read
 | 
			
		||||
                         uint8_t *values,  ///< uint8_t array to store the values in.
 | 
			
		||||
                         uint8_t rx_align  ///< Only bit positions rxAlign..7 in values[0] are updated.
 | 
			
		||||
                         ) override;
 | 
			
		||||
  void pcd_write_register(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums.
 | 
			
		||||
                          uint8_t value     ///< The value to write.
 | 
			
		||||
                          ) override;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Writes a number of uint8_ts to the specified register in the MFRC522 chip.
 | 
			
		||||
   * The interface is described in the datasheet section 8.1.2.
 | 
			
		||||
   */
 | 
			
		||||
  void pcd_write_register_(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums.
 | 
			
		||||
                           uint8_t count,    ///< The number of uint8_ts to write to the register
 | 
			
		||||
                           uint8_t *values   ///< The values to write. uint8_t array.
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  StatusCode picc_request_a_(
 | 
			
		||||
      uint8_t *buffer_atqa,  ///< The buffer to store the ATQA (Answer to request) in
 | 
			
		||||
      uint8_t *buffer_size   ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK.
 | 
			
		||||
  );
 | 
			
		||||
  StatusCode picc_reqa_or_wupa_(
 | 
			
		||||
      uint8_t command,       ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA
 | 
			
		||||
      uint8_t *buffer_atqa,  ///< The buffer to store the ATQA (Answer to request) in
 | 
			
		||||
      uint8_t *buffer_size   ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK.
 | 
			
		||||
  );
 | 
			
		||||
  void pcd_set_register_bit_mask_(PcdRegister reg,  ///< The register to update. One of the PCD_Register enums.
 | 
			
		||||
                                  uint8_t mask      ///< The bits to set.
 | 
			
		||||
  );
 | 
			
		||||
  void pcd_clear_register_bit_mask_(PcdRegister reg,  ///< The register to update. One of the PCD_Register enums.
 | 
			
		||||
                                    uint8_t mask      ///< The bits to clear.
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  StatusCode pcd_transceive_data_(uint8_t *send_data, uint8_t send_len, uint8_t *back_data, uint8_t *back_len,
 | 
			
		||||
                                  uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false);
 | 
			
		||||
  StatusCode pcd_communicate_with_picc_(uint8_t command, uint8_t wait_i_rq, uint8_t *send_data, uint8_t send_len,
 | 
			
		||||
                                        uint8_t *back_data = nullptr, uint8_t *back_len = nullptr,
 | 
			
		||||
                                        uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false);
 | 
			
		||||
  StatusCode pcd_calculate_crc_(
 | 
			
		||||
      uint8_t *data,   ///< In: Pointer to the data to transfer to the FIFO for CRC calculation.
 | 
			
		||||
      uint8_t length,  ///< In: The number of uint8_ts to transfer.
 | 
			
		||||
      uint8_t *result  ///< Out: Pointer to result buffer. Result is written to result[0..1], low uint8_t first.
 | 
			
		||||
  );
 | 
			
		||||
  RC522::StatusCode picc_is_new_card_present_();
 | 
			
		||||
  bool picc_read_card_serial_();
 | 
			
		||||
  StatusCode picc_select_(
 | 
			
		||||
      Uid *uid,               ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID.
 | 
			
		||||
      uint8_t valid_bits = 0  ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also
 | 
			
		||||
                              ///< supply uid->size.
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  /** Read a data frame from the RC522 and return the result as a vector.
 | 
			
		||||
   *
 | 
			
		||||
   * Note that is_ready needs to be checked first before requesting this method.
 | 
			
		||||
   *
 | 
			
		||||
   * On failure, an empty vector is returned.
 | 
			
		||||
   */
 | 
			
		||||
  std::vector<uint8_t> r_c522_read_data_();
 | 
			
		||||
 | 
			
		||||
  GPIOPin *reset_pin_{nullptr};
 | 
			
		||||
  uint8_t reset_count_{0};
 | 
			
		||||
  uint32_t reset_timeout_{0};
 | 
			
		||||
  bool initialize_pending_{false};
 | 
			
		||||
  std::vector<RC522BinarySensor *> binary_sensors_;
 | 
			
		||||
  std::vector<RC522Trigger *> triggers_;
 | 
			
		||||
 | 
			
		||||
  enum RC522Error {
 | 
			
		||||
    NONE = 0,
 | 
			
		||||
    RESET_FAILED,
 | 
			
		||||
  } error_code_{NONE};
 | 
			
		||||
  void pcd_write_register(PcdRegister reg,  ///< The register to write to. One of the PCD_Register enums.
 | 
			
		||||
                          uint8_t count,    ///< The number of uint8_ts to write to the register
 | 
			
		||||
                          uint8_t *values   ///< The values to write. uint8_t array.
 | 
			
		||||
                          ) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class RC522BinarySensor : public binary_sensor::BinarySensor {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_uid(const std::vector<uint8_t> &uid) { uid_ = uid; }
 | 
			
		||||
 | 
			
		||||
  bool process(const uint8_t *data, uint8_t len);
 | 
			
		||||
 | 
			
		||||
  void on_scan_end() {
 | 
			
		||||
    if (!this->found_) {
 | 
			
		||||
      this->publish_state(false);
 | 
			
		||||
    }
 | 
			
		||||
    this->found_ = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::vector<uint8_t> uid_;
 | 
			
		||||
  bool found_{false};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class RC522Trigger : public Trigger<std::string> {
 | 
			
		||||
 public:
 | 
			
		||||
  void process(const uint8_t *uid, uint8_t uid_length);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifndef MFRC522_SPICLOCK
 | 
			
		||||
#define MFRC522_SPICLOCK SPI_CLOCK_DIV4  // MFRC522 accept upto 10MHz
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace rc522_spi
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,7 @@ void SNTPComponent::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Server 3: '%s'", this->server_3_.c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Timezone: '%s'", this->timezone_.c_str());
 | 
			
		||||
}
 | 
			
		||||
void SNTPComponent::update() {}
 | 
			
		||||
void SNTPComponent::loop() {
 | 
			
		||||
  if (this->has_time_)
 | 
			
		||||
    return;
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ class SNTPComponent : public time::RealTimeClock {
 | 
			
		||||
  }
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
 | 
			
		||||
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,7 @@ enum SPIClockPhase {
 | 
			
		||||
 */
 | 
			
		||||
enum SPIDataRate : uint32_t {
 | 
			
		||||
  DATA_RATE_1KHZ = 1000,
 | 
			
		||||
  DATA_RATE_75KHZ = 75000,
 | 
			
		||||
  DATA_RATE_200KHZ = 200000,
 | 
			
		||||
  DATA_RATE_1MHZ = 1000000,
 | 
			
		||||
  DATA_RATE_2MHZ = 2000000,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ CODEOWNERS = ['@OttoWinter']
 | 
			
		||||
IS_PLATFORM_COMPONENT = True
 | 
			
		||||
 | 
			
		||||
time_ns = cg.esphome_ns.namespace('time')
 | 
			
		||||
RealTimeClock = time_ns.class_('RealTimeClock', cg.Component)
 | 
			
		||||
RealTimeClock = time_ns.class_('RealTimeClock', cg.PollingComponent)
 | 
			
		||||
CronTrigger = time_ns.class_('CronTrigger', automation.Trigger.template(), cg.Component)
 | 
			
		||||
ESPTime = time_ns.struct('ESPTime')
 | 
			
		||||
TimeHasTimeCondition = time_ns.class_('TimeHasTimeCondition', Condition)
 | 
			
		||||
@@ -294,7 +294,7 @@ TIME_SCHEMA = cv.Schema({
 | 
			
		||||
        cv.Optional(CONF_CRON): validate_cron_raw,
 | 
			
		||||
        cv.Optional(CONF_AT): validate_time_at,
 | 
			
		||||
    }, validate_cron_keys),
 | 
			
		||||
})
 | 
			
		||||
}).extend(cv.polling_component_schema('15min'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ RealTimeClock::RealTimeClock() = default;
 | 
			
		||||
void RealTimeClock::call_setup() {
 | 
			
		||||
  setenv("TZ", this->timezone_.c_str(), 1);
 | 
			
		||||
  tzset();
 | 
			
		||||
  this->setup();
 | 
			
		||||
  PollingComponent::call_setup();
 | 
			
		||||
}
 | 
			
		||||
void RealTimeClock::synchronize_epoch_(uint32_t epoch) {
 | 
			
		||||
  struct timeval timev {
 | 
			
		||||
 
 | 
			
		||||
@@ -106,7 +106,7 @@ struct ESPTime {
 | 
			
		||||
/// The C library (newlib) available on ESPs only supports TZ strings that specify an offset and DST info;
 | 
			
		||||
/// you cannot specify zone names or paths to zoneinfo files.
 | 
			
		||||
/// \see https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
 | 
			
		||||
class RealTimeClock : public Component {
 | 
			
		||||
class RealTimeClock : public PollingComponent {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit RealTimeClock();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -105,7 +105,7 @@ void WhirlpoolClimate::transmit_state() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Checksum
 | 
			
		||||
  for (uint8_t i = 2; i < 12; i++)
 | 
			
		||||
  for (uint8_t i = 2; i < 13; i++)
 | 
			
		||||
    remote_state[13] ^= remote_state[i];
 | 
			
		||||
  for (uint8_t i = 14; i < 20; i++)
 | 
			
		||||
    remote_state[20] ^= remote_state[i];
 | 
			
		||||
@@ -184,7 +184,7 @@ bool WhirlpoolClimate::on_receive(remote_base::RemoteReceiveData data) {
 | 
			
		||||
  uint8_t checksum13 = 0;
 | 
			
		||||
  uint8_t checksum20 = 0;
 | 
			
		||||
  // Calculate  checksum and compare with signal value.
 | 
			
		||||
  for (uint8_t i = 2; i < 12; i++)
 | 
			
		||||
  for (uint8_t i = 2; i < 13; i++)
 | 
			
		||||
    checksum13 ^= remote_state[i];
 | 
			
		||||
  for (uint8_t i = 14; i < 20; i++)
 | 
			
		||||
    checksum20 ^= remote_state[i];
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
MAJOR_VERSION = 1
 | 
			
		||||
MINOR_VERSION = 16
 | 
			
		||||
PATCH_VERSION = '0b2'
 | 
			
		||||
PATCH_VERSION = '0b4'
 | 
			
		||||
__short_version__ = f'{MAJOR_VERSION}.{MINOR_VERSION}'
 | 
			
		||||
__version__ = f'{__short_version__}.{PATCH_VERSION}'
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,5 +10,5 @@ pytz==2020.5
 | 
			
		||||
pyserial==3.5
 | 
			
		||||
ifaddr==0.1.7
 | 
			
		||||
platformio==5.0.4
 | 
			
		||||
esptool==3.0
 | 
			
		||||
esptool==2.8
 | 
			
		||||
click==7.1.2
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,6 @@ pexpect==4.8.0
 | 
			
		||||
# Unit tests
 | 
			
		||||
pytest==6.2.1
 | 
			
		||||
pytest-cov==2.10.1
 | 
			
		||||
pytest-mock==3.3.1
 | 
			
		||||
pytest-mock==3.5.1
 | 
			
		||||
asyncmock==0.4.2
 | 
			
		||||
hypothesis==5.21.0
 | 
			
		||||
 
 | 
			
		||||
@@ -197,16 +197,21 @@ wled:
 | 
			
		||||
 | 
			
		||||
adalight:
 | 
			
		||||
 | 
			
		||||
mcp3008:
 | 
			
		||||
  - id: 'mcp3008_hub'
 | 
			
		||||
    cs_pin: GPIO12
 | 
			
		||||
 | 
			
		||||
mcp23s08:
 | 
			
		||||
  - id: 'mcp23s08_hub'
 | 
			
		||||
    cs_pin: GPIO12
 | 
			
		||||
    deviceaddress: 0
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
mcp23s17:
 | 
			
		||||
  - id: 'mcp23s17_hub'
 | 
			
		||||
    cs_pin: GPIO12
 | 
			
		||||
    deviceaddress: 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
sensor:
 | 
			
		||||
  - platform: adc
 | 
			
		||||
    pin: A0
 | 
			
		||||
@@ -801,6 +806,12 @@ sensor:
 | 
			
		||||
    id: ph_ezo
 | 
			
		||||
    address: 99
 | 
			
		||||
    unit_of_measurement: 'pH'
 | 
			
		||||
  - platform: mcp3008
 | 
			
		||||
    update_interval: 5s
 | 
			
		||||
    mcp3008_id: 'mcp3008_hub'
 | 
			
		||||
    id: freezer_temp_source
 | 
			
		||||
    reference_voltage: 3.19
 | 
			
		||||
    number: 0
 | 
			
		||||
 | 
			
		||||
esp32_touch:
 | 
			
		||||
  setup_mode: False
 | 
			
		||||
@@ -813,7 +824,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 +833,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 +1402,7 @@ climate:
 | 
			
		||||
 | 
			
		||||
switch:
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    name: "MCP23S08 Pin #0"
 | 
			
		||||
    name: 'MCP23S08 Pin #0'
 | 
			
		||||
    pin:
 | 
			
		||||
      mcp23s08: mcp23s08_hub
 | 
			
		||||
      # Use pin number 0
 | 
			
		||||
@@ -1399,7 +1410,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 +1834,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:
 | 
			
		||||
@@ -1837,6 +1854,7 @@ time:
 | 
			
		||||
      then:
 | 
			
		||||
        - lambda: 'ESP_LOGD("main", "time");'
 | 
			
		||||
  - platform: gps
 | 
			
		||||
    update_interval: 1h
 | 
			
		||||
    on_time:
 | 
			
		||||
      seconds: 0
 | 
			
		||||
      minutes: /15
 | 
			
		||||
@@ -1845,13 +1863,12 @@ time:
 | 
			
		||||
          id: ds1307_time
 | 
			
		||||
  - platform: ds1307
 | 
			
		||||
    id: ds1307_time
 | 
			
		||||
    update_interval: never
 | 
			
		||||
    on_time:
 | 
			
		||||
      seconds: 0
 | 
			
		||||
      then:
 | 
			
		||||
          ds1307.read
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cover:
 | 
			
		||||
  - platform: template
 | 
			
		||||
    name: 'Template Cover'
 | 
			
		||||
@@ -1933,7 +1950,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 +1984,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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user