ESPHome 2025.5.0
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 ESP_LOGCONFIG(TAG, "Setting up Grow Fingerprint Reader...");
61
62 this->has_sensing_pin_ = (this->sensing_pin_ != nullptr);
63 this->has_power_pin_ = (this->sensor_power_pin_ != nullptr);
64
65 // Call pins setup, so we effectively apply the config generated from the yaml file.
66 if (this->has_sensing_pin_) {
67 this->sensing_pin_->setup();
68 }
69 if (this->has_power_pin_) {
70 // Starts with output low (disabling power) to avoid glitches in the sensor
71 this->sensor_power_pin_->digital_write(false);
72 this->sensor_power_pin_->setup();
73
74 // If the user didn't specify an idle period to sleep, applies the default.
75 if (this->idle_period_to_sleep_ms_ == UINT32_MAX) {
76 this->idle_period_to_sleep_ms_ = DEFAULT_IDLE_PERIOD_TO_SLEEP_MS;
77 }
78 }
79
80 // Place the sensor in a known (sleep/off) state and sync internal var state.
81 this->sensor_sleep_();
82 delay(20); // This delay guarantees the sensor will in fact be powered power.
83
84 if (this->check_password_()) {
85 if (this->new_password_ != -1) {
86 if (this->set_password_())
87 return;
88 } else {
89 if (this->get_parameters_())
90 return;
91 }
92 }
93 this->mark_failed();
94}
95
96void FingerprintGrowComponent::enroll_fingerprint(uint16_t finger_id, uint8_t num_buffers) {
97 ESP_LOGI(TAG, "Starting enrollment in slot %d", finger_id);
98 if (this->enrolling_binary_sensor_ != nullptr) {
100 }
101 this->enrollment_slot_ = finger_id;
102 this->enrollment_buffers_ = num_buffers;
103 this->enrollment_image_ = 1;
104}
105
107 if (result == OK) {
110 } else {
111 if (this->enrollment_slot_ != ENROLLMENT_SLOT_UNUSED) {
113 }
114 }
115 this->enrollment_image_ = 0;
116 this->enrollment_slot_ = ENROLLMENT_SLOT_UNUSED;
117 if (this->enrolling_binary_sensor_ != nullptr) {
119 }
120 ESP_LOGI(TAG, "Finished enrollment");
121}
122
124 if (this->has_sensing_pin_) {
125 ESP_LOGD(TAG, "Scan and match");
126 } else {
127 ESP_LOGV(TAG, "Scan and match");
128 }
129 if (this->scan_image_(1) == OK) {
130 this->waiting_removal_ = true;
131 this->data_ = {SEARCH, 0x01, 0x00, 0x00, (uint8_t) (this->capacity_ >> 8), (uint8_t) (this->capacity_ & 0xFF)};
132 switch (this->send_command_()) {
133 case OK: {
134 ESP_LOGD(TAG, "Fingerprint matched");
135 uint16_t finger_id = ((uint16_t) this->data_[1] << 8) | this->data_[2];
136 uint16_t confidence = ((uint16_t) this->data_[3] << 8) | this->data_[4];
137 if (this->last_finger_id_sensor_ != nullptr) {
138 this->last_finger_id_sensor_->publish_state(finger_id);
139 }
140 if (this->last_confidence_sensor_ != nullptr) {
141 this->last_confidence_sensor_->publish_state(confidence);
142 }
143 this->finger_scan_matched_callback_.call(finger_id, confidence);
144 break;
145 }
146 case NOT_FOUND:
147 ESP_LOGD(TAG, "Fingerprint not matched to any saved slots");
149 break;
150 }
151 }
152}
153
155 if (this->has_sensing_pin_) {
156 ESP_LOGD(TAG, "Getting image %d", buffer);
157 } else {
158 ESP_LOGV(TAG, "Getting image %d", buffer);
159 }
160 this->data_ = {GET_IMAGE};
161 uint8_t send_result = this->send_command_();
162 switch (send_result) {
163 case OK:
164 break;
165 case NO_FINGER:
166 if (this->has_sensing_pin_) {
167 this->waiting_removal_ = true;
168 ESP_LOGD(TAG, "Finger Misplaced");
170 } else {
171 ESP_LOGV(TAG, "No finger");
172 }
173 return send_result;
174 case IMAGE_FAIL:
175 ESP_LOGE(TAG, "Imaging error");
177 return send_result;
178 default:
179 ESP_LOGD(TAG, "Unknown Scan Error: %d", send_result);
180 return send_result;
181 }
182
183 ESP_LOGD(TAG, "Processing image %d", buffer);
184 this->data_ = {IMAGE_2_TZ, buffer};
185 send_result = this->send_command_();
186 switch (send_result) {
187 case OK:
188 ESP_LOGI(TAG, "Processed image %d", buffer);
189 break;
190 case IMAGE_MESS:
191 ESP_LOGE(TAG, "Image too messy");
193 break;
194 case FEATURE_FAIL:
195 case INVALID_IMAGE:
196 ESP_LOGE(TAG, "Could not find fingerprint features");
198 break;
199 }
200 return send_result;
201}
202
204 ESP_LOGI(TAG, "Creating model");
205 this->data_ = {REG_MODEL};
206 switch (this->send_command_()) {
207 case OK:
208 break;
209 case ENROLL_MISMATCH:
210 ESP_LOGE(TAG, "Scans do not match");
211 default:
212 return this->data_[0];
213 }
214
215 ESP_LOGI(TAG, "Storing model");
216 this->data_ = {STORE, 0x01, (uint8_t) (this->enrollment_slot_ >> 8), (uint8_t) (this->enrollment_slot_ & 0xFF)};
217 switch (this->send_command_()) {
218 case OK:
219 ESP_LOGI(TAG, "Stored model");
220 break;
221 case BAD_LOCATION:
222 ESP_LOGE(TAG, "Invalid slot");
223 break;
224 case FLASH_ERR:
225 ESP_LOGE(TAG, "Error writing to flash");
226 break;
227 }
228 return this->data_[0];
229}
230
232 ESP_LOGD(TAG, "Checking password");
233 this->data_ = {VERIFY_PASSWORD, (uint8_t) (this->password_ >> 24), (uint8_t) (this->password_ >> 16),
234 (uint8_t) (this->password_ >> 8), (uint8_t) (this->password_ & 0xFF)};
235 switch (this->send_command_()) {
236 case OK:
237 ESP_LOGD(TAG, "Password verified");
238 return true;
239 case PASSWORD_FAIL:
240 ESP_LOGE(TAG, "Wrong password");
241 break;
242 }
243 return false;
244}
245
247 ESP_LOGI(TAG, "Setting new password: %" PRIu32, this->new_password_);
248 this->data_ = {SET_PASSWORD, (uint8_t) (this->new_password_ >> 24), (uint8_t) (this->new_password_ >> 16),
249 (uint8_t) (this->new_password_ >> 8), (uint8_t) (this->new_password_ & 0xFF)};
250 if (this->send_command_() == OK) {
251 ESP_LOGI(TAG, "New password successfully set");
252 ESP_LOGI(TAG, "Define the new password in your configuration and reflash now");
253 ESP_LOGW(TAG, "!!!Forgetting the password will render your device unusable!!!");
254 return true;
255 }
256 return false;
257}
258
260 ESP_LOGD(TAG, "Getting parameters");
261 this->data_ = {READ_SYS_PARAM};
262 if (this->send_command_() == OK) {
263 ESP_LOGD(TAG, "Got parameters"); // Bear in mind data_[0] is the transfer status,
264 if (this->status_sensor_ != nullptr) { // the parameters table start at data_[1]
265 this->status_sensor_->publish_state(((uint16_t) this->data_[1] << 8) | this->data_[2]);
266 }
267 this->system_identifier_code_ = ((uint16_t) this->data_[3] << 8) | this->data_[4];
268 this->capacity_ = ((uint16_t) this->data_[5] << 8) | this->data_[6];
269 if (this->capacity_sensor_ != nullptr) {
271 }
272 if (this->security_level_sensor_ != nullptr) {
273 this->security_level_sensor_->publish_state(((uint16_t) this->data_[7] << 8) | this->data_[8]);
274 }
275 if (this->enrolling_binary_sensor_ != nullptr) {
277 }
279 return true;
280 }
281 return false;
282}
283
285 ESP_LOGD(TAG, "Getting fingerprint count");
286 this->data_ = {TEMPLATE_COUNT};
287 if (this->send_command_() == OK) {
288 ESP_LOGD(TAG, "Got fingerprint count");
289 if (this->fingerprint_count_sensor_ != nullptr)
290 this->fingerprint_count_sensor_->publish_state(((uint16_t) this->data_[1] << 8) | this->data_[2]);
291 }
292}
293
295 ESP_LOGI(TAG, "Deleting fingerprint in slot %d", finger_id);
296 this->data_ = {DELETE, (uint8_t) (finger_id >> 8), (uint8_t) (finger_id & 0xFF), 0x00, 0x01};
297 switch (this->send_command_()) {
298 case OK:
299 ESP_LOGI(TAG, "Deleted fingerprint");
301 break;
302 case DELETE_FAIL:
303 ESP_LOGE(TAG, "Reader failed to delete fingerprint");
304 break;
305 }
306}
307
309 ESP_LOGI(TAG, "Deleting all stored fingerprints");
310 this->data_ = {DELETE_ALL};
311 switch (this->send_command_()) {
312 case OK:
313 ESP_LOGI(TAG, "Deleted all fingerprints");
315 break;
316 case DB_CLEAR_FAIL:
317 ESP_LOGE(TAG, "Reader failed to clear fingerprint library");
318 break;
319 }
320}
321
323 ESP_LOGD(TAG, "Setting LED");
324 if (state) {
325 this->data_ = {LED_ON};
326 } else {
327 this->data_ = {LED_OFF};
328 }
329 switch (this->send_command_()) {
330 case OK:
331 ESP_LOGD(TAG, "LED set");
332 break;
333 case PACKET_RCV_ERR:
334 case TIMEOUT:
335 break;
336 default:
337 ESP_LOGE(TAG, "Try aura_led_control instead");
338 break;
339 }
340}
341
342void FingerprintGrowComponent::aura_led_control(uint8_t state, uint8_t speed, uint8_t color, uint8_t count) {
343 const uint32_t now = millis();
344 const uint32_t elapsed = now - this->last_aura_led_control_;
345 if (elapsed < this->last_aura_led_duration_) {
346 delay(this->last_aura_led_duration_ - elapsed);
347 }
348 ESP_LOGD(TAG, "Setting Aura LED");
349 this->data_ = {AURA_CONFIG, state, speed, color, count};
350 switch (this->send_command_()) {
351 case OK:
352 ESP_LOGD(TAG, "Aura LED set");
353 this->last_aura_led_control_ = millis();
354 this->last_aura_led_duration_ = 10 * speed * count;
355 break;
356 case PACKET_RCV_ERR:
357 case TIMEOUT:
358 break;
359 default:
360 ESP_LOGE(TAG, "Try led_control instead");
361 break;
362 }
363}
364
365uint8_t FingerprintGrowComponent::transfer_(std::vector<uint8_t> *p_data_buffer) {
366 while (this->available())
367 this->read();
368 this->write((uint8_t) (START_CODE >> 8));
369 this->write((uint8_t) (START_CODE & 0xFF));
370 this->write(this->address_[0]);
371 this->write(this->address_[1]);
372 this->write(this->address_[2]);
373 this->write(this->address_[3]);
374 this->write(COMMAND);
375
376 uint16_t wire_length = p_data_buffer->size() + 2;
377 this->write((uint8_t) (wire_length >> 8));
378 this->write((uint8_t) (wire_length & 0xFF));
379
380 uint16_t sum = (wire_length >> 8) + (wire_length & 0xFF) + COMMAND;
381 for (auto data : *p_data_buffer) {
382 this->write(data);
383 sum += data;
384 }
385
386 this->write((uint8_t) (sum >> 8));
387 this->write((uint8_t) (sum & 0xFF));
388
389 p_data_buffer->clear();
390
391 uint8_t byte;
392 uint16_t idx = 0, length = 0;
393
394 for (uint16_t timer = 0; timer < 1000; timer++) {
395 if (this->available() == 0) {
396 delay(1);
397 continue;
398 }
399
400 byte = this->read();
401
402 switch (idx) {
403 case 0:
404 if (byte != (uint8_t) (START_CODE >> 8))
405 continue;
406 break;
407 case 1:
408 if (byte != (uint8_t) (START_CODE & 0xFF)) {
409 idx = 0;
410 continue;
411 }
412 break;
413 case 2:
414 case 3:
415 case 4:
416 case 5:
417 if (byte != this->address_[idx - 2]) {
418 idx = 0;
419 continue;
420 }
421 break;
422 case 6:
423 if (byte != ACK) {
424 idx = 0;
425 continue;
426 }
427 break;
428 case 7:
429 length = (uint16_t) byte << 8;
430 break;
431 case 8:
432 length |= byte;
433 break;
434 default:
435 p_data_buffer->push_back(byte);
436 if ((idx - 8) == length) {
437 switch ((*p_data_buffer)[0]) {
438 case OK:
439 case NO_FINGER:
440 case IMAGE_FAIL:
441 case IMAGE_MESS:
442 case FEATURE_FAIL:
443 case NO_MATCH:
444 case NOT_FOUND:
445 case ENROLL_MISMATCH:
446 case BAD_LOCATION:
447 case DELETE_FAIL:
448 case DB_CLEAR_FAIL:
449 case PASSWORD_FAIL:
450 case INVALID_IMAGE:
451 case FLASH_ERR:
452 break;
453 case PACKET_RCV_ERR:
454 ESP_LOGE(TAG, "Reader failed to process request");
455 break;
456 default:
457 ESP_LOGE(TAG, "Unknown response received from reader: 0x%.2X", (*p_data_buffer)[0]);
458 break;
459 }
460 this->last_transfer_ms_ = millis();
461 return (*p_data_buffer)[0];
462 }
463 break;
464 }
465 idx++;
466 }
467 ESP_LOGE(TAG, "No response received from reader");
468 (*p_data_buffer)[0] = TIMEOUT;
469 this->last_transfer_ms_ = millis();
470 return TIMEOUT;
471}
472
474 this->sensor_wakeup_();
475 return this->transfer_(&this->data_);
476}
477
479 // Immediately return if there is no power pin or the sensor is already on
480 if ((!this->has_power_pin_) || (this->is_sensor_awake_))
481 return;
482
483 this->sensor_power_pin_->digital_write(true);
484 this->is_sensor_awake_ = true;
485
486 uint8_t byte = TIMEOUT;
487
488 // Wait for the byte HANDSHAKE_SIGN from the sensor meaning it is operational.
489 for (uint16_t timer = 0; timer < WAIT_FOR_WAKE_UP_MS; timer++) {
490 if (this->available() > 0) {
491 byte = this->read();
492
493 /* If the received byte is zero, the UART probably misinterpreted a raising edge on
494 * the RX pin due the power up as byte "zero" - I verified this behaviour using
495 * the esp32-arduino lib. So here we just ignore this fake byte.
496 */
497 if (byte != 0)
498 break;
499 }
500 delay(1);
501 }
502
503 /* Lets check if the received by is a HANDSHAKE_SIGN, otherwise log an error
504 * message and try to continue on the best effort.
505 */
506 if (byte == HANDSHAKE_SIGN) {
507 ESP_LOGD(TAG, "Sensor has woken up!");
508 } else if (byte == TIMEOUT) {
509 ESP_LOGE(TAG, "Timed out waiting for sensor wake-up");
510 } else {
511 ESP_LOGE(TAG, "Received wrong byte from the sensor during wake-up: 0x%.2X", byte);
512 }
513
514 /* Next step, we must authenticate with the password. We cannot call check_password_ here
515 * neither use data_ to store the command because it might be already in use by the caller
516 * of send_command_()
517 */
518 std::vector<uint8_t> buffer = {VERIFY_PASSWORD, (uint8_t) (this->password_ >> 24), (uint8_t) (this->password_ >> 16),
519 (uint8_t) (this->password_ >> 8), (uint8_t) (this->password_ & 0xFF)};
520
521 if (this->transfer_(&buffer) != OK) {
522 ESP_LOGE(TAG, "Wrong password");
523 }
524}
525
527 // Immediately return if the power pin feature is not implemented
528 if (!this->has_power_pin_)
529 return;
530
531 this->sensor_power_pin_->digital_write(false);
532 this->is_sensor_awake_ = false;
533 ESP_LOGD(TAG, "Fingerprint sensor is now in sleep mode.");
534}
535
537 ESP_LOGCONFIG(TAG, "GROW_FINGERPRINT_READER:");
538 ESP_LOGCONFIG(TAG, " System Identifier Code: 0x%.4X", this->system_identifier_code_);
539 ESP_LOGCONFIG(TAG, " Touch Sensing Pin: %s",
540 this->has_sensing_pin_ ? this->sensing_pin_->dump_summary().c_str() : "None");
541 ESP_LOGCONFIG(TAG, " Sensor Power Pin: %s",
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 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:39
float get_state() const
Getter-syntax for .state.
Definition sensor.cpp:86
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:28
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:27
uint16_t length
Definition tt21100.cpp:0