[Fingerprint_grow] Implements Sleep Mode feature (#6116)

This commit is contained in:
alexborro 2024-03-11 00:04:16 +01:00 committed by GitHub
parent 247baa414a
commit 8850b959e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 185 additions and 25 deletions

View file

@ -118,7 +118,7 @@ esphome/components/ezo_pmp/* @carlos-sarmiento
esphome/components/factory_reset/* @anatoly-savchenkov
esphome/components/fastled_base/* @OttoWinter
esphome/components/feedback/* @ianchi
esphome/components/fingerprint_grow/* @OnFreund @loongyh
esphome/components/fingerprint_grow/* @OnFreund @alexborro @loongyh
esphome/components/fs3000/* @kahrendt
esphome/components/ft5x06/* @clydebarrow
esphome/components/ft63x6/* @gpambrozio

View file

@ -25,12 +25,14 @@ from esphome.const import (
CONF_TRIGGER_ID,
)
CODEOWNERS = ["@OnFreund", "@loongyh"]
CODEOWNERS = ["@OnFreund", "@loongyh", "@alexborro"]
DEPENDENCIES = ["uart"]
AUTO_LOAD = ["binary_sensor", "sensor"]
MULTI_CONF = True
CONF_FINGERPRINT_GROW_ID = "fingerprint_grow_id"
CONF_SENSOR_POWER_PIN = "sensor_power_pin"
CONF_IDLE_PERIOD_TO_SLEEP = "idle_period_to_sleep"
fingerprint_grow_ns = cg.esphome_ns.namespace("fingerprint_grow")
FingerprintGrowComponent = fingerprint_grow_ns.class_(
@ -102,11 +104,26 @@ AURA_LED_COLORS = {
}
validate_aura_led_colors = cv.enum(AURA_LED_COLORS, upper=True)
CONFIG_SCHEMA = (
def validate(config):
if CONF_SENSOR_POWER_PIN in config and CONF_SENSING_PIN not in config:
raise cv.Invalid("You cannot use the Sensor Power Pin without a Sensing Pin")
if CONF_IDLE_PERIOD_TO_SLEEP in config and CONF_SENSOR_POWER_PIN not in config:
raise cv.Invalid(
"You cannot have an Idle Period to Sleep without a Sensor Power Pin"
)
return config
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(FingerprintGrowComponent),
cv.Optional(CONF_SENSING_PIN): pins.gpio_input_pin_schema,
cv.Optional(CONF_SENSOR_POWER_PIN): pins.gpio_output_pin_schema,
cv.Optional(
CONF_IDLE_PERIOD_TO_SLEEP
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_PASSWORD): cv.uint32_t,
cv.Optional(CONF_NEW_PASSWORD): cv.uint32_t,
cv.Optional(CONF_ON_FINGER_SCAN_START): automation.validate_automation(
@ -168,7 +185,8 @@ CONFIG_SCHEMA = (
}
)
.extend(cv.polling_component_schema("500ms"))
.extend(uart.UART_DEVICE_SCHEMA)
.extend(uart.UART_DEVICE_SCHEMA),
validate,
)
@ -188,6 +206,14 @@ async def to_code(config):
sensing_pin = await cg.gpio_pin_expression(config[CONF_SENSING_PIN])
cg.add(var.set_sensing_pin(sensing_pin))
if CONF_SENSOR_POWER_PIN in config:
sensor_power_pin = await cg.gpio_pin_expression(config[CONF_SENSOR_POWER_PIN])
cg.add(var.set_sensor_power_pin(sensor_power_pin))
if CONF_IDLE_PERIOD_TO_SLEEP in config:
idle_period_to_sleep_ms = config[CONF_IDLE_PERIOD_TO_SLEEP]
cg.add(var.set_idle_period_to_sleep_ms(idle_period_to_sleep_ms))
for conf in config.get(CONF_ON_FINGER_SCAN_START, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)

View file

@ -16,9 +16,14 @@ void FingerprintGrowComponent::update() {
}
if (this->has_sensing_pin_) {
// A finger touch results in a low level (digital_read() == false)
if (this->sensing_pin_->digital_read()) {
ESP_LOGV(TAG, "No touch sensing");
this->waiting_removal_ = false;
if ((this->enrollment_image_ == 0) && // Not in enrolment process
(millis() - this->last_transfer_ms_ > this->idle_period_to_sleep_ms_) && (this->is_sensor_awake_)) {
this->sensor_sleep_();
}
return;
} else if (!this->waiting_removal_) {
this->finger_scan_start_callback_.call();
@ -53,7 +58,29 @@ void FingerprintGrowComponent::update() {
void FingerprintGrowComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up Grow Fingerprint Reader...");
this->has_sensing_pin_ = (this->sensing_pin_ != nullptr);
this->has_power_pin_ = (this->sensor_power_pin_ != nullptr);
// Call pins setup, so we effectively apply the config generated from the yaml file.
if (this->has_sensing_pin_) {
this->sensing_pin_->setup();
}
if (this->has_power_pin_) {
// Starts with output low (disabling power) to avoid glitches in the sensor
this->sensor_power_pin_->digital_write(false);
this->sensor_power_pin_->setup();
// If the user didn't specify an idle period to sleep, applies the default.
if (this->idle_period_to_sleep_ms_ == UINT32_MAX) {
this->idle_period_to_sleep_ms_ = DEFAULT_IDLE_PERIOD_TO_SLEEP_MS;
}
}
// Place the sensor in a known (sleep/off) state and sync internal var state.
this->sensor_sleep_();
delay(20); // This delay guarantees the sensor will in fact be powered power.
if (this->check_password_()) {
if (this->new_password_ != -1) {
if (this->set_password_())
@ -335,7 +362,7 @@ void FingerprintGrowComponent::aura_led_control(uint8_t state, uint8_t speed, ui
}
}
uint8_t FingerprintGrowComponent::send_command_() {
uint8_t FingerprintGrowComponent::transfer_(std::vector<uint8_t> *p_data_buffer) {
while (this->available())
this->read();
this->write((uint8_t) (START_CODE >> 8));
@ -346,12 +373,12 @@ uint8_t FingerprintGrowComponent::send_command_() {
this->write(this->address_[3]);
this->write(COMMAND);
uint16_t wire_length = this->data_.size() + 2;
uint16_t wire_length = p_data_buffer->size() + 2;
this->write((uint8_t) (wire_length >> 8));
this->write((uint8_t) (wire_length & 0xFF));
uint16_t sum = ((wire_length) >> 8) + ((wire_length) &0xFF) + COMMAND;
for (auto data : this->data_) {
for (auto data : *p_data_buffer) {
this->write(data);
sum += data;
}
@ -359,7 +386,7 @@ uint8_t FingerprintGrowComponent::send_command_() {
this->write((uint8_t) (sum >> 8));
this->write((uint8_t) (sum & 0xFF));
this->data_.clear();
p_data_buffer->clear();
uint8_t byte;
uint16_t idx = 0, length = 0;
@ -369,7 +396,9 @@ uint8_t FingerprintGrowComponent::send_command_() {
delay(1);
continue;
}
byte = this->read();
switch (idx) {
case 0:
if (byte != (uint8_t) (START_CODE >> 8))
@ -403,9 +432,9 @@ uint8_t FingerprintGrowComponent::send_command_() {
length |= byte;
break;
default:
this->data_.push_back(byte);
p_data_buffer->push_back(byte);
if ((idx - 8) == length) {
switch (this->data_[0]) {
switch ((*p_data_buffer)[0]) {
case OK:
case NO_FINGER:
case IMAGE_FAIL:
@ -425,38 +454,122 @@ uint8_t FingerprintGrowComponent::send_command_() {
ESP_LOGE(TAG, "Reader failed to process request");
break;
default:
ESP_LOGE(TAG, "Unknown response received from reader: %d", this->data_[0]);
ESP_LOGE(TAG, "Unknown response received from reader: 0x%.2X", (*p_data_buffer)[0]);
break;
}
return this->data_[0];
this->last_transfer_ms_ = millis();
return (*p_data_buffer)[0];
}
break;
}
idx++;
}
ESP_LOGE(TAG, "No response received from reader");
this->data_[0] = TIMEOUT;
(*p_data_buffer)[0] = TIMEOUT;
this->last_transfer_ms_ = millis();
return TIMEOUT;
}
uint8_t FingerprintGrowComponent::send_command_() {
this->sensor_wakeup_();
return this->transfer_(&this->data_);
}
void FingerprintGrowComponent::sensor_wakeup_() {
// Immediately return if there is no power pin or the sensor is already on
if ((!this->has_power_pin_) || (this->is_sensor_awake_))
return;
this->sensor_power_pin_->digital_write(true);
this->is_sensor_awake_ = true;
uint8_t byte = TIMEOUT;
// Wait for the byte HANDSHAKE_SIGN from the sensor meaning it is operational.
for (uint16_t timer = 0; timer < WAIT_FOR_WAKE_UP_MS; timer++) {
if (this->available() > 0) {
byte = this->read();
/* If the received byte is zero, the UART probably misinterpreted a raising edge on
* the RX pin due the power up as byte "zero" - I verified this behaviour using
* the esp32-arduino lib. So here we just ignore this fake byte.
*/
if (byte != 0)
break;
}
delay(1);
}
/* Lets check if the received by is a HANDSHAKE_SIGN, otherwise log an error
* message and try to continue on the best effort.
*/
if (byte == HANDSHAKE_SIGN) {
ESP_LOGD(TAG, "Sensor has woken up!");
} else if (byte == TIMEOUT) {
ESP_LOGE(TAG, "Timed out waiting for sensor wake-up");
} else {
ESP_LOGE(TAG, "Received wrong byte from the sensor during wake-up: 0x%.2X", byte);
}
/* Next step, we must authenticate with the password. We cannot call check_password_ here
* neither use data_ to store the command because it might be already in use by the caller
* of send_command_()
*/
std::vector<uint8_t> buffer = {VERIFY_PASSWORD, (uint8_t) (this->password_ >> 24), (uint8_t) (this->password_ >> 16),
(uint8_t) (this->password_ >> 8), (uint8_t) (this->password_ & 0xFF)};
if (this->transfer_(&buffer) != OK) {
ESP_LOGE(TAG, "Wrong password");
}
}
void FingerprintGrowComponent::sensor_sleep_() {
// Immediately return if the power pin feature is not implemented
if (!this->has_power_pin_)
return;
this->sensor_power_pin_->digital_write(false);
this->is_sensor_awake_ = false;
ESP_LOGD(TAG, "Fingerprint sensor is now in sleep mode.");
}
void FingerprintGrowComponent::dump_config() {
ESP_LOGCONFIG(TAG, "GROW_FINGERPRINT_READER:");
ESP_LOGCONFIG(TAG, " System Identifier Code: 0x%.4X", this->system_identifier_code_);
ESP_LOGCONFIG(TAG, " Touch Sensing Pin: %s",
this->has_sensing_pin_ ? this->sensing_pin_->dump_summary().c_str() : "None");
ESP_LOGCONFIG(TAG, " Sensor Power Pin: %s",
this->has_power_pin_ ? this->sensor_power_pin_->dump_summary().c_str() : "None");
if (this->idle_period_to_sleep_ms_ < UINT32_MAX) {
ESP_LOGCONFIG(TAG, " Idle Period to Sleep: %u ms", this->idle_period_to_sleep_ms_);
} else {
ESP_LOGCONFIG(TAG, " Idle Period to Sleep: Never");
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Fingerprint Count", this->fingerprint_count_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint16_t) this->fingerprint_count_sensor_->get_state());
LOG_SENSOR(" ", "Status", this->status_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint8_t) this->status_sensor_->get_state());
LOG_SENSOR(" ", "Capacity", this->capacity_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint16_t) this->capacity_sensor_->get_state());
LOG_SENSOR(" ", "Security Level", this->security_level_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint8_t) this->security_level_sensor_->get_state());
LOG_SENSOR(" ", "Last Finger ID", this->last_finger_id_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint32_t) this->last_finger_id_sensor_->get_state());
LOG_SENSOR(" ", "Last Confidence", this->last_confidence_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint32_t) this->last_confidence_sensor_->get_state());
if (this->fingerprint_count_sensor_) {
LOG_SENSOR(" ", "Fingerprint Count", this->fingerprint_count_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint16_t) this->fingerprint_count_sensor_->get_state());
}
if (this->status_sensor_) {
LOG_SENSOR(" ", "Status", this->status_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint8_t) this->status_sensor_->get_state());
}
if (this->capacity_sensor_) {
LOG_SENSOR(" ", "Capacity", this->capacity_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint16_t) this->capacity_sensor_->get_state());
}
if (this->security_level_sensor_) {
LOG_SENSOR(" ", "Security Level", this->security_level_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint8_t) this->security_level_sensor_->get_state());
}
if (this->last_finger_id_sensor_) {
LOG_SENSOR(" ", "Last Finger ID", this->last_finger_id_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint32_t) this->last_finger_id_sensor_->get_state());
}
if (this->last_confidence_sensor_) {
LOG_SENSOR(" ", "Last Confidence", this->last_confidence_sensor_);
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint32_t) this->last_confidence_sensor_->get_state());
}
}
} // namespace fingerprint_grow

