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