Калитка на сканере отпечатков пальцев CROW R503
- Войдите на сайт для отправки комментариев
Заняться этой проблемой меня сподвигло 2 не связанных друг с другом событий, во первых на работе при решение весьма конкретных задач всерьез обсуждали некую автоматизацию с использованием биометрического распознавания, вариантов было много, но все или слишком дорогие или нам не подходили.
Ну а второе событие - меня обокрали :) сперли с участка велосипед, и хоть злодеев нашли и велосипед вернули но я стал укреплять передний край обороны от злодеев (датчики, камеры и т.д.).
И вот я добрался до калитки, стал думать как сделать и надежно и безопасно, сначала хотел купить датчик "таблетка", но потом понял, что это не удобно, и потерять можно и забыть дома (дети постоянно бегают то туда то сюда).
От сюда возникло желание сделать так, что бы нельзя было ключ украсть или потерять. Но по сколько мне было интересно разобраться и я решить сразу и вопрос который возник на работе, то я заказал датчик без платы управления.
Начал я с библиотеки Adafruit_Fingerprint, но очень быстро понял, что она во первых сильно кривая, а во вторых в ней банально нет поддержки части нужных команд датчика R503.
В результате я просто частично ее использовал в виде вставок в код.
На момент публикации у меня все работает в домашних условия (платы спаяны, код написан). Но поставить на улицу пока не могу (погода плохая), буду ждать выходных.
теперь ТТХ того, что получилось:
1. После обучения, распознает палец почти со 100% вероятностью, не срабатывает примерно 1 раз на 50 попыток. Чужие пальцы ни разу не привели к открытию (проверял на всей семье).
2. Время распознавания около 1 сек
3. Обучение на 1 палец занимает 2...3 минуты (необходимо создать 3 модели)
В 1 посте выложу код и схему (что бы можно было править)
схема
файл R503.ino
001
// ---------------------------------------------------------
002
// Управление сканером отпечатка пальцев
003
//
004
// состав проекта:
005
//
006
// версия среды Arduino 1.8.14
007
//
008
// автор <a href="mailto:vde69@mail.ru">vde69@mail.ru</a> (с), процедуры главного алгоритма
009
// ---------------------------------------------------------
010
011
// ------------------------------------------------------------
012
// конфигурация прошивки
013
//
014
// перечень прошивок, все кроме одной должны быть закоментированы
015
// детальная настройка прошивок в файле "A_Config.h"
016
// ------------------------------------------------------------
017
018
#define DOR_CLOCK // конфигурация "уличная калитка"
019
020
021
#include "A_Config.h"
022
023
// ------------------------------------------------------------
024
// расширения и общие данные для всех подключаемых классов (модулей)
025
//
026
// вынесены в отдельные 2 файла "include/a_house_addon.h" и "include/a_house_addon.cpp"
027
// с целью возможности использовать данные как в основной сборке так и при
028
// синтаксическом контроле подключаемых классов расположеных в каталоге "include/"
029
// ------------------------------------------------------------
030
031
#include
032
#include "Run.h"
033
034
// ------------------------------------------------------------
035
// загрузка библиотек, желательно загрузить все библиотеки до первого оператора
036
// при более позденй загрузки возможны неочевидные ошибки компиляции,
037
// очередность загрузки библиотек важен!!!
038
// до загрузки библиотек не должно быть ничего кроме директив компилятору!!!
039
040
#ifdef GROW_R503
041
#include "GROW_R503.h"
042
#endif
043
044
045
// ------------------------------------------------------------
046
// Общие переменные не зависящие от конфигурации устройства
047
unsigned
long
time_loop_new = 0;
// время начала цикла, глобальное время используется для отсчета времени событий
048
049
unsigned
long
time_loop_key = 0;
// время отсчета изменения статуса кнопки
050
boolean status_key =
false
;
// статус кнопки, true - кнопка нажата
051
052
const
uint8_t pinKey = 6;
// кнопка перехода в спец режимы на 6 пине
053
const
unsigned
long
pauseKey = 5000;
// время удержания кнопки необходимое для смены режима
054
055
const
uint8_t pinOpen = 7;
// пин открытия замка
056
boolean status_open =
false
;
// статус кнопки, true - кнопка нажата
057
058
// ------------------------------------------------------------
059
// определение переменных и настроек для датчика GROW-R503
060
#ifdef GROW_R503
061
// 2pin на - TXD датчика (желтый)
062
// 3pin на - RXD датчика (зеленый)
063
// 5pin на - WAKEUP датчика (синий)
064
// 4pin на - основное питание датчика (красный)
065
SoftwareSerial SerialR503(2, 3);
066
Finger_R503 finger = Finger_R503(5,4,&SerialR503);
067
#endif
068
069
070
071
072
void
setup
()
073
{
074
// кнопка перехода в спец режимы на 6 пине
075
pinMode(pinKey, INPUT);
076
077
digitalWrite(pinOpen, LOW);
// сигнал на замок выключен
078
pinMode(pinOpen, OUTPUT);
079
status_open =
false
;
080
081
Serial
.begin(9600);
// Инициализация аппаратного UART на скорости 9600
082
Serial
.println(
"System START..."
);
// Вывод сообщения начла работы
083
084
#ifdef GROW_R503
085
finger.init();
086
finger.set_mode(FINGER_MODE_SLEEP);
// при включении установим спящий режим
087
#endif
088
089
}
090
091
//*************************************************************************************************
092
// главный цикл
093
//*************************************************************************************************
094
void
loop
() {
095
096
// получим время начала цикла
097
time_loop_new = millis();
098
099
// обработаем кнопку спец режимов
100
poll(time_loop_new);
101
102
#ifdef GROW_R503
103
// отвечает за обмен данными с датчиком R503
104
finger.poll(time_loop_new);
105
#endif
106
107
}
108
}
файл A_Config.h
01
// ------------------------------------------------------------
02
// Управление электромеханическим замком
03
//
04
// автор <a href="mailto:vde69@mail.ru">vde69@mail.ru</a> (с)
05
// ------------------------------------------------------------
06
07
08
09
// ВНИМАНИЕ, в данном модуле использовать можно ТОЛЬКО директивы компилятору #define, #ifdef, #endif, #undef
10
// в случае использовании любых других операторов будут неочевидные ошибки компиляции
11
12
13
// ------------------------------------------------------------------
14
// Значения по умолчанию, можно переопределять в конфигурациях через отмену #undef
15
16
// ------------------------------------------------------------------
17
// Конфигурация управления замком на калитке
18
// состоит из
19
// Arduino NANO
20
// GROW R503
21
// Вызывная панель домофона P21GE
22
#ifdef DOR_CLOCK
23
#define GROW_R503 // определяем датчик
24
25
26
#endif
файл GROW_R503.cpp
001
/***************************************************
002
Объект для работы со сканером отпечатка пальцев GROW R503
003
004
Создан на основе библиотеки Adafruit_Fingerprint
005
006
****************************************************/
007
008
#include "GROW_R503.h"
009
010
#ifdef __AVR__
011
#include
012
#include
013
#endif
014
015
//-----------------------------------------------------------
016
//#define DEBUG_R503 // вывод сообщений модуля R503, в продакшене закоментить
017
//#define DEBUG_R503_READ_ECHO // вывод данных получаемых от модуля R503, в продакшене закоментить
018
019
#ifdef DEBUG_R503_READ_ECHO
020
uint8_t Debug_Read_Echo_Packet[] = {0x05,0x06,0x1f,0x0D};
// номера пакетов которые нужно выводить
021
#endif
022
023
// -----------------------------------------------------------
024
#ifdef __AVR__
025
Finger_R503::Finger_R503(
int
wPin,
int
pPin, SoftwareSerial *ss) {
026
thePassword = 0;
027
theAddress = 0xFFFFFFFF;
028
029
hwSerial = NULL;
030
swSerial = ss;
031
mySerial = swSerial;
032
wakerPin = wPin;
033
powerPin = pPin;
034
035
}
036
#endif
037
038
Finger_R503::Finger_R503(
int
wPin,
int
pPin, HardwareSerial *ss) {
039
thePassword = 0;
040
theAddress = 0xFFFFFFFF;
041
042
#ifdef __AVR__
043
swSerial = NULL;
044
#endif
045
hwSerial = ss;
046
mySerial = hwSerial;
047
wakerPin = wPin;
048
powerPin = pPin;
049
}
050
051
052
void
Finger_R503::RunStep(unsigned
long
time_loop ) {
053
054
boolean TecFirstStep = FirstStep; FirstStep =
false
;
055
056
if
(stepMode == 0) {
057
if
(mode == FINGER_MODE_SLEEP) { set_stepMode (0); }
058
else
if
(mode == FINGER_MODE_SCAN) { set_stepMode (1); }
059
else
if
(mode == FINGER_MODE_ADD) { set_stepMode (20); }
060
else
if
(mode == FINGER_MODE_ADD_A) { set_stepMode (10); }
061
else
if
(mode == FINGER_MODE_CLEAR) { set_stepMode (90); }
062
else
if
(mode == FINGER_MODE_CLEAR_A) { set_stepMode (90); }
063
else
{ set_stepMode (0); }
064
return
;
065
}
066
067
// ------------------------------------------------------------------------------------
068
if
(stepMode == 1) {
069
if
(verifyPassword()) {
070
if
(TecFirstStep) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_BLUE, 0);
071
if
(getImage() == FINGER_OK) set_stepMode (2);
072
}
else
FirstStep = TecFirstStep;
073
return
;
074
}
075
076
if
(stepMode == 2) {
077
if
(verifyPassword()) {
078
if
(image2Tz(1) == FINGER_OK) set_stepMode (3);
079
else
set_stepMode (1);
080
time_loop_step = time_loop;
081
}
082
return
;
083
}
084
085
if
(stepMode == 3) {
086
if
(verifyPassword()) {
087
if
(Search(0x01) == FINGER_OK) set_stepMode (100);
088
else
set_stepMode (101);
089
time_loop_step = time_loop;
090
}
091
return
;
092
}
093
094
if
(stepMode == 100) {
// есть выход по тайму
095
// это событие "скан сработал"
096
if
(TecFirstStep) {
097
if
(verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_PURLE, 0);
098
else
FirstStep = TecFirstStep;
099
time_loop_step = time_loop;
100
}
101
if
(getDelayTime(time_loop_step, time_loop) > 2000) {
102
set_stepMode (0);
103
time_loop_step = time_loop;
104
}
105
return
;
106
}
107
108
if
(stepMode == 101) {
// есть выход по тайму
109
if
(TecFirstStep) {
110
if
(verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_RED, 0);
111
else
FirstStep = TecFirstStep;
112
time_loop_step = time_loop;
113
}
114
if
(getDelayTime(time_loop_step, time_loop) > 2000) {
115
set_stepMode (0);
116
time_loop_step = time_loop;
117
}
118
return
;
119
}
120
121
// ------------------------------------------------------------------------------------
122
123
if
(stepMode == 10) {
124
if
(verifyPassword()) {
125
leteNum = GetFreeIndex(0);
126
if
(leteNum == 0xff) {}
// ошибка, пробуем еще
127
else
if
(leteNum >= 199) {}
// нет места в библиотеке, никуда не уходим, ждем смены режима на сон или на очистку
128
else
if
(leteNum == 0) {
// отпечатков в базе нет, переходим к добавлению
129
set_stepMode (20);
130
Num = 1;
131
time_loop_step = time_loop;
132
}
else
if
(mode == FINGER_MODE_ADD) {
// отпечаток был предоставлен ранее
133
set_stepMode (20);
134
Num = 1;
135
time_loop_step = time_loop;
136
}
else
{
// есть отпечатки, требуется подтверждение
137
set_stepMode (12);
138
time_loop_step = time_loop;
139
}
140
}
141
return
;
142
}
143
144
if
(stepMode == 12) {
145
if
(verifyPassword()) {
146
if
(TecFirstStep) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_BLUE, 0);
147
if
(getImage() == FINGER_OK) {
148
set_stepMode (13);
149
time_loop_step = time_loop;
150
}
151
}
else
FirstStep = TecFirstStep;
152
return
;
153
}
154
155
if
(stepMode == 13) {
156
if
(verifyPassword()) {
157
if
(image2Tz(1) == FINGER_OK) set_stepMode (14);
158
else
set_stepMode (11);
159
time_loop_step = time_loop;
160
}
161
return
;
162
}
163
164
if
(stepMode == 14) {
165
if
(verifyPassword()) {
166
if
(Search(0x01) == FINGER_OK) {
167
set_stepMode (102);
168
Num = 1;
169
}
170
else
set_stepMode (103);
171
time_loop_step = time_loop;
172
}
173
return
;
174
}
175
176
if
(stepMode == 102) {
// есть выход по тайму
177
if
(TecFirstStep) {
178
if
(verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_PURLE, 0);
179
else
FirstStep = TecFirstStep;
180
time_loop_step = time_loop;
181
}
182
if
(getDelayTime(time_loop_step, time_loop) > 2000) {
183
set_mode(FINGER_MODE_ADD);
184
Num = 1;
185
time_loop_step = time_loop;
186
}
187
return
;
188
}
189
190
if
(stepMode == 103) {
// есть выход по тайму
191
if
(TecFirstStep) {
192
if
(verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_RED, 0);
193
else
FirstStep = TecFirstStep;
194
time_loop_step = time_loop;
195
}
196
if
(getDelayTime(time_loop_step, time_loop) > 2000) {
197
set_stepMode (10);
198
time_loop_step = time_loop;
199
}
200
return
;
201
}
202
203
// ------------------------------------------------------------------------------------
204
205
if
(stepMode == 20) {
206
if
(verifyPassword()) {
207
if
(TecFirstStep) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_PURLE, 0);
208
if
(getImage() == FINGER_OK) {
209
set_stepMode (21);
210
time_loop_step = time_loop;
211
}
212
}
else
{
213
FirstStep = TecFirstStep;
214
}
215
return
;
216
}
217
218
if
(stepMode == 21) {
219
if
(verifyPassword()) {
220
if
(image2Tz(Num) == FINGER_OK) set_stepMode (22);
221
else
set_stepMode (20);
222
time_loop_step = time_loop;
223
}
224
return
;
225
}
226
227
if
(stepMode == 22) {
228
if
(verifyPassword()) {
229
if
(Search(Num) != FINGER_OK) set_stepMode (104);
230
else
set_stepMode (105);
231
time_loop_step = time_loop;
232
}
233
return
;
234
}
235
236
if
(stepMode == 104) {
// есть выход по тайму
237
if
(TecFirstStep) {
238
if
(verifyPassword()) {
239
LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_BLUE, 0);
240
time_loop_step = time_loop;
241
Num = Num + 1;
242
}
243
else
FirstStep = TecFirstStep;
244
}
245
if
(getDelayTime(time_loop_step, time_loop) > 2000) {
246
if
(Num > 2) set_stepMode (60);
247
else
set_stepMode (20);
248
time_loop_step = time_loop;
249
}
250
return
;
251
}
252
253
if
(stepMode == 105) {
// есть выход по тайму
254
if
(TecFirstStep) {
255
if
(verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_RED, 0);
256
else
FirstStep = TecFirstStep;
257
time_loop_step = time_loop;
258
}
259
if
(getDelayTime(time_loop_step, time_loop) > 2000) {
260
set_stepMode (20);
261
Num = 1;
262
time_loop_step = time_loop;
263
}
264
return
;
265
}
266
267
// ------------------------------------------------------------------------------------
268
269
270
271
if
(stepMode == 60) {
272
if
(verifyPassword()) {
273
if
(TecFirstStep) {
274
LedConfig(FINGER_LED_1, 0, FINGER_COLOR_PURLE, 0);
275
if
(RegModel() == FINGER_OK) set_stepMode (61);
276
else
set_stepMode (106);
277
time_loop_step = time_loop;
278
}
else
FirstStep = TecFirstStep;
279
}
280
return
;
281
}
282
283
if
(stepMode == 61) {
284
if
(verifyPassword()) {
285
if
(store(leteNum) == FINGER_OK) {
286
set_mode(FINGER_MODE_ADD);
287
Num = 1;
288
set_stepMode (10);
289
#ifdef DEBUG_R503
290
Serial
.println(
"ADD SCAN BASE"
);
291
#endif
292
293
}
294
else
set_stepMode (106);
295
time_loop_step = time_loop;
296
}
297
return
;
298
}
299
300
if
(stepMode == 106) {
// есть выход по тайму
301
if
(TecFirstStep) {
302
if
(verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_RED, 0);
303
else
FirstStep = TecFirstStep;
304
time_loop_step = time_loop;
305
}
306
if
(getDelayTime(time_loop_step, time_loop) > 2000) {
307
set_mode(FINGER_MODE_ADD);
308
set_stepMode (10);
309
Num = 1;
310
time_loop_step = time_loop;
311
}
312
return
;
313
}
314
315
//----------------------------------------------------------------------------------------------
316
317
if
(stepMode == 90) {
318
if
(verifyPassword()) {
319
if
(TecFirstStep) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_BLUE, 0);
320
if
(getImage() == FINGER_OK) {
321
set_stepMode (91);
322
time_loop_step = time_loop;
323
}
324
}
else
FirstStep = TecFirstStep;
325
return
;
326
}
327
328
if
(stepMode == 91) {
329
if
(verifyPassword()) {
330
if
(image2Tz(1) == FINGER_OK) set_stepMode (92);
331
else
set_stepMode (90);
332
time_loop_step = time_loop;
333
}
334
return
;
335
}
336
337
if
(stepMode == 92) {
338
if
(verifyPassword()) {
339
if
(Search(0x01) == FINGER_OK) set_stepMode (93);
340
else
set_stepMode (109);
341
time_loop_step = time_loop;
342
}
343
return
;
344
}
345
346
if
(stepMode == 93) {
// есть выход по тайму
347
if
(TecFirstStep) {
348
if
(verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_PURLE, 0);
349
else
FirstStep = TecFirstStep;
350
time_loop_step = time_loop;
351
}
352
if
(getDelayTime(time_loop_step, time_loop) > 2000) {
353
set_stepMode (94);
354
time_loop_step = time_loop;
355
}
356
return
;
357
}
358
359
if
(stepMode == 94) {
360
if
(verifyPassword()) {
361
if
(TecFirstStep) LedConfig(FINGER_LED_1, 0, FINGER_COLOR_RED, 0);
362
if
(getImage() == FINGER_OK) {
363
set_stepMode (95);
364
time_loop_step = time_loop;
365
}
366
}
else
FirstStep = TecFirstStep;
367
return
;
368
}
369
370
if
(stepMode == 95) {
371
if
(verifyPassword()) {
372
if
(image2Tz(1) == FINGER_OK) set_stepMode (96);
373
else
set_stepMode (94);
374
time_loop_step = time_loop;
375
}
376
return
;
377
}
378
379
if
(stepMode == 96) {
380
if
(verifyPassword()) {
381
if
(Search(0x01) == FINGER_OK) {
382
emptyDatabase();
383
set_mode(FINGER_MODE_SLEEP);
384
}
385
else
set_stepMode (109);
386
time_loop_step = time_loop;
387
}
388
return
;
389
}
390
391
if
(stepMode == 109) {
// есть выход по тайму
392
if
(TecFirstStep) {
393
if
(verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_RED, 0);
394
else
FirstStep = TecFirstStep;
395
time_loop_step = time_loop;
396
}
397
if
(getDelayTime(time_loop_step, time_loop) > 2000) {
398
set_stepMode (90);
399
time_loop_step = time_loop;
400
}
401
return
;
402
}
403
404
405
406
407
}
408
409
410
// -----------------------------------------------------------
411
void
Finger_R503::poll(unsigned
long
time_loop) {
412
413
boolean waker = !digitalRead(wakerPin);
414
415
if
(waker) {
416
if
(mode == FINGER_MODE_SLEEP ) {
417
set_mode (FINGER_MODE_SCAN);
418
}
419
time_loop_mode = time_loop;
420
}
else
{
421
if
(mode != FINGER_MODE_SLEEP ) {
422
if
(getDelayTime(time_loop_mode, time_loop) > TIME_RUN) {
423
set_mode (FINGER_MODE_SLEEP);
424
}
425
}
426
}
427
RunStep(time_loop );
428
429
}
430
431
432
// -----------------------------------------------------------
433
boolean Finger_R503::LedConfig(uint8_t Ctrl, uint8_t Speed, uint8_t Color, uint8_t Count) {
434
uint8_t packet[] = {0x35,
435
Ctrl,
436
Speed,
437
Color,
438
Count
439
};
440
writePacket(theAddress, FINGER_COMMANDPACKET,
sizeof
(packet) + 2, packet);
441
uint8_t packetR[2];
442
uint8_t len = getReply(packetR, packet[0]);
443
444
if
((len == 1) && (packetR[0] == FINGER_ACKPACKET) && (packetR[1] == FINGER_OK))
return
true
;
445
return
false
;
446
}
447
448
449
// -----------------------------------------------------------
450
boolean Finger_R503::HandShake(
void
) {
451
uint8_t packet[] = {0x40};
452
writePacket(theAddress, FINGER_COMMANDPACKET,
sizeof
(packet) + 2, packet);
453
uint8_t packetR[2];
454
uint8_t len = getReply(packetR, packet[0]);
455
456
if
((len == 1) && (packetR[0] == FINGER_ACKPACKET) && (packetR[1] == FINGER_OK))
return
true
;
457
return
false
;
458
}
459
460
// -----------------------------------------------------------
461
uint8_t Finger_R503::TempleteNum(
void
) {
462
uint8_t packet[] = {0x1d};
463
writePacket(theAddress, FINGER_COMMANDPACKET,
sizeof
(packet) + 2, packet);
464
uint8_t packetR[2];
465
uint8_t len = getReply(packetR, packet[0]);
466
467
if
((len != 1) && (packetR[0] != FINGER_ACKPACKET)) {
468
return
0;
469
}
470
return
packetR[1];
471
}
472
473
// -----------------------------------------------------------
474
uint8_t Finger_R503::GetFreeIndex(uint8_t page = 0) {
475
uint8_t packet[] = {0x1f, page};
476
writePacket(theAddress, FINGER_COMMANDPACKET,
sizeof
(packet) + 2, packet);
477
uint8_t packetR[35];
478
uint8_t len = getReply(packetR, packet[0]);
479
480
if
((len != 33) || (packetR[0] != FINGER_ACKPACKET) || (packetR[1] != FINGER_OK)) {
481
return
0xff;
482
}
483
484
uint8_t r = 0;
485
uint8_t ri = 0;
486
for
(
int
i = 2; i <= 33; i++) {
487
ri = packetR[i];
488
for
(
int
ii = 0; ii <= 7; ii++) {
489
if
(bitRead(ri, ii) == 0) {
490
return
r;
491
}
492
r++;
493
}
494
}
495
496
return
0;
497
}
498
499
// -----------------------------------------------------------
500
uint8_t Finger_R503::getImage(
void
) {
501
uint8_t packet[] = {0x01};
502
writePacket(theAddress, FINGER_COMMANDPACKET,
sizeof
(packet) + 2, packet);
503
uint8_t packetR[2];
504
uint8_t len = getReply(packetR, packet[0]);
505
506
if
((len != 1) || (packetR[0] != FINGER_ACKPACKET)) {
507
return
100;
508
}
509
return
packetR[1];
510
}
511
512
// -----------------------------------------------------------
513
uint8_t Finger_R503::image2Tz(uint8_t slot) {
// нумерация 1...6
514
uint8_t packet[] = {0x02, slot};
515
writePacket(theAddress, FINGER_COMMANDPACKET,
sizeof
(packet) + 2, packet);
516
uint8_t packetR[2];
517
uint8_t len = getReply(packetR, packet[0]);
518
519
if
((len != 1) || (packetR[0] != FINGER_ACKPACKET)) {
520
return
100;
521
}
522
return
packetR[1];
523
}
524
525
// -----------------------------------------------------------
526
uint16_t Finger_R503::Search(uint8_t slot) {
527
uint16_t StartPage = 0x0000;
528
uint16_t PageNum = 0x00c8;
529
uint8_t packet[] = {0x04,
530
slot,
531
(StartPage >> 8),
532
StartPage,
533
(PageNum >> 8),
534
PageNum
535
};
536
537
writePacket(theAddress, FINGER_COMMANDPACKET,
sizeof
(packet) + 2, packet);
538
uint8_t packetR[6];
539
uint8_t len = getReply(packetR, packet[0]);
540
541
if
((len != 5) || (packetR[0] != FINGER_ACKPACKET)) {
542
return
-1;
543
}
544
// с 0 по 5
545
uint16_t fingerID = 0xFFFF;
546
fingerID = packetR[2];
547
fingerID <<= 8;
548
fingerID |= packetR[3];
549
550
return
packetR[1];
551
}
552
553
554
// -----------------------------------------------------------
555
uint8_t Finger_R503::RegModel(
void
) {
556
uint8_t packet[] = {0x05};
557
writePacket(theAddress, FINGER_COMMANDPACKET,
sizeof
(packet) + 2, packet);
558
uint8_t packetR[2];
559
uint8_t len = getReply(packetR, packet[0]);
560
561
if
((len != 1) || (packetR[0] != FINGER_ACKPACKET))
562
return
-1;
563
return
packetR[1];
564
}
565
566
567
568
// -----------------------------------------------------------
569
uint8_t Finger_R503::store(uint16_t id) {
570
uint8_t packet[] = {0x06, 0x01, id >> 8, id & 0xFF};
571
writePacket(theAddress, FINGER_COMMANDPACKET,
sizeof
(packet) + 2, packet);
572
uint8_t packetR[2];
573
uint8_t len = getReply(packetR, packet[0]);
574
575
if
((len != 1) || (packetR[0] != FINGER_ACKPACKET))
576
return
-1;
577
return
packetR[1];
578
}
579
580
581
// -----------------------------------------------------------
582
boolean Finger_R503::verifyPassword(
void
) {
583
uint8_t packet[] = {FINGER_VERIFYPASSWORD,
584
(thePassword >> 24), (thePassword >> 16),
585
(thePassword >> 8), thePassword
586
};
587
writePacket(theAddress, FINGER_COMMANDPACKET, 7, packet);
588
uint8_t packetR[2];
589
uint8_t len = getReply(packetR, packet[0]);
590
591
if
((len == 1) && (packetR[0] == FINGER_ACKPACKET) && (packetR[1] == FINGER_OK))
return
true
;
592
593
return
false
;
594
}
595
596
// -----------------------------------------------------------
597
uint8_t Finger_R503::emptyDatabase(
void
) {
598
uint8_t packet[] = {0x0D};
599
writePacket(theAddress, FINGER_COMMANDPACKET,
sizeof
(packet) + 2, packet);
600
uint8_t len = getReply(packet, packet[0]);
601
602
if
((len != 1) || (packet[0] != FINGER_ACKPACKET))
603
return
-1;
604
return
packet[1];
605
}
606
607
608
609
void
Finger_R503::writePacket(uint32_t addr, uint8_t packettype, uint16_t len, uint8_t *packet) {
610
611
while
(mySerial->available()) {
612
mySerial->read();
613
}
614
615
#if ARDUINO >= 100
616
mySerial->write((uint8_t)(FINGER_STARTCODE >> 8));
617
mySerial->write((uint8_t)FINGER_STARTCODE);
618
mySerial->write((uint8_t)(addr >> 24));
619
mySerial->write((uint8_t)(addr >> 16));
620
mySerial->write((uint8_t)(addr >> 8));
621
mySerial->write((uint8_t)(addr));
622
mySerial->write((uint8_t)packettype);
623
mySerial->write((uint8_t)(len >> 8));
624
mySerial->write((uint8_t)(len));
625
#else
626
mySerial->print((uint8_t)(FINGER_STARTCODE >> 8), BYTE);
627
mySerial->print((uint8_t)FINGER_STARTCODE, BYTE);
628
mySerial->print((uint8_t)(addr >> 24), BYTE);
629
mySerial->print((uint8_t)(addr >> 16), BYTE);
630
mySerial->print((uint8_t)(addr >> 8), BYTE);
631
mySerial->print((uint8_t)(addr), BYTE);
632
mySerial->print((uint8_t)packettype, BYTE);
633
mySerial->print((uint8_t)(len >> 8), BYTE);
634
mySerial->print((uint8_t)(len), BYTE);
635
#endif
636
637
uint16_t sum = (len >> 8) + (len & 0xFF) + packettype;
638
for
(uint8_t i = 0; i < len - 2; i++) {
639
#if ARDUINO >= 100
640
mySerial->write((uint8_t)(packet[i]));
641
#else
642
mySerial->print((uint8_t)(packet[i]), BYTE);
643
#endif
644
sum += packet[i];
645
}
646
#if ARDUINO >= 100
647
mySerial->write((uint8_t)(sum >> 8));
648
mySerial->write((uint8_t)sum);
649
#else
650
mySerial->print((uint8_t)(sum >> 8), BYTE);
651
mySerial->print((uint8_t)sum, BYTE);
652
#endif
653
}
654
655
656
657
658
uint8_t Finger_R503::getReply(uint8_t packet[], uint8_t id_packet, uint16_t timeout) {
659
uint8_t reply[58], idx;
660
// uint8_t reply[42], idx;
661
uint16_t timer = 0;
662
663
idx = 0;
664
#ifdef DEBUG_R503_READ_ECHO
665
boolean Echo_ON =
false
;
666
for
(uint8_t i1 = 0; i1 <
sizeof
(Debug_Read_Echo_Packet); i1++) {
if
(Debug_Read_Echo_Packet[i1] == id_packet) {Echo_ON =
true
;} }
667
if
(Echo_ON) {
Serial
.print(
" 0x"
);
Serial
.print(String(id_packet, HEX));
Serial
.print(
":<--- "
);}
668
#endif
669
while
(
true
) {
670
671
if
(idx >= 58) {
672
#ifdef DEBUG_R503_READ_ECHO
673
if
(Echo_ON) {
Serial
.print(
" Bad length-"
);
Serial
.println(idx);}
674
#endif
675
return
FINGER_BADPACKET;
676
}
677
678
while
(!mySerial->available()) {
679
delay(1);
680
timer++;
681
if
(timer >= timeout)
return
FINGER_TIMEOUT;
682
}
683
// something to read!
684
reply[idx] = mySerial->read();
685
#ifdef DEBUG_R503_READ_ECHO
686
if
(Echo_ON) {
Serial
.print(
" 0x"
);
Serial
.print(reply[idx], HEX);}
687
#endif
688
if
((idx == 0) && (reply[0] != (FINGER_STARTCODE >> 8)))
continue
;
689
idx++;
690
691
// check packet!
692
if
(idx >= 9) {
693
if
((reply[0] != (FINGER_STARTCODE >> 8)) ||
694
(reply[1] != (FINGER_STARTCODE & 0xFF)))
695
return
FINGER_BADPACKET;
696
uint8_t packettype = reply[6];
697
//Serial.print("Packet type"); Serial.println(packettype);
698
uint16_t len = reply[7];
699
len <<= 8;
700
len |= reply[8];
701
len -= 2;
702
//Serial.print("Packet len"); Serial.println(len);
703
if
(idx <= (len + 10))
continue
;
704
packet[0] = packettype;
705
for
(uint8_t i = 0; i < len; i++) {
706
packet[1 + i] = reply[9 + i];
707
}
708
#ifdef DEBUG_R503_READ_ECHO
709
if
(Echo_ON) {
Serial
.println();}
710
#endif
711
return
len;
712
}
713
}
714
}
715
716
717
718
// -----------------------------------------------------------
719
void
Finger_R503::powerOn (
void
) {
720
digitalWrite(powerPin, HIGH);
721
#ifdef DEBUG_R503
722
Serial
.println(
"Power - On"
);
723
#endif
724
begin();
725
if
(!verifyPassword()) {
726
#ifdef DEBUG_R503
727
Serial
.println(
"Not sensor"
);
728
#endif
729
powerOff();
// при неудачной попытке включения питания переходим в спящий режим
730
}
else
{
731
}
732
733
}
734
735
// -----------------------------------------------------------
736
void
Finger_R503::powerOff (
void
) {
737
set_mode (FINGER_MODE_SLEEP);
// при выключении питания в любом случае переходим в спящий режим
738
if
(hwSerial) hwSerial->end();
739
#ifdef __AVR__
740
if
(swSerial) swSerial->end();
741
#endif
742
digitalWrite(powerPin, LOW);
743
#ifdef DEBUG_R503
744
Serial
.print(powerPin);
745
Serial
.print(
">"
);
746
Serial
.println(
"Power - Off"
);
747
#endif
748
749
}
750
751
// -----------------------------------------------------------
752
void
Finger_R503::begin(
void
) {
753
delay(TIME_PAUSE_P);
// delay to let the sensor 'boot up'
754
755
if
(hwSerial) hwSerial->begin(baudrate, SERIAL_8N2);
756
#ifdef __AVR__
757
if
(swSerial) swSerial->begin(baudrate);
758
#endif
759
}
760
761
762
// -----------------------------------------------------------
763
uint8_t Finger_R503::get_mode (
void
) {
764
return
mode;
765
}
766
767
768
// -----------------------------------------------------------
769
void
Finger_R503::set_stepMode (
int
new_stepMode) {
770
if
(stepMode != new_stepMode) {
771
FirstStep =
true
;
772
#ifdef DEBUG_R503
773
Serial
.print(
"step: "
);
774
Serial
.print(stepMode);
775
Serial
.print(
" > "
);
776
Serial
.println(new_stepMode);
777
#endif
778
}
779
stepMode = new_stepMode;
780
}
781
782
// -----------------------------------------------------------
783
int
Finger_R503::get_stepMode (
void
){
784
return
stepMode;
785
}
786
787
// -----------------------------------------------------------
788
void
Finger_R503::set_mode (uint8_t newMode) {
789
790
if
(newMode == FINGER_MODE_SLEEP) {
791
if
(mode != FINGER_MODE_SLEEP) {
792
mode = newMode;
793
set_stepMode (0);
794
FirstStep =
true
;
795
#ifdef DEBUG_R503
796
Serial
.println(
"SLEEP"
);
797
#endif
798
powerOff();
799
}
800
}
else
if
(newMode == FINGER_MODE_ADD) {
801
if
(mode != FINGER_MODE_ADD) {
802
if
(mode == FINGER_MODE_SLEEP) powerOn();
803
mode = newMode;
804
Num = 1;
805
set_stepMode (0);
806
FirstStep =
true
;
807
time_loop_mode = millis();
808
#ifdef DEBUG_R503
809
Serial
.println(
"ADD"
);
810
#endif
811
}
812
}
else
if
(newMode == FINGER_MODE_ADD_A) {
813
if
(mode != FINGER_MODE_ADD_A) {
814
if
(mode == FINGER_MODE_SLEEP) powerOn();
815
mode = newMode;
816
Num = 1;
817
set_stepMode (0);
818
FirstStep =
true
;
819
time_loop_mode = millis();
820
#ifdef DEBUG_R503
821
Serial
.println(
"ADD+"
);
822
#endif
823
}
824
}
else
if
(newMode == FINGER_MODE_CLEAR) {
825
if
(mode != FINGER_MODE_CLEAR) {
826
if
(mode == FINGER_MODE_SLEEP) powerOn();
827
mode = newMode;
828
set_stepMode (0);
829
FirstStep =
true
;
830
time_loop_mode = millis();
831
#ifdef DEBUG_R503
832
Serial
.println(
"CLEAR"
);
833
#endif
834
}
835
}
else
if
(newMode == FINGER_MODE_CLEAR_A) {
836
if
(mode != FINGER_MODE_CLEAR_A) {
837
if
(mode == FINGER_MODE_SLEEP) powerOn();
838
mode = newMode;
839
set_stepMode (0);
840
FirstStep =
true
;
841
time_loop_mode = millis();
842
#ifdef DEBUG_R503
843
Serial
.println(
"CLEAR+"
);
844
#endif
845
}
846
}
else
{
847
if
(mode != FINGER_MODE_SCAN) {
848
if
(mode == FINGER_MODE_SLEEP) powerOn();
849
mode = FINGER_MODE_SCAN;
850
set_stepMode (0);
851
FirstStep =
true
;
852
time_loop_mode = millis();
853
#ifdef DEBUG_R503
854
Serial
.println(
"SCAN"
);
855
#endif
856
}
857
}
858
}
859
860
// -----------------------------------------------------------
861
void
Finger_R503::init (
void
) {
862
pinMode(powerPin, OUTPUT);
863
digitalWrite(powerPin, LOW);
864
pinMode(wakerPin, INPUT);
865
digitalWrite(powerPin, HIGH);
866
}
файл GROW_R503.h
001
/***************************************************
002
Объект для работы со сканером отпечатка пальцев GROW R503
003
004
Создан на основе библиотеки Adafruit_Fingerprint
005
006
Общий алгоритм работы
007
stepMode=0 - ничего не делается, состояние инициализации
008
1. Режим SCAN
009
stepMode=1 - LedConfig, включить синий цвет, означает "готов"
010
getImage, сканируем палец
011
stepMode=2 - image2Tz, обрабатываем скан пальца
012
stepMode=3 - Search, ищем отпечаток в базе
013
stepMode=100 - LedConfig, включить фиолетовый цвет, означает "палец распознан"
014
pause, ожидание для открытия замка, этот статус должен обрабатыватся в основном цикле
015
stepMode=101 - LedConfig, включить красный цвет, означает "ошибка"
016
pause, ожидание, ничего не происходит просто показываем красную подсветку
017
2. Режим ADD подтверждение режима (библиотека не пустая)
018
stepMode=10 - TempleteNum, найдем номер в библиотеке
019
stepMode=11 - LedConfig, включить моргание фиолетовым цветом, означает "готов"
020
getImage, сканируем палец дяя подтверждения возможности добавления (только если библиотека не пустая)
021
stepMode=12 - image2Tz, обрабатываем скан пальца
022
stepMode=13 - Search, ищем отпечаток в базе
023
stepMode=102 - LedConfig, включить красный цвет, означает "ошибка подтверждения"
024
pause, ожидание, ничего не происходит просто показываем красную подсветку
025
3. Режим ADD ввод новых сканов в библиотеку
026
// 1
027
stepMode=20 - LedConfig, включить фиолетовый цвет, означает "готов"
028
getImage, сканируем палец
029
stepMode=21 - image2Tz, обрабатываем скан пальца
030
stepMode=22 - Search, ищем отпечаток в базе
031
stepMode=103 - LedConfig, включить синий цвет, означает "вариант пальца принят"
032
pause, ожидание, ничего не происходит просто показываем синюю подсветку
033
stepMode=104 - LedConfig, включить красный цвет, означает "ошибка или палец уже в базе"
034
pause, ожидание, ничего не происходит просто показываем красную подсветку
035
// 2
036
stepMode=30 - LedConfig, включить фиолетовый цвет, означает "готов"
037
getImage, сканируем палец
038
stepMode=31 - image2Tz, обрабатываем скан пальца
039
stepMode=32 - Search, ищем отпечаток в базе
040
041
// 3
042
stepMode=40 - LedConfig, включить фиолетовый цвет, означает "готов"
043
getImage, сканируем палец
044
stepMode=41 - image2Tz, обрабатываем скан пальца
045
stepMode=42 - Search, ищем отпечаток в базе
046
047
// 4
048
stepMode=50 - LedConfig, включить фиолетовый цвет, означает "готов"
049
getImage, сканируем палец
050
stepMode=51 - image2Tz, обрабатываем скан пальца
051
stepMode=52 - Search, ищем отпечаток в базе
052
053
// 5
054
stepMode=50 - LedConfig, включить фиолетовый цвет, означает "готов"
055
getImage, сканируем палец
056
stepMode=51 - image2Tz, обрабатываем скан пальца
057
stepMode=52 - Search, ищем отпечаток в базе
058
059
// ----
060
stepMode=60 - LedConfig, моргание фиолетовым цветом, означает "пальцы сосканированы идет расчет"
061
RegModel, создаем модель отпечатка
062
stepMode=61 - Store, сохраняем модель отпечатка в библиотеке
063
064
stepMode=105 - LedConfig, включить красный цвет, означает "ошибка сборки модели"
065
pause, ожидание, ничего не происходит просто показываем красную подсветку
066
067
4. Режим CLEAR подтверждение очистки
068
stepMode=90 - LedConfig, включить моргание красным цветом, означает "готов"
069
getImage, сканируем палец дяя подтверждения возможности добавления (только если библиотека не пустая)
070
stepMode=91 - image2Tz, обрабатываем скан пальца
071
stepMode=92 - Search, ищем отпечаток в базе
072
073
stepMode=106 - LedConfig, включить красный цвет, означает "ошибка подтверждения"
074
pause, ожидание, ничего не происходит просто показываем красную подсветку
075
076
stepMode=107 - LedConfig, включить фиолетовый цвет, означает "очистка завершена"
077
pause, ожидание, ничего не происходит просто показываем фиолетовую подсветку
078
079
080
****************************************************/
081
082
#ifndef GROW_R503_H
083
#define GROW_R503_H
084
085
#include "Run.h"
086
#include "Arduino.h"
087
#ifdef __AVR__
088
#include
089
#endif
090
091
092
#define TIME_PAUSE_W 50 // пауза включения waker, для избежания ложных срабатываний,
093
#define TIME_PAUSE_P 500 // пауза включения порта (до начала приема команд), минимально по документации 200
094
#define TIME_RUN 30000 // время работы по сигналу waker, потом идет выключение
095
096
097
#define FINGER_LED_1 0x01 // мерцающий
098
#define FINGER_LED_2 0x02 // мигающий
099
#define FINGER_LED_ON 0x03 // включить
100
#define FINGER_LED_OFF 0x04 // выключить
101
#define FINGER_LED_ON_L 0x05 // включить плавно
102
#define FINGER_LED_OFF_L 0x06 // выключить плавно
103
104
#define FINGER_COLOR_RED 0x01 //
105
#define FINGER_COLOR_BLUE 0x02 //
106
#define FINGER_COLOR_PURLE 0x03 //
107
108
#define FINGER_MODE_SLEEP 0x00 // режим сна
109
#define FINGER_MODE_SCAN 0x01 // режим скана
110
#define FINGER_MODE_ADD 0x02 // режим добавления отпечатков
111
#define FINGER_MODE_ADD_A 0x03 // режим авторизации добавления отпечатков
112
#define FINGER_MODE_CLEAR 0x04 // режим сброса всех отпечатков
113
#define FINGER_MODE_CLEAR_A 0x05// режим авторизации для сброса всех отпечатков
114
115
116
117
#define FINGER_STARTCODE 0xEF01
118
#define FINGER_COMMANDPACKET 0x1
119
#define FINGER_DATAPACKET 0x2
120
#define FINGER_ACKPACKET 0x7
121
#define FINGER_ENDDATAPACKET 0x8
122
123
124
125
#define FINGER_OK 0x00
126
#define FINGER_PACKETRECIEVEERR 0x01
127
#define FINGER_NOFINGER 0x02
128
#define FINGER_IMAGEFAIL 0x03
129
#define FINGER_IMAGEMESS 0x06
130
#define FINGER_FEATUREFAIL 0x07
131
#define FINGER_NOMATCH 0x08
132
#define FINGER_NOTFOUND 0x09
133
#define FINGER_ENROLLMISMATCH 0x0A
134
#define FINGER_BADLOCATION 0x0B
135
#define FINGER_DBRANGEFAIL 0x0C
136
#define FINGER_UPLOADFEATUREFAIL 0x0D
137
#define FINGER_PACKETRESPONSEFAIL 0x0E
138
#define FINGER_UPLOADFAIL 0x0F
139
#define FINGER_DELETEFAIL 0x10
140
#define FINGER_DBCLEARFAIL 0x11
141
#define FINGER_PASSFAIL 0x13
142
#define FINGER_INVALIDIMAGE 0x15
143
#define FINGER_FLASHERR 0x18
144
#define FINGER_INVALIDREG 0x1A
145
#define FINGER_ADDRCODE 0x20
146
#define FINGER_PASSVERIFY 0x21
147
148
149
150
#define FINGER_TIMEOUT 0xFF
151
#define FINGER_BADPACKET 0xFE
152
153
#define FINGER_REGMODEL 0x05
154
#define FINGER_STORE 0x06
155
#define FINGER_LOAD 0x07
156
#define FINGER_UPLOAD 0x08
157
#define FINGER_DELETE 0x0C
158
#define FINGER_EMPTY 0x0D
159
#define FINGER_VERIFYPASSWORD 0x13
160
#define FINGER_TEMPLATECOUNT 0x1D
161
162
#define DEFAULTTIMEOUT 5000 // milliseconds
163
164
165
class
Finger_R503 {
166
public
:
167
168
uint16_t fingerID, confidence, templateCount;
169
170
171
#ifdef __AVR__
172
Finger_R503(
int
wPin,
int
pPin,SoftwareSerial *);
173
#endif
174
Finger_R503(
int
wPin,
int
pPin,HardwareSerial *);
175
176
uint8_t get_mode (
void
);
// возвращает текущий режим
177
void
set_mode (uint8_t newMode);
178
void
set_stepMode (
int
new_stepMode);
179
int
get_stepMode (
void
);
180
181
void
init (
void
);
// должна вызыватся из setup, устанавливает параметры ввода вывода контроллера
182
void
poll (unsigned
long
time_loop);
// должна вызыватся из loop, отрабатывает сигнал WAKEUP
183
void
RunStep (unsigned
long
time_loop);
// должна вызыватся из loop, отрабатывает состояния Step
184
void
begin (
void
);
185
186
boolean LedConfig (uint8_t Ctrl, uint8_t Speed, uint8_t Color, uint8_t Count);
187
boolean HandShake (
void
);
188
uint8_t getImage (
void
);
189
uint8_t TempleteNum (
void
);
190
uint8_t image2Tz (uint8_t slot = 1);
191
uint16_t Search (uint8_t slot);
192
uint8_t GetFreeIndex (uint8_t page = 0);
193
boolean verifyPassword (
void
);
194
uint8_t RegModel (
void
);
195
uint8_t store (uint16_t id);
196
uint8_t emptyDatabase (
void
);
197
198
void
writePacket (uint32_t addr, uint8_t packettype, uint16_t len, uint8_t *packet);
199
uint8_t getReply (uint8_t packet[], uint8_t id_packet, uint16_t timeout=DEFAULTTIMEOUT);
200
201
202
private
:
203
204
int
wakerPin = 0;
205
int
powerPin = 0;
206
int
Num = 1;
207
boolean FirstStep =
false
;
208
uint16_t baudrate = 57600;
209
210
unsigned
long
time_loop_mode = 0;
// время начала работы во временном режиме
211
unsigned
long
time_loop_step = 0;
// время начала паузы по степу
212
213
uint8_t mode = FINGER_MODE_SCAN;
// текущий режим,
214
215
int
stepMode = 0;
// текущий шаг для режима (-1 для ошибок)
216
uint8_t leteNum = 0;
// пустой индекс библиотеки отпечатков
217
uint32_t thePassword;
218
uint32_t theAddress;
219
220
Stream *mySerial;
221
#ifdef __AVR__
222
SoftwareSerial *swSerial;
223
#endif
224
HardwareSerial *hwSerial;
225
226
void
powerOn (
void
);
227
void
powerOff (
void
);
228
229
230
231
};
232
233
234
235
#endif
файл Run.cpp
01
// *************************************************************************************************
02
// процедура сравнивает два времени и возвращает разницу в виде числа, учитывает переход времени через 0
03
// start_time - начальное время
04
// end_time - конечное время
05
//
06
// !!!! процедура чуствительна к разрядности исполняемого кода !!!!
07
// !!!! процедура может работать неправильно при двойном переходе времени через 0 !!!!
08
// *************************************************************************************************
09
unsigned
long
getDelayTime(unsigned
long
start_time, unsigned
long
end_time){
10
unsigned
long
result;
11
if
(start_time <= end_time) {
12
result = end_time - start_time;
13
}
14
else
{
15
result = 4294967295 - end_time + start_time;
16
}
17
return
result;
18
}
файл Run.h
01
#ifndef RUN_H
02
#define RUN_H
03
04
05
// ------------------------------------------------------------
06
// Описания функций используемых в объектах и в проекте
07
unsigned
long
getDelayTime(unsigned
long
start_time, unsigned
long
end_time);
08
09
10
#endif
файл other.ino
01
// ---------------------------------------------------------
02
// Дополнительные процедуры
03
//
04
// ---------------------------------------------------------
05
06
07
08
// -----------------------------------------------------------
09
void
poll(unsigned
long
time_loop) {
10
11
boolean buttonState = digitalRead(pinKey);
12
13
if
(!buttonState) {
14
// кнопка отжата, обновим время и статус
15
time_loop_key = time_loop;
16
status_key =
false
;
17
}
else
if
(!status_key) {
18
// кнопка нажата, но это первый цикл
19
time_loop_key = time_loop;
20
status_key =
true
;
21
}
else
if
(getDelayTime(time_loop_key, time_loop) > pauseKey) {
22
// кнопка зажата давно, изменим режим и начнем считать заново
23
time_loop_key = time_loop;
24
status_key =
false
;
25
26
#ifdef GROW_R503
27
if
(finger.get_mode() == FINGER_MODE_SLEEP) {
28
finger.set_mode(FINGER_MODE_ADD_A);
29
}
else
if
(finger.get_mode() == FINGER_MODE_SCAN) {
30
finger.set_mode(FINGER_MODE_ADD_A);
31
}
else
if
(finger.get_mode() == FINGER_MODE_ADD) {
32
finger.set_mode(FINGER_MODE_CLEAR_A);
33
}
else
if
(finger.get_mode() == FINGER_MODE_ADD_A) {
34
finger.set_mode(FINGER_MODE_CLEAR_A);
35
}
else
{
36
finger.set_mode(FINGER_MODE_SLEEP);
37
}
38
39
#endif
40
41
}
42
43
44
#ifdef GROW_R503
45
if
(finger.get_stepMode() == 100) {
46
if
(!status_open) {
47
digitalWrite(pinOpen, HIGH);
48
status_open =
true
;
49
Serial
.println(
"=== DOOR OPEN ==="
);
50
}
51
}
else
{
52
if
(status_open) {
53
digitalWrite(pinOpen, LOW);
54
status_open =
false
;
55
Serial
.println(
"=== DOOR CLOSE ==="
);
56
}
57
}
58
#endif
59
60
}
У меня на мобиле только чистые пальцы распознаются. Стоит только мелким ремонтом (с наличием пыли) позаниматься - все, начинаются капризы. Мол пальцы не те...
Вот и я ж про то же. А на улице пальцы то замёрзли, то увлажнились, то ещё чего. Потому и спрашиваю про плохую погоду.
Сканер отпечатков в условиях улицы - ИМХО это баловство. Либо надо его весить в сухом и теплом месте.
У нас в офисе я ставил заводской сканер отпечатков на входной двери. Работает нормально, если руки сухие. Чуть только немного влажные - все, не открывает. Ну и плюс нужно ручное дублирование открывания замка. Пару раз замок глючил, если бы он с ключа не открывался - куча народа оказалось бы запертыми в офисе.
В детском саду у нас около года на калитках (на улице) стоят примерно вот такие замки
Я такой замок тоже настраивал и устанавливал. У него есть возможность привязать брелок (как у домофона), код менять можно хоть каждый день. Единственный минус - от частого использования некоторые цифры на кнопках "облезли". И на одной панели перегорела подсветка одной из клавиш. Настроить можно различные типы замков, и еще есть куча настроек. Цена замка в районе 1-2 тыс. руб. и выглядит вполне прилично. Если прикинуть, что его каждый день открывают по несколько сотен (если не тысяч) раз - вполне достойный вариант. И ключи таскать не надо.
Есть еще замки в антивандальном исполнении, типа таких


