ESPHome 2025.9.0-dev
Loading...
Searching...
No Matches
fingerprint_grow.cpp
Go to the documentation of this file.
1#include "fingerprint_grow.h"
2#include "esphome/core/log.h"
3#include <cinttypes>
4
5namespace esphome {
6namespace fingerprint_grow {
7
8static const char *const TAG = "fingerprint_grow";
9
10// Based on Adafruit's library: https://github.com/adafruit/Adafruit-Fingerprint-Sensor-Library
11
13 if (this->enrollment_image_ > this->enrollment_buffers_) {
15 return;
16 }
17
18 if (this->has_sensing_pin_) {
19 // A finger touch results in a low level (digital_read() == false)
20 if (this->sensing_pin_->digital_read()) {
21 ESP_LOGV(TAG, "No touch sensing");
22 this->waiting_removal_ = false;
23 if ((this->enrollment_image_ == 0) && // Not in enrolment process
25 this->sensor_sleep_();
26 }
27 return;
28 } else if (!this->waiting_removal_) {
29 this->finger_scan_start_callback_.call();
30 }
31 }
32
33 if (this->waiting_removal_) {
34 if ((!this->has_sensing_pin_) && (this->scan_image_(1) == NO_FINGER)) {
35 ESP_LOGD(TAG, "Finger removed");
36 this->waiting_removal_ = false;
37 }
38 return;
39 }
40
41 if (this->enrollment_image_ == 0) {
42 this->scan_and_match_();
43 return;
44 }
45
46 uint8_t result = this->scan_image_(this->enrollment_image_);
47 if (result == NO_FINGER) {
48 return;
49 }
50 this->waiting_removal_ = true;
51 if (result != OK) {
52 this->finish_enrollment(result);
53 return;
54 }
56 ++this->enrollment_image_;
57}
58
60 this->has_sensing_pin_ = (this->sensing_pin_ != nullptr);
61 this->has_power_pin_ = (this->sensor_power_pin_ != nullptr);
62
63 // Call pins setup, so we effectively apply the config generated from the yaml file.
64 if (this->has_sensing_pin_) {
65 this->sensing_pin_->setup();
66 }
67 if (this->has_power_pin_) {
68 // Starts with output low (disabling power) to avoid glitches in the sensor
69 this->sensor_power_pin_->digital_write(false);
70 this->sensor_power_pin_->setup();
71
72 // If the user didn't specify an idle period to sleep, applies the default.
73 if (this->idle_period_to_sleep_ms_ == UINT32_MAX) {
74 this->idle_period_to_sleep_ms_ = DEFAULT_IDLE_PERIOD_TO_SLEEP_MS;
75 }
76 }
77
78 // Place the sensor in a known (sleep/off) state and sync internal var state.
79 this->sensor_sleep_();
80 delay(20); // This delay guarantees the sensor will in fact be powered power.
81
82 if (this->check_password_()) {
83 if (this->new_password_ != -1) {
84 if (this->set_password_())
85 return;
86 } else {
87 if (this->get_parameters_())
88 return;
89 }
90 }
91 this->mark_failed();
92}
93
94void FingerprintGrowComponent::enroll_fingerprint(uint16_t finger_id, uint8_t num_buffers) {
95 ESP_LOGI(TAG, "Starting enrollment in slot %d", finger_id);
96 if (this->enrolling_binary_sensor_ != nullptr) {
98 }
99 this->enrollment_slot_ = finger_id;
100 this->enrollment_buffers_ = num_buffers;
101 this->enrollment_image_ = 1;
102}
103
105 if (result == OK) {
108 } else {
109 if (this->enrollment_slot_ != ENROLLMENT_SLOT_UNUSED) {
111 }
112 }
113 this->enrollment_image_ = 0;
114 this->enrollment_slot_ = ENROLLMENT_SLOT_UNUSED;
115 if (this->enrolling_binary_sensor_ != nullptr) {
117 }
118 ESP_LOGI(TAG, "Finished enrollment");
119}
120
122 if (this->has_sensing_pin_) {
123 ESP_LOGD(TAG, "Scan and match");
124 } else {
125 ESP_LOGV(TAG, "Scan and match");
126 }
127 if (this->scan_image_(1) == OK) {
128 this->waiting_removal_ = true;
129 this->data_ = {SEARCH, 0x01, 0x00, 0x00, (uint8_t) (this->capacity_ >> 8), (uint8_t) (this->capacity_ & 0xFF)};
130 switch (this->send_command_()) {
131 case OK: {
132 ESP_LOGD(TAG, "Fingerprint matched");
133 uint16_t finger_id = ((uint16_t) this->data_[1] << 8) | this->data_[2];
134 uint16_t confidence = ((uint16_t) this->data_[3] << 8) | this->data_[4];
135 if (this->last_finger_id_sensor_ != nullptr) {
136 this->last_finger_id_sensor_->publish_state(finger_id);
137 }
138 if (this->last_confidence_sensor_ != nullptr) {
139 this->last_confidence_sensor_->publish_state(confidence);
140 }
141 this->finger_scan_matched_callback_.call(finger_id, confidence);
142 break;
143 }
144 case NOT_FOUND:
145 ESP_LOGD(TAG, "Fingerprint not matched to any saved slots");
147 break;
148 }
149 }
150}
151
153 if (this->has_sensing_pin_) {
154 ESP_LOGD(TAG, "Getting image %d", buffer);
155 } else {
156 ESP_LOGV(TAG, "Getting image %d", buffer);
157 }
158 this->data_ = {GET_IMAGE};
159 uint8_t send_result = this->send_command_();
160 switch (send_result) {
161 case OK:
162 break;
163 case NO_FINGER:
164 if (this->has_sensing_pin_) {
165 this->waiting_removal_ = true;
166 ESP_LOGD(TAG, "Finger Misplaced");
168 } else {
169 ESP_LOGV(TAG, "No finger");
170 }
171 return send_result;
172 case IMAGE_FAIL:
173 ESP_LOGE(TAG, "Imaging error");
175 return send_result;
176 default:
177 ESP_LOGD(TAG, "Unknown Scan Error: %d", send_result);
178 return send_result;
179 }
180
181 ESP_LOGD(TAG, "Processing image %d", buffer);
182 this->data_ = {IMAGE_2_TZ, buffer};
183 send_result = this->send_command_();
184 switch (send_result) {
185 case OK:
186 ESP_LOGI(TAG, "Processed image %d", buffer);
187 break;
188 case IMAGE_MESS:
189 ESP_LOGE(TAG, "Image too messy");
191 break;
192 case FEATURE_FAIL:
193 case INVALID_IMAGE:
194 ESP_LOGE(TAG, "Could not find fingerprint features");
196 break;
197 }
198 return send_result;
199}
200
202 ESP_LOGI(TAG, "Creating model");
203 this->data_ = {REG_MODEL};
204 switch (this->send_command_()) {
205 case OK:
206 break;
207 case ENROLL_MISMATCH:
208 ESP_LOGE(TAG, "Scans do not match");
209 default:
210 return this->data_[0];
211 }
212
213 ESP_LOGI(TAG, "Storing model");
214 this->data_ = {STORE, 0x01, (uint8_t) (this->enrollment_slot_ >> 8), (uint8_t) (this->enrollment_slot_ & 0xFF)};
215 switch (this->send_command_()) {
216 case OK:
217 ESP_LOGI(TAG, "Stored model");
218 break;
219 case BAD_LOCATION:
220 ESP_LOGE(TAG, "Invalid slot");
221 break;
222 case FLASH_ERR:
223 ESP_LOGE(TAG, "Error writing to flash");
224 break;
225 }
226 return this->data_[0];
227}
228
230 ESP_LOGD(TAG, "Checking password");
231 this->data_ = {VERIFY_PASSWORD, (uint8_t) (this->password_ >> 24), (uint8_t) (this->password_ >> 16),
232 (uint8_t) (this->password_ >> 8), (uint8_t) (this->password_ & 0xFF)};
233 switch (this->send_command_()) {
234 case OK:
235 ESP_LOGD(TAG, "Password verified");
236 return true;
237 case PASSWORD_FAIL:
238 ESP_LOGE(TAG, "Wrong password");
239 break;
240 }
241 return false;
242}
243
245 ESP_LOGI(TAG, "Setting new password: %" PRIu32, this->new_password_);
246 this->data_ = {SET_PASSWORD, (uint8_t) (this->new_password_ >> 24), (uint8_t) (this->new_password_ >> 16),
247 (uint8_t) (this->new_password_ >> 8), (uint8_t) (this->new_password_ & 0xFF)};
248 if (this->send_command_() == OK) {
249 ESP_LOGI(TAG, "New password successfully set");
250 ESP_LOGI(TAG, "Define the new password in your configuration and reflash now");
251 ESP_LOGW(TAG, "!!!Forgetting the password will render your device unusable!!!");
252 return true;
253 }
254 return false;
255}
256
258 ESP_LOGD(TAG, "Getting parameters");
259 this->data_ = {READ_SYS_PARAM};
260 if (this->send_command_() == OK) {
261 ESP_LOGD(TAG, "Got parameters"); // Bear in mind data_[0] is the transfer status,
262 if (this->status_sensor_ != nullptr) { // the parameters table start at data_[1]
263 this->status_sensor_->publish_state(((uint16_t) this->data_[1] << 8) | this->data_[2]);
264 }
265 this->system_identifier_code_ = ((uint16_t) this->data_[3] << 8) | this->data_[4];
266 this->capacity_ = ((uint16_t) this->data_[5] << 8) | this->data_[6];
267 if (this->capacity_sensor_ != nullptr) {
269 }
270 if (this->security_level_sensor_ != nullptr) {
271 this->security_level_sensor_->publish_state(((uint16_t) this->data_[7] << 8) | this->data_[8]);
272 }
273 if (this->enrolling_binary_sensor_ != nullptr) {
275 }
277 return true;
278 }
279 return false;
280}
281
283 ESP_LOGD(TAG, "Getting fingerprint count");
284 this->data_ = {TEMPLATE_COUNT};
285 if (this->send_command_() == OK) {
286 ESP_LOGD(TAG, "Got fingerprint count");
287 if (this->fingerprint_count_sensor_ != nullptr)
288 this->fingerprint_count_sensor_->publish_state(((uint16_t) this->data_[1] << 8) | this->data_[2]);
289 }
290}
291
293 ESP_LOGI(TAG, "Deleting fingerprint in slot %d", finger_id);
294 this->data_ = {DELETE, (uint8_t) (finger_id >> 8), (uint8_t) (finger_id & 0xFF), 0x00, 0x01};
295 switch (this->send_command_()) {
296 case OK:
297 ESP_LOGI(TAG, "Deleted fingerprint");
299 break;
300 case DELETE_FAIL:
301 ESP_LOGE(TAG, "Reader failed to delete fingerprint");
302 break;
303 }
304}
305
307 ESP_LOGI(TAG, "Deleting all stored fingerprints");
308 this->data_ = {DELETE_ALL};
309 switch (this->send_command_()) {
310 case OK:
311 ESP_LOGI(TAG, "Deleted all fingerprints");
313 break;
314 case DB_CLEAR_FAIL:
315 ESP_LOGE(TAG, "Reader failed to clear fingerprint library");
316 break;
317 }
318}
319
321 ESP_LOGD(TAG, "Setting LED");
322 if (state) {
323 this->data_ = {LED_ON};
324 } else {
325 this->data_ = {LED_OFF};
326 }
327 switch (this->send_command_()) {
328 case OK:
329 ESP_LOGD(TAG, "LED set");
330 break;
331 case PACKET_RCV_ERR:
332 case TIMEOUT:
333 break;
334 default:
335 ESP_LOGE(TAG, "Try aura_led_control instead");
336 break;
337 }
338}
339
340void FingerprintGrowComponent::aura_led_control(uint8_t state, uint8_t speed, uint8_t color, uint8_t count) {
341 const uint32_t now = millis();
342 const uint32_t elapsed = now - this->last_aura_led_control_;
343 if (elapsed < this->last_aura_led_duration_) {
344 delay(this->last_aura_led_duration_ - elapsed);
345 }
346 ESP_LOGD(TAG, "Setting Aura LED");
347 this->data_ = {AURA_CONFIG, state, speed, color, count};
348 switch (this->send_command_()) {
349 case OK:
350 ESP_LOGD(TAG, "Aura LED set");
351 this->last_aura_led_control_ = millis();
352 this->last_aura_led_duration_ = 10 * speed * count;
353 break;
354 case PACKET_RCV_ERR:
355 case TIMEOUT:
356 break;
357 default:
358 ESP_LOGE(TAG, "Try led_control instead");
359 break;
360 }
361}
362
363uint8_t FingerprintGrowComponent::transfer_(std::vector<uint8_t> *p_data_buffer) {
364 while (this->available())
365 this->read();
366 this->write((uint8_t) (START_CODE >> 8));
367 this->write((uint8_t) (START_CODE & 0xFF));
368 this->write(this->address_[0]);
369 this->write(this->address_[1]);
370 this->write(this->address_[2]);
371 this->write(this->address_[3]);
372 this->write(COMMAND);
373
374 uint16_t wire_length = p_data_buffer->size() + 2;
375 this->write((uint8_t) (wire_length >> 8));
376 this->write((uint8_t) (wire_length & 0xFF));
377
378 uint16_t sum = (wire_length >> 8) + (wire_length & 0xFF) + COMMAND;
379 for (auto data : *p_data_buffer) {
380 this->write(data);
381 sum += data;
382 }
383
384 this->write((uint8_t) (sum >> 8));
385 this->write((uint8_t) (sum & 0xFF));
386
387 p_data_buffer->clear();
388
389 uint8_t byte;
390 uint16_t idx = 0, length = 0;
391
392 for (uint16_t timer = 0; timer < 1000; timer++) {
393 if (this->available() == 0) {
394 delay(1);
395 continue;
396 }
397
398 byte = this->read();
399
400 switch (idx) {
401 case 0:
402 if (byte != (uint8_t) (START_CODE >> 8))
403 continue;
404 break;
405 case 1:
406 if (byte != (uint8_t) (START_CODE & 0xFF)) {
407 idx = 0;
408 continue;
409 }
410 break;
411 case 2:
412 case 3:
413 case 4:
414 case 5:
415 if (byte != this->address_[idx - 2]) {
416 idx = 0;
417 continue;
418 }
419 break;
420 case 6:
421 if (byte != ACK) {
422 idx = 0;
423 continue;
424 }
425 break;
426 case 7:
427 length = (uint16_t) byte << 8;
428 break;
429 case 8:
430 length |= byte;
431 break;
432 default:
433 p_data_buffer->push_back(byte);
434 if ((idx - 8) == length) {
435 switch ((*p_data_buffer)[0]) {
436 case OK:
437 case NO_FINGER:
438 case IMAGE_FAIL:
439 case IMAGE_MESS:
440 case FEATURE_FAIL:
441 case NO_MATCH:
442 case NOT_FOUND:
443 case ENROLL_MISMATCH:
444 case BAD_LOCATION:
445 case DELETE_FAIL:
446 case DB_CLEAR_FAIL:
447 case PASSWORD_FAIL:
448 case INVALID_IMAGE:
449 case FLASH_ERR:
450 break;
451 case PACKET_RCV_ERR:
452 ESP_LOGE(TAG, "Reader failed to process request");
453 break;
454 default:
455 ESP_LOGE(TAG, "Unknown response received from reader: 0x%.2X", (*p_data_buffer)[0]);
456 break;
457 }
458 this->last_transfer_ms_ = millis();
459 return (*p_data_buffer)[0];
460 }
461 break;
462 }
463 idx++;
464 }
465 ESP_LOGE(TAG, "No response received from reader");
466 (*p_data_buffer)[0] = TIMEOUT;
467 this->last_transfer_ms_ = millis();
468 return TIMEOUT;
469}
470
472 this->sensor_wakeup_();
473 return this->transfer_(&this->data_);
474}
475
477 // Immediately return if there is no power pin or the sensor is already on
478 if ((!this->has_power_pin_) || (this->is_sensor_awake_))
479 return;
480
481 this->sensor_power_pin_->digital_write(true);
482 this->is_sensor_awake_ = true;
483
484 uint8_t byte = TIMEOUT;
485
486 // Wait for the byte HANDSHAKE_SIGN from the sensor meaning it is operational.
487 for (uint16_t timer = 0; timer < WAIT_FOR_WAKE_UP_MS; timer++) {
488 if (this->available() > 0) {
489 byte = this->read();
490
491 /* If the received byte is zero, the UART probably misinterpreted a raising edge on
492 * the RX pin due the power up as byte "zero" - I verified this behaviour using
493 * the esp32-arduino lib. So here we just ignore this fake byte.
494 */
495 if (byte != 0)
496 break;
497 }
498 delay(1);
499 }
500
501 /* Lets check if the received by is a HANDSHAKE_SIGN, otherwise log an error
502 * message and try to continue on the best effort.
503 */
504 if (byte == HANDSHAKE_SIGN) {
505 ESP_LOGD(TAG, "Sensor has woken up!");
506 } else if (byte == TIMEOUT) {
507 ESP_LOGE(TAG, "Timed out waiting for sensor wake-up");
508 } else {
509 ESP_LOGE(TAG, "Received wrong byte from the sensor during wake-up: 0x%.2X", byte);
510 }
511
512 /* Next step, we must authenticate with the password. We cannot call check_password_ here
513 * neither use data_ to store the command because it might be already in use by the caller
514 * of send_command_()
515 */
516 std::vector<uint8_t> buffer = {VERIFY_PASSWORD, (uint8_t) (this->password_ >> 24), (uint8_t) (this->password_ >> 16),
517 (uint8_t) (this->password_ >> 8), (uint8_t) (this->password_ & 0xFF)};
518
519 if (this->transfer_(&buffer) != OK) {
520 ESP_LOGE(TAG, "Wrong password");
521 }
522}
523
525 // Immediately return if the power pin feature is not implemented
526 if (!this->has_power_pin_)
527 return;
528
529 this->sensor_power_pin_->digital_write(false);
530 this->is_sensor_awake_ = false;
531 ESP_LOGD(TAG, "Fingerprint sensor is now in sleep mode.");
532}
533
535 ESP_LOGCONFIG(TAG,
536 "GROW_FINGERPRINT_READER:\n"
537 " System Identifier Code: 0x%.4X\n"
538 " Touch Sensing Pin: %s\n"
539 " Sensor Power Pin: %s",
541 this->has_sensing_pin_ ? this->sensing_pin_->dump_summary().c_str() : "None",
542 this->has_power_pin_ ? this->sensor_power_pin_->dump_summary().c_str() : "None");
543 if (this->idle_period_to_sleep_ms_ < UINT32_MAX) {
544 ESP_LOGCONFIG(TAG, " Idle Period to Sleep: %" PRIu32 " ms", this->idle_period_to_sleep_ms_);
545 } else {
546 ESP_LOGCONFIG(TAG, " Idle Period to Sleep: Never");
547 }
548 LOG_UPDATE_INTERVAL(this);
549 if (this->fingerprint_count_sensor_) {
550 LOG_SENSOR(" ", "Fingerprint Count", this->fingerprint_count_sensor_);
551 ESP_LOGCONFIG(TAG, " Current Value: %u", (uint16_t) this->fingerprint_count_sensor_->get_state());
552 }
553 if (this->status_sensor_) {
554 LOG_SENSOR(" ", "Status", this->status_sensor_);
555 ESP_LOGCONFIG(TAG, " Current Value: %u", (uint8_t) this->status_sensor_->get_state());
556 }
557 if (this->capacity_sensor_) {
558 LOG_SENSOR(" ", "Capacity", this->capacity_sensor_);
559 ESP_LOGCONFIG(TAG, " Current Value: %u", (uint16_t) this->capacity_sensor_->get_state());
560 }
561 if (this->security_level_sensor_) {
562 LOG_SENSOR(" ", "Security Level", this->security_level_sensor_);
563 ESP_LOGCONFIG(TAG, " Current Value: %u", (uint8_t) this->security_level_sensor_->get_state());
564 }
565 if (this->last_finger_id_sensor_) {
566 LOG_SENSOR(" ", "Last Finger ID", this->last_finger_id_sensor_);
567 ESP_LOGCONFIG(TAG, " Current Value: %" PRIu32, (uint32_t) this->last_finger_id_sensor_->get_state());
568 }
569 if (this->last_confidence_sensor_) {
570 LOG_SENSOR(" ", "Last Confidence", this->last_confidence_sensor_);
571 ESP_LOGCONFIG(TAG, " Current Value: %" PRIu32, (uint32_t) this->last_confidence_sensor_->get_state());
572 }
573}
574
575} // namespace fingerprint_grow
576} // namespace esphome
virtual void mark_failed()
Mark this component as failed.
virtual void setup()=0
virtual std::string dump_summary() const =0
virtual void digital_write(bool value)=0
virtual bool digital_read()=0
void publish_state(bool new_state)
Publish a new state to the front-end.
CallbackManager< void(uint16_t, uint16_t)> finger_scan_matched_callback_
void enroll_fingerprint(uint16_t finger_id, uint8_t num_buffers)
uint8_t transfer_(std::vector< uint8_t > *p_data_buffer)
CallbackManager< void(uint16_t)> enrollment_done_callback_
CallbackManager< void(uint16_t)> enrollment_failed_callback_
void aura_led_control(uint8_t state, uint8_t speed, uint8_t color, uint8_t count)
CallbackManager< void(uint8_t, uint16_t)> enrollment_scan_callback_
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:45
float get_state() const
Getter-syntax for .state.
Definition sensor.cpp:97
size_t write(uint8_t data)
Definition uart.h:52
int speed
Definition fan.h:1
bool state
Definition fan.h:0
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:29
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28
uint16_t length
Definition tt21100.cpp:0