ESPHome  2023.11.6
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 
5 namespace esphome {
6 namespace fingerprint_grow {
7 
8 static 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_) {
14  this->finish_enrollment(this->save_fingerprint_());
15  return;
16  }
17 
18  if (this->sensing_pin_ != nullptr) {
19  if (this->sensing_pin_->digital_read()) {
20  ESP_LOGV(TAG, "No touch sensing");
21  this->waiting_removal_ = false;
22  return;
23  }
24  }
25 
26  if (this->waiting_removal_) {
27  if (this->scan_image_(1) == NO_FINGER) {
28  ESP_LOGD(TAG, "Finger removed");
29  this->waiting_removal_ = false;
30  }
31  return;
32  }
33 
34  if (this->enrollment_image_ == 0) {
35  this->scan_and_match_();
36  return;
37  }
38 
39  uint8_t result = this->scan_image_(this->enrollment_image_);
40  if (result == NO_FINGER) {
41  return;
42  }
43  this->waiting_removal_ = true;
44  if (result != OK) {
45  this->finish_enrollment(result);
46  return;
47  }
49  ++this->enrollment_image_;
50 }
51 
53  ESP_LOGCONFIG(TAG, "Setting up Grow Fingerprint Reader...");
54  if (this->check_password_()) {
55  if (this->new_password_ != -1) {
56  if (this->set_password_())
57  return;
58  } else {
59  if (this->get_parameters_())
60  return;
61  }
62  }
63  this->mark_failed();
64 }
65 
66 void FingerprintGrowComponent::enroll_fingerprint(uint16_t finger_id, uint8_t num_buffers) {
67  ESP_LOGI(TAG, "Starting enrollment in slot %d", finger_id);
68  if (this->enrolling_binary_sensor_ != nullptr) {
70  }
71  this->enrollment_slot_ = finger_id;
72  this->enrollment_buffers_ = num_buffers;
73  this->enrollment_image_ = 1;
74 }
75 
77  if (result == OK) {
79  this->get_fingerprint_count_();
80  } else {
81  if (this->enrollment_slot_ != ENROLLMENT_SLOT_UNUSED) {
83  }
84  }
85  this->enrollment_image_ = 0;
86  this->enrollment_slot_ = ENROLLMENT_SLOT_UNUSED;
87  if (this->enrolling_binary_sensor_ != nullptr) {
89  }
90  ESP_LOGI(TAG, "Finished enrollment");
91 }
92 
94  if (this->sensing_pin_ != nullptr) {
95  ESP_LOGD(TAG, "Scan and match");
96  } else {
97  ESP_LOGV(TAG, "Scan and match");
98  }
99  if (this->scan_image_(1) == OK) {
100  this->waiting_removal_ = true;
101  this->data_ = {SEARCH, 0x01, 0x00, 0x00, (uint8_t) (this->capacity_ >> 8), (uint8_t) (this->capacity_ & 0xFF)};
102  switch (this->send_command_()) {
103  case OK: {
104  ESP_LOGD(TAG, "Fingerprint matched");
105  uint16_t finger_id = ((uint16_t) this->data_[1] << 8) | this->data_[2];
106  uint16_t confidence = ((uint16_t) this->data_[3] << 8) | this->data_[4];
107  if (this->last_finger_id_sensor_ != nullptr) {
108  this->last_finger_id_sensor_->publish_state(finger_id);
109  }
110  if (this->last_confidence_sensor_ != nullptr) {
111  this->last_confidence_sensor_->publish_state(confidence);
112  }
113  this->finger_scan_matched_callback_.call(finger_id, confidence);
114  break;
115  }
116  case NOT_FOUND:
117  ESP_LOGD(TAG, "Fingerprint not matched to any saved slots");
118  this->finger_scan_unmatched_callback_.call();
119  break;
120  }
121  }
122 }
123 
124 uint8_t FingerprintGrowComponent::scan_image_(uint8_t buffer) {
125  if (this->sensing_pin_ != nullptr) {
126  ESP_LOGD(TAG, "Getting image %d", buffer);
127  } else {
128  ESP_LOGV(TAG, "Getting image %d", buffer);
129  }
130  this->data_ = {GET_IMAGE};
131  switch (this->send_command_()) {
132  case OK:
133  break;
134  case NO_FINGER:
135  if (this->sensing_pin_ != nullptr) {
136  ESP_LOGD(TAG, "No finger");
137  } else {
138  ESP_LOGV(TAG, "No finger");
139  }
140  return this->data_[0];
141  case IMAGE_FAIL:
142  ESP_LOGE(TAG, "Imaging error");
143  default:
144  return this->data_[0];
145  }
146 
147  ESP_LOGD(TAG, "Processing image %d", buffer);
148  this->data_ = {IMAGE_2_TZ, buffer};
149  switch (this->send_command_()) {
150  case OK:
151  ESP_LOGI(TAG, "Processed image %d", buffer);
152  break;
153  case IMAGE_MESS:
154  ESP_LOGE(TAG, "Image too messy");
155  break;
156  case FEATURE_FAIL:
157  case INVALID_IMAGE:
158  ESP_LOGE(TAG, "Could not find fingerprint features");
159  break;
160  }
161  return this->data_[0];
162 }
163 
165  ESP_LOGI(TAG, "Creating model");
166  this->data_ = {REG_MODEL};
167  switch (this->send_command_()) {
168  case OK:
169  break;
170  case ENROLL_MISMATCH:
171  ESP_LOGE(TAG, "Scans do not match");
172  default:
173  return this->data_[0];
174  }
175 
176  ESP_LOGI(TAG, "Storing model");
177  this->data_ = {STORE, 0x01, (uint8_t) (this->enrollment_slot_ >> 8), (uint8_t) (this->enrollment_slot_ & 0xFF)};
178  switch (this->send_command_()) {
179  case OK:
180  ESP_LOGI(TAG, "Stored model");
181  break;
182  case BAD_LOCATION:
183  ESP_LOGE(TAG, "Invalid slot");
184  break;
185  case FLASH_ERR:
186  ESP_LOGE(TAG, "Error writing to flash");
187  break;
188  }
189  return this->data_[0];
190 }
191 
193  ESP_LOGD(TAG, "Checking password");
194  this->data_ = {VERIFY_PASSWORD, (uint8_t) (this->password_ >> 24), (uint8_t) (this->password_ >> 16),
195  (uint8_t) (this->password_ >> 8), (uint8_t) (this->password_ & 0xFF)};
196  switch (this->send_command_()) {
197  case OK:
198  ESP_LOGD(TAG, "Password verified");
199  return true;
200  case PASSWORD_FAIL:
201  ESP_LOGE(TAG, "Wrong password");
202  break;
203  }
204  return false;
205 }
206 
208  ESP_LOGI(TAG, "Setting new password: %" PRIu32, this->new_password_);
209  this->data_ = {SET_PASSWORD, (uint8_t) (this->new_password_ >> 24), (uint8_t) (this->new_password_ >> 16),
210  (uint8_t) (this->new_password_ >> 8), (uint8_t) (this->new_password_ & 0xFF)};
211  if (this->send_command_() == OK) {
212  ESP_LOGI(TAG, "New password successfully set");
213  ESP_LOGI(TAG, "Define the new password in your configuration and reflash now");
214  ESP_LOGW(TAG, "!!!Forgetting the password will render your device unusable!!!");
215  return true;
216  }
217  return false;
218 }
219 
221  ESP_LOGD(TAG, "Getting parameters");
222  this->data_ = {READ_SYS_PARAM};
223  if (this->send_command_() == OK) {
224  ESP_LOGD(TAG, "Got parameters");
225  if (this->status_sensor_ != nullptr) {
226  this->status_sensor_->publish_state(((uint16_t) this->data_[1] << 8) | this->data_[2]);
227  }
228  this->capacity_ = ((uint16_t) this->data_[5] << 8) | this->data_[6];
229  if (this->capacity_sensor_ != nullptr) {
231  }
232  if (this->security_level_sensor_ != nullptr) {
233  this->security_level_sensor_->publish_state(((uint16_t) this->data_[7] << 8) | this->data_[8]);
234  }
235  if (this->enrolling_binary_sensor_ != nullptr) {
237  }
238  this->get_fingerprint_count_();
239  return true;
240  }
241  return false;
242 }
243 
245  ESP_LOGD(TAG, "Getting fingerprint count");
246  this->data_ = {TEMPLATE_COUNT};
247  if (this->send_command_() == OK) {
248  ESP_LOGD(TAG, "Got fingerprint count");
249  if (this->fingerprint_count_sensor_ != nullptr)
250  this->fingerprint_count_sensor_->publish_state(((uint16_t) this->data_[1] << 8) | this->data_[2]);
251  }
252 }
253 
255  ESP_LOGI(TAG, "Deleting fingerprint in slot %d", finger_id);
256  this->data_ = {DELETE, (uint8_t) (finger_id >> 8), (uint8_t) (finger_id & 0xFF), 0x00, 0x01};
257  switch (this->send_command_()) {
258  case OK:
259  ESP_LOGI(TAG, "Deleted fingerprint");
260  this->get_fingerprint_count_();
261  break;
262  case DELETE_FAIL:
263  ESP_LOGE(TAG, "Reader failed to delete fingerprint");
264  break;
265  }
266 }
267 
269  ESP_LOGI(TAG, "Deleting all stored fingerprints");
270  this->data_ = {EMPTY};
271  switch (this->send_command_()) {
272  case OK:
273  ESP_LOGI(TAG, "Deleted all fingerprints");
274  this->get_fingerprint_count_();
275  break;
276  case DB_CLEAR_FAIL:
277  ESP_LOGE(TAG, "Reader failed to clear fingerprint library");
278  break;
279  }
280 }
281 
283  ESP_LOGD(TAG, "Setting LED");
284  if (state) {
285  this->data_ = {LED_ON};
286  } else {
287  this->data_ = {LED_OFF};
288  }
289  switch (this->send_command_()) {
290  case OK:
291  ESP_LOGD(TAG, "LED set");
292  break;
293  case PACKET_RCV_ERR:
294  case TIMEOUT:
295  break;
296  default:
297  ESP_LOGE(TAG, "Try aura_led_control instead");
298  break;
299  }
300 }
301 
302 void FingerprintGrowComponent::aura_led_control(uint8_t state, uint8_t speed, uint8_t color, uint8_t count) {
303  const uint32_t now = millis();
304  const uint32_t elapsed = now - this->last_aura_led_control_;
305  if (elapsed < this->last_aura_led_duration_) {
306  delay(this->last_aura_led_duration_ - elapsed);
307  }
308  ESP_LOGD(TAG, "Setting Aura LED");
309  this->data_ = {AURA_CONFIG, state, speed, color, count};
310  switch (this->send_command_()) {
311  case OK:
312  ESP_LOGD(TAG, "Aura LED set");
313  this->last_aura_led_control_ = millis();
314  this->last_aura_led_duration_ = 10 * speed * count;
315  break;
316  case PACKET_RCV_ERR:
317  case TIMEOUT:
318  break;
319  default:
320  ESP_LOGE(TAG, "Try led_control instead");
321  break;
322  }
323 }
324 
326  this->write((uint8_t) (START_CODE >> 8));
327  this->write((uint8_t) (START_CODE & 0xFF));
328  this->write(this->address_[0]);
329  this->write(this->address_[1]);
330  this->write(this->address_[2]);
331  this->write(this->address_[3]);
332  this->write(COMMAND);
333 
334  uint16_t wire_length = this->data_.size() + 2;
335  this->write((uint8_t) (wire_length >> 8));
336  this->write((uint8_t) (wire_length & 0xFF));
337 
338  uint16_t sum = ((wire_length) >> 8) + ((wire_length) &0xFF) + COMMAND;
339  for (auto data : this->data_) {
340  this->write(data);
341  sum += data;
342  }
343 
344  this->write((uint8_t) (sum >> 8));
345  this->write((uint8_t) (sum & 0xFF));
346 
347  this->data_.clear();
348 
349  uint8_t byte;
350  uint16_t idx = 0, length = 0;
351 
352  for (uint16_t timer = 0; timer < 1000; timer++) {
353  if (this->available() == 0) {
354  delay(1);
355  continue;
356  }
357  byte = this->read();
358  switch (idx) {
359  case 0:
360  if (byte != (uint8_t) (START_CODE >> 8))
361  continue;
362  break;
363  case 1:
364  if (byte != (uint8_t) (START_CODE & 0xFF)) {
365  idx = 0;
366  continue;
367  }
368  break;
369  case 2:
370  case 3:
371  case 4:
372  case 5:
373  if (byte != this->address_[idx - 2]) {
374  idx = 0;
375  continue;
376  }
377  break;
378  case 6:
379  if (byte != ACK) {
380  idx = 0;
381  continue;
382  }
383  break;
384  case 7:
385  length = (uint16_t) byte << 8;
386  break;
387  case 8:
388  length |= byte;
389  break;
390  default:
391  this->data_.push_back(byte);
392  if ((idx - 8) == length) {
393  switch (this->data_[0]) {
394  case OK:
395  case NO_FINGER:
396  case IMAGE_FAIL:
397  case IMAGE_MESS:
398  case FEATURE_FAIL:
399  case NO_MATCH:
400  case NOT_FOUND:
401  case ENROLL_MISMATCH:
402  case BAD_LOCATION:
403  case DELETE_FAIL:
404  case DB_CLEAR_FAIL:
405  case PASSWORD_FAIL:
406  case INVALID_IMAGE:
407  case FLASH_ERR:
408  break;
409  case PACKET_RCV_ERR:
410  ESP_LOGE(TAG, "Reader failed to process request");
411  break;
412  default:
413  ESP_LOGE(TAG, "Unknown response received from reader: %d", this->data_[0]);
414  break;
415  }
416  return this->data_[0];
417  }
418  break;
419  }
420  idx++;
421  }
422  ESP_LOGE(TAG, "No response received from reader");
423  this->data_[0] = TIMEOUT;
424  return TIMEOUT;
425 }
426 
428  ESP_LOGCONFIG(TAG, "GROW_FINGERPRINT_READER:");
429  LOG_UPDATE_INTERVAL(this);
430  LOG_SENSOR(" ", "Fingerprint Count", this->fingerprint_count_sensor_);
431  LOG_SENSOR(" ", "Status", this->status_sensor_);
432  LOG_SENSOR(" ", "Capacity", this->capacity_sensor_);
433  LOG_SENSOR(" ", "Security Level", this->security_level_sensor_);
434  LOG_SENSOR(" ", "Last Finger ID", this->last_finger_id_sensor_);
435  LOG_SENSOR(" ", "Last Confidence", this->last_confidence_sensor_);
436 }
437 
438 } // namespace fingerprint_grow
439 } // namespace esphome
CallbackManager< void(uint16_t, uint16_t)> finger_scan_matched_callback_
CallbackManager< void(uint8_t, uint16_t)> enrollment_scan_callback_
int speed
Definition: fan.h:35
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
void publish_state(bool state)
Publish a new state to the front-end.
void enroll_fingerprint(uint16_t finger_id, uint8_t num_buffers)
CallbackManager< void(uint16_t)> enrollment_failed_callback_
virtual bool digital_read()=0
binary_sensor::BinarySensor * enrolling_binary_sensor_
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:112
uint16_t length
Definition: tt21100.cpp:12
CallbackManager< void(uint16_t)> enrollment_done_callback_
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void aura_led_control(uint8_t state, uint8_t speed, uint8_t color, uint8_t count)
size_t write(uint8_t data)
Definition: uart.h:52
bool state
Definition: fan.h:34
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26