они на вид попрочнее, программируются тоже легко. Только вроде брелки к ним не привязываются.
Если решите все-таки ставить отпечаток пальцев - отпишитесь о полевых испытаниях.
ставить на улицу буду в выходные (если дождя не будет),
пока провел следующие испытания
1. сильно потер пальцы (до красноты), эмитация физ нагрузки - все ОК
2. подложил под палец 2 нитки, эмитация мусора (пробовал под разными положениями) - все ОК
3. втер в палец цемент, эмитация грязных рук - все ОК
4. сильно намочил палец - НЕ работает
5. сухой палец и влажный датчик (из пульеверизатора, аналог росы) если не слегка влажно - все ОК, если прямо каплями - НЕ работает
6. через тонкий слой пленки - вообще не срабатывает датчик касания
Поставил на улицу, правда не до конца (но об этом чуть ниже)
На улице 0 градусов, влажно и что-то моросит, короче мерзость, провозился около 3х часов сам замерз, но точно могу сказать датчик то-же замерз :)
Итого - сам сканер работает почти так-же как дома, чуть хуже стал работать датчик наличия пальца (контакт WakeUp), но распознавание и поиск работаю хорошо.
Сами пальцы от мороза то же "потеряли форму", и стали давать примерно 20% ошибок, но дополнительное "дообучение" (повторил обучение и он добавил по 1 модели на палец), в результате правильное распознование более примерно 95%
теперь о том что я не доделал, и тут нужна помощь сообщества
Короче у меня есть явная проблема в электрической схеме, я не зря запаял джампер "test", без него вся схема работает нормально, но само собой замок не открывается.
Когда я его ставлю у меня проседает напряжение на датчике и он перестает работать,
Средний провод разьема отмеченный как "Key" идет из вызывной панели и на нем постоянные +4,5v, это провод должен идти на внутренний выключатель "Открыть замок".
То есть если замкнуть "Key" на землю, то замок открывается.
Как правильно реализовать такое замыкание? понятно, что можно поставить реле, но очень не хочется...
Средний провод разьема отмеченный как "Key" идет из вызывной панели и на нем постоянные +4,5v, это провод должен идти на внутренний выключатель "Открыть замок".
То есть если замкнуть "Key" на землю, то замок открывается.
Вы точно уверены, что логика именно такова? У меня вот всё по другому. Открыванием занимается сама вызывная панель по сигналу с домофона. Она (панель) замыкает питание на соленоид замка. А когда она ничего не замыкает, то мультиметр показывает на проводе те же 12В, что и должны быть при открывании за счёт тока утечки ключа.
Средний провод разьема отмеченный как "Key" идет из вызывной панели и на нем постоянные +4,5v, это провод должен идти на внутренний выключатель "Открыть замок".
То есть если замкнуть "Key" на землю, то замок открывается.
Вы точно уверены, что логика именно такова? У меня вот всё по другому. Открыванием занимается сама вызывная панель по сигналу с домофона. Она (панель) замыкает питание на соленоид замка. А когда она ничего не замыкает, то мультиметр показывает на проводе те же 12В, что и должны быть при открывании за счёт тока утечки ключа.
Уверен, вот схема
кроме того если я замыкаю этот контакт на землю действительно замок открывается
Вот как установил сам датчик, по идее он закрыт от ветра и дождя (с верху над калиткой навес)
Все теперь у меня работает. Была проблема с оптроном, он банально не рабочий был.
заодно поменял резистр на 10к (можно и меньше, но учитывая, что мне не требуется яркость и тот факт, что этот идет с чужого устройства решил оставить 10к), и поставил светодиод для визуального контроля.
Исправил несколько багов в программе.
схему и программу в 1 посте обновил до актуального состояния.
Теперь, что касается субьективного ощущения (на улице -2 и мелкий снег)
1. на разьем 3.3vt лучше подавать более высокое напряжение чем 3.3, от этого улучшается "отзывчивость", по той документации которая у меня есть, допустимо до 6 вольт, у меня сейчас не смотря на стабилитрон (на 3.3) реально 4.3 вольта, по чему - не понимаю, когда питание было от USB было ровно 3.3, а сейчас 4.3, но я включал и напрямую на +5, отзывчивость была выше.
2. есть эффект "разогрева", возможно при -25 будут проблемы, будем ждать морозов
3. Обучение надо периодически повторять, при этом добавляются модели на "скукоженные" пальцы
4. ощущается некая "тормознутость" по сравнению например с телефоном, причина в том, что питание на датчик подается только после обнаружения касания, то есть расходуется время на определение касания+запуск подуля+сканирование+распознавание+поиск в базе. На холодную это все примерно 2...3 сек, при повторном скане 1..2 сек
Прошла неделя, за это время был и снегопад и ледяной дождь. В целом все работает хорошо. Для датчика единственно чего ему мешает - это когда он мокрый, все остальное работает. Проверка проходит на 4х членах семьи, возраст от 8 до 50 лет, полет хороший.
сегодня наконец запихнул платы во временную коробочку, когда будет тепло надо будет переделать с макеток на нормальную плату и сделать коробочку по размеру побольше.
у меня сейчас такая https://www.stroyportal.ru/catalog/section-korobki-montazhnye-2668/korobka-raspayachnaya-80h80h25-otkrytoy-provodki-b-684306313/ но висит на видном месте, по этому надежную и страшную не хочется.
сейчас из бутылки феном сделал сверху колпак, то есть сейчас ничего не промокнет, но во первых нужно отверстие для кнопки обучения, наверно нужно светодиод вывести.
Короче надо будет весной посмотреть, сейчас все равно этим заниматься холодно, к весне может 3д принтер куплю и напечатаю или подберу чего...
Ну, для видного места я сам на принтере напечатал такую, что сверху у неё вообще никаких щелей нет от слова совсем, т.е. никакой дождь и снег ей не страшны. Там у меня профильная труба 40х25, вот я и сделал коробку для клемм в аккурат шириной 25мм, чтобы как родная стала. Могу показать, если интересно.
Всем кого интересовали полевые испытания:
Пришлось третий раз проводить обучение, видимо пальцы меняют геометрию от множества параметров. При чем у меня сильнее хуже чем у остальных принимаются, у детей вообще замечательо у меня иногда с десятого раза... После дообучения все хорошо, но не надолго, примерно на месяц хватает.
наверно буду проект дорабатывать, поставлю sd карту и заложу программу самообучение, на основе принятых отпечатков буду строить варианты с теми которые не прошли, по идее те которые совсем чужие не смогут объединится в пару.
Короче если до НГ придет блок питания для сверлильного станка (3 посылки зависли на почте) доделаю его, если не придет займусь этой доработкой...
Прошла зима.
Разобрал макетку, вытравил плату, перепаял, залачил цапон лаком, на плату нанес технические надписи. Внес небольшие изменения, в основном в схему питания.
Исправил в коде 1 серьезную ошибку (переполнение буфера) и немного улучшил стабильность и скорость.
Напечатал нормальный корпус.
Все работает, ничего переделывать больше не планирую.
РЕЗЮМЕ: в целом данный датчик вполне подходит для средней полосы для работы на улице без прямого попадания на него осадков (под козырьком)
зы
в 1м посту обновил и схему и код
Скажите пожалуйста как вы это все прошивали? через Atom? ардуино IDE ругается...
Скажите пожалуйста как вы это все прошивали? через Atom? ардуино IDE ругается...
шил через IDE
я в коментах всегда пишу версию на чем шил - версия среды Arduino 1.8.14
А не могли бы Вы скинуть все файлы в архиве? Мб я что-то не так перенес из кода.
какую ошибку выдает?
Ну можно ж сделать намного проще, штош вы.
Звоните своей ардуине со своего номера. Ардуина сверяет входящий номер. Если он true, система сбрасывает звонок и открывает/снимает с охраны. Никакого внешнего контакта, никаких чужих.
Не забываем про watchdog
Была открыта IDE от Flprog. Поставил отдельно IDE, установил библиотеку и все равно такая же ошибка...
Была открыта IDE от Flprog. Поставил отдельно IDE, установил библиотеку и все равно такая же ошибка...
это проблема не скеча а установки IDE, заведи новую тему (в соответствующем разделе) с описанием ошибки, народ поможет, я спать ушел :)
Звоните своей ардуине со своего номера. Ардуина сверяет входящий номер. Если он true, система сбрасывает звонок и открывает/снимает с охраны. Никакого внешнего контакта, никаких чужих.
Не забываем про watchdog
Не понимаю, чем Ваш вариант проще:
1. Нужно достать телефон, набрать номер, дождаться ответа. Это всё секунд 10-15 минимум. Отпечаток пальца всегда под рукой в прямом и переносном смысле слова.
2. Существуют сервисы подмены номеров, любой может открыть дверь, зная Ваш номер. Способов узнать номер можно придумать кучу.
3. Телефон абонента может быть временно недоступен. При отсутствии движения по счету месяца через три оператор блокирует симку.
1. Нужно достать телефон, набрать номер, дождаться ответа. Это всё секунд 10-15 минимум. Отпечаток пальца всегда под рукой в прямом и переносном смысле слова.
2. Существуют сервисы подмены номеров, любой может открыть дверь, зная Ваш номер. Способов узнать номер можно придумать кучу.
3. Телефон абонента может быть временно недоступен. При отсутствии движения по счету месяца через три оператор блокирует симку.
Да, мой вариант дольше, но безопаснее и без контакта с погодными условиями.
2.От того, что вы подмените номер, вы совершенно не можете знать, на каком номере висит охранка. Куда будете звонит с подменного номера-то?
С телефоном, когда то делал следующий доступ и он до сих пор работает (используя то, что у всех операторов связи по их закону, 3 секунды соединения всегда бесплатны для людей) .
1. Подойдя к двери, человек звонит на номер для открытия 2. Контроллер, определив номер, кладет трубку. 3. Найдя номер в базе доступа, перезванивает на номер человека. 4. Если действительно человек хочет пройти в дверь, он поднимает трубку. 4. контроллер видя, что подняли трубку, ее кладет и открывает дверь. 5. Если человек дает отбой (сам кладет трубку), то означает, что это не он хочет пройти. Да, вот этот алгоритм занимал примерно 5-7 секунд. Но он был надежный и работает годами бесплатно на безлимитных тарифах опсосов. Потому как они блокируют симку, если нет исходящих/входящих звонков а не движения по счету. Может уже что то поменялось у них но раньше было так.
И в данном случае, ни какой подмены номеров не прокатит, потому как звонок контроллера будет прямым. Ну и я думаю безопастникам будет повод задуматься о возможной попытке проникновения.
С телефоном, когда то делал следующий доступ и он до сих пор работает (используя то, что у всех операторов связи по их закону, 3 секунды соединения всегда бесплатны для людей) .
Все варианты с телефоном требуют наличие этого самого телефона, живя в деревне Вы бы знали, что наличие телефона не всегда возможно, а при наличии детей тем более.
Кроме того если уж привязыватся к физическому устройству, то не к телефона а к таблетке/брелку это однозначно и проще и быстрее и надежнее.
Лично я за биометрию, если Вы хотите привязку к внешнему устройству - Вам никто не запрещает...
может проще архивом скетч выложить?
может проще архивом скетч выложить?
мне тут zip не дает сохранять в своих файлах, а выкладывать на левые ресурсы не хорошо, он сдохнет через какое-то время
мне тут zip не дает сохранять в своих файлах, а выкладывать на левые ресурсы не хорошо, он сдохнет через какое-то время
скинь на мыло ua6em собака orviss точка ru
и мне бы на мыло не могли бы скинуть? kesha.ncuxxx@gmail.com
скинул на 3 ящика.
в папке "схемы" 3d модели корпуса, плата, схема, и официальная pdf по датчику
Спасибо огромное, вот теперь скетч компилится без ошибок.
Спасибо огромное, вот теперь скетч компилится без ошибок.
да в первом посту строка 31 (файл R503.ino) обрезалась, должно быть
31
#include <SoftwareSerial.h>
строки 11,12 (файл GROW_R503.cpp) обрезалась, должно быть
11
#include <util/delay.h>
12
#include <SoftwareSerial.h>
строка 88 (файл GROW_R503.h) обрезалась, должно быть
88
#include <SoftwareSerial.h>
короче обрезались символы больше, меньше.