View file

@ -15,6 +15,11 @@ static const uint16_t START_CODE = 0xEF01;
static const uint16_t ENROLLMENT_SLOT_UNUSED = 0xFFFF;
// The datasheet says a max wake up time of of 200ms.
static const uint8_t WAIT_FOR_WAKE_UP_MS = 200;
static const uint32_t DEFAULT_IDLE_PERIOD_TO_SLEEP_MS = 5000;
enum GrowPacketType {
COMMAND = 0x01,
DATA = 0x02,
@ -63,6 +68,7 @@ enum GrowResponse {
INVALID_IMAGE = 0x15,
FLASH_ERR = 0x18,
INVALID_REG = 0x1A,
HANDSHAKE_SIGN = 0x55,
BAD_PACKET = 0xFE,
TIMEOUT = 0xFF,
};
@ -99,8 +105,10 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
this->address_[3] = (uint8_t) (address & 0xFF);
}
void set_sensing_pin(GPIOPin *sensing_pin) { this->sensing_pin_ = sensing_pin; }
void set_sensor_power_pin(GPIOPin *sensor_power_pin) { this->sensor_power_pin_ = sensor_power_pin; }
void set_password(uint32_t password) { this->password_ = password; }
void set_new_password(uint32_t new_password) { this->new_password_ = new_password; }
void set_idle_period_to_sleep_ms(uint32_t period_ms) { this->idle_period_to_sleep_ms_ = period_ms; }
void set_fingerprint_count_sensor(sensor::Sensor *fingerprint_count_sensor) {
this->fingerprint_count_sensor_ = fingerprint_count_sensor;
}
@ -160,7 +168,10 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
bool set_password_();
bool get_parameters_();
void get_fingerprint_count_();
uint8_t transfer_(std::vector<uint8_t> *p_data_buffer);
uint8_t send_command_();
void sensor_wakeup_();
void sensor_sleep_();
std::vector<uint8_t> data_ = {};
uint8_t address_[4] = {0xFF, 0xFF, 0xFF, 0xFF};
@ -168,14 +179,19 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
uint32_t password_ = 0x0;
uint32_t new_password_ = -1;
GPIOPin *sensing_pin_{nullptr};
GPIOPin *sensor_power_pin_{nullptr};
uint8_t enrollment_image_ = 0;
uint16_t enrollment_slot_ = ENROLLMENT_SLOT_UNUSED;
uint8_t enrollment_buffers_ = 5;
bool waiting_removal_ = false;
bool has_sensing_pin_ = false;
bool has_power_pin_ = false;
bool is_sensor_awake_ = false;
uint32_t last_transfer_ms_ = 0;
uint32_t last_aura_led_control_ = 0;
uint16_t last_aura_led_duration_ = 0;
uint16_t system_identifier_code_ = 0;
uint32_t idle_period_to_sleep_ms_ = UINT32_MAX;
sensor::Sensor *fingerprint_count_sensor_{nullptr};
sensor::Sensor *status_sensor_{nullptr};
sensor::Sensor *capacity_sensor_{nullptr};

View file

@ -1299,6 +1299,11 @@ fingerprint_grow:
sensing_pin:
allow_other_uses: true
number: 4
sensor_power_pin:
allow_other_uses: true
number: 5
inverted: true
idle_period_to_sleep: 5s
password: 0x12FE37DC
new_password: 0xA65B9840
on_finger_scan_start: