Автоматическая камера хранения на 24 ячейки + сканер отпечатка + купюроприемник (разработка с нуля)

u163
Offline
Зарегистрирован: 07.04.2014

Оцените пожалуйста масштабы задачи и мое понимание реализации, а также, разумеется, озвучьте примерную стоимость написания программной части. Необходима всесторонняя поддержка, так как знаний в аппаратной/программной части, к сожалению, не хватает. Буду рад услышать как советы так и критику. В идеале, получить бы краткий вводный курс и скетч с комментариями. Т.е. хотелось не просто собрать, но и понять как оно работает.

Исходный список "железа" (обсуждаемо):

1. Arduino (Uno имеется в наличии)

2. Ethernet shield/GPRS shield (для возможности удаленного мониторинга, обсуждаемо)

3. RTC модуль (например DS1307)

4. Fingerprint scaner FPM10A - описание полного аналога можно найти здесь: http://www.adafruit.com/datasheets/ZFM%20user%20manualV15.pdf и саму железку тут http://www.aliexpress.com/item/Free-Shipping-Fingerprint-Recognition-Module-FPM10A-Optical-fingerprint-Fingerprint-Module-For-Arduino-in-stock-Best-quality/1663157893.html Модуль имеется в наличии, готовые библиотеки есть на Adafuit, хранит отпечатки в своей энергонезависимой памяти, в режиме распознавания возвращает номер ячейки памяти в которой хранится отпечаток.

5. SD Card модуль (для оперативных данных, например времени на балансе ячейки хранения и, возможно, отчетности)

6. Сдвиговые регистры (например MCP23017)

7. Блоки реле 2х16 для управления замками. Например http://www.aliexpress.com/item/Free-shipping-12V-16-Channel-Relay-Module-Interface-Board-For-Arduino-PIC-ARM-DSP-PLC-With/1834575608.html

8. Замки с обратной связью (датчик открытия). Например http://www.aliexpress.com/store/product/Electric-Cabinet-Lock-MA1208S/1018446_1400342102.html

9. ЖК дисплей

10. Купюроприемник (как я понимаю нужен поддерживающий Pulse, тут необходим совет по выбору). Возможность принимать 2 номинала (1 и 5 у.е.). Выдача сдачи не подразумевается. Вот например http://www.aliexpress.com/item/Bill-acceptor-or-note-acceptors-with-MDB-or-RS232-or-Pulse-or-CCtalk-interface/688126227.html

11. Buzzer сигнализирующий о незакрытой двери.

// Интерфейс пользователя:

- Сканер отпечатка

- Дисплей с информацией о текущем системном времени, загруженной сумме и времени на балансе

- Клавиша "1" вызова режима постановки на хранение

- Клавиша "2" вызова режима временного открытия

- Клавиша "3" вызова режима изъятия

// Алгоритм работы:

- Нажимается клавиша "1"

- Вставляется купюра (купюры), согласно сумме на экране отображается время хранения (к примеру 1 у.е. = 1 час, 2 у.е. = 2 часа, 3 у.е. = 3 часа, итд)

- Включается fingerprint, прикладывается палец (дважды, как этого требует FPM10A) Желателен вывод подсказки на ЖК-дисплей.

- Открывается первая свободная ячейка

- Срабатывает сигнализация незакрытой двери, пищит до закрытия.

- По закрытию ячейки записывается номер ячейки+текущее время на SD карту и запускается обратный отсчет времени (после достижения 0 счетчик уходит в минус, для изъятия требует ввода недостающего количества купюр)

- Ячейка принимает статус занятой и привязаной к конкретному отпечатку пальца (в интервале оплаченного времени пользователь может открывать ячейку сколько угодно раз при помощи клавиши "2" и верификации отпечатка)

Для изъятия из ячейки:

- Нажимается клавиша "3"

- Проводится верификация отпечатка

- Если не требуется доплата, ячейка открывается

- Отпечаток удаляется из памяти FPM10A

- Ячейка отмечается как свободная (предположительно это запись в файле на SD карте, я не представляю как еще её сделать энергонезависимой и защищенной от случайных сбоев по питанию)

Спасибо за внимание.

Электромыло для связи: avadanliq () gmail.com 

freearduino
Offline
Зарегистрирован: 21.04.2014

Дело в том, что не у каждого будут все комплектующие. Стоимост у них не малая. 

NeiroN
NeiroN аватар
Offline
Зарегистрирован: 15.06.2013

UNO будет маловато для такой задачи - памяти мало.

1. Рекомендовал бы Mega2560+Ethernet Sheild(есть слот microSD) - а интернет вводить с роутера, который или Wi-Fi или GPRS или по проводу.

2. Все что выведено к пользователю замок и датчик в замке - должно быть запитанно от отдельного источника питания и электрически развязано от ардуины - чтобы статичеким электричеством не вызвать выход из строя контроллера и открывание всех ячеек.

u163
Offline
Зарегистрирован: 07.04.2014

freearduino пишет:

Дело в том, что не у каждого будут все комплектующие. Стоимост у них не малая. 

Да, согласен. Тоже думал над этим вопросом. По одним даташитам скетч не напишешь. Пересылка комплектующих туда-обратно тоже не вариант - я не в России. Пока без идей как это провернуть.

NeiroN пишет:

UNO будет маловато для такой задачи - памяти мало.

1. Рекомендовал бы Mega2560+Ethernet Sheild(есть слот microSD) - а интернет вводить с роутера, который или Wi-Fi или GPRS или по проводу.

Спасибо, буду иметь в виду. Тогда Mega2560+Ethernet с microSD, а для интернет-гейта можно взять TP-Link 3020 http://www.tp-link.com/lk/products/details/?model=TL-MR3020. На него всякие WRT накатываются, есть поддержка 3G модемов.

NeiroN пишет:

2. Все что выведено к пользователю замок и датчик в замке - должно быть запитанно от отдельного источника питания и электрически развязано от ардуины - чтобы статичеким электричеством не вызвать выход из строя контроллера и открывание всех ячеек.

Указанные блоки реле судя по всему имеют оптронную развязку, с замками ардуино напрямую не связана, остальные компоненты это кнопки, и сканер отпечатка, у них не будет проводящих поверхностей.

u163
Offline
Зарегистрирован: 07.04.2014

А что если разбить задачу на подзадачи и для начала поговорить о написании скетча для Fingerprint scaner FPM10A и четырёх кнопок. Буду рад вопросам, комментариям, исправлениям и дополнениям.

Дело в том, что Адафрут предоставляет в качестве примера работы с модулем два отдельных скетча - один на регистрацию отпечатков, второй на сверку с сохраненными в памяти модуля. Нужно сделать из них единый скетч, по возможности исключив лишнее и оставив только необходимый функционал. Вот библиотека от Adafruit.com: https://github.com/adafruit/Adafruit-Fingerprint-Sensor-Library. Страница продукта: https://www.adafruit.com/products/751 

Скетчи: 
1) Enroll - регистрация

001/***************************************************
002  This is an example sketch for our optical Fingerprint sensor
003 
004  Designed specifically to work with the Adafruit BMP085 Breakout
006 
007  These displays use TTL Serial to communicate, 2 pins are required to
008  interface
009  Adafruit invests time and resources providing this open source code,
010  please support Adafruit and open-source hardware by purchasing
011  products from Adafruit!
012 
013  Written by Limor Fried/Ladyada for Adafruit Industries. 
014  BSD license, all text above must be included in any redistribution
015 ****************************************************/
016 
017#include <Adafruit_Fingerprint.h>
018#if ARDUINO >= 100
019 #include <SoftwareSerial.h>
020#else
021 #include <NewSoftSerial.h>
022#endif
023 
024uint8_t getFingerprintEnroll(uint8_t id);
025 
026 
027// pin #2 is IN from sensor (GREEN wire)
028// pin #3 is OUT from arduino  (WHITE wire)
029#if ARDUINO >= 100
030SoftwareSerial mySerial(2, 3);
031#else
032NewSoftSerial mySerial(2, 3);
033#endif
034 
035Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
036 
037void setup() 
038{
039  Serial.begin(9600);
040  Serial.println("fingertest");
041 
042  // set the data rate for the sensor serial port
043  finger.begin(57600);
044   
045  if (finger.verifyPassword()) {
046    Serial.println("Found fingerprint sensor!");
047  } else {
048    Serial.println("Did not find fingerprint sensor :(");
049    while (1);
050  }
051}
052 
053void loop()                     // run over and over again
054{
055  Serial.println("Type in the ID # you want to save this finger as...");
056  uint8_t id = 0;
057  while (true) {
058    while (! Serial.available());
059    char c = Serial.read();
060    if (! isdigit(c)) break;
061    id *= 10;
062    id += c - '0';
063  }
064  Serial.print("Enrolling ID #");
065  Serial.println(id);
066   
067  while (!  getFingerprintEnroll(id) );
068}
069 
070uint8_t getFingerprintEnroll(uint8_t id) {
071  uint8_t p = -1;
072  Serial.println("Waiting for valid finger to enroll");
073  while (p != FINGERPRINT_OK) {
074    p = finger.getImage();
075    switch (p) {
076    case FINGERPRINT_OK:
077      Serial.println("Image taken");
078      break;
079    case FINGERPRINT_NOFINGER:
080      Serial.println(".");
081      break;
082    case FINGERPRINT_PACKETRECIEVEERR:
083      Serial.println("Communication error");
084      break;
085    case FINGERPRINT_IMAGEFAIL:
086      Serial.println("Imaging error");
087      break;
088    default:
089      Serial.println("Unknown error");
090      break;
091    }
092  }
093 
094  // OK success!
095 
096  p = finger.image2Tz(1);
097  switch (p) {
098    case FINGERPRINT_OK:
099      Serial.println("Image converted");
100      break;
101    case FINGERPRINT_IMAGEMESS:
102      Serial.println("Image too messy");
103      return p;
104    case FINGERPRINT_PACKETRECIEVEERR:
105      Serial.println("Communication error");
106      return p;
107    case FINGERPRINT_FEATUREFAIL:
108      Serial.println("Could not find fingerprint features");
109      return p;
110    case FINGERPRINT_INVALIDIMAGE:
111      Serial.println("Could not find fingerprint features");
112      return p;
113    default:
114      Serial.println("Unknown error");
115      return p;
116  }
117   
118  Serial.println("Remove finger");
119  delay(2000);
120  p = 0;
121  while (p != FINGERPRINT_NOFINGER) {
122    p = finger.getImage();
123  }
124 
125  p = -1;
126  Serial.println("Place same finger again");
127  while (p != FINGERPRINT_OK) {
128    p = finger.getImage();
129    switch (p) {
130    case FINGERPRINT_OK:
131      Serial.println("Image taken");
132      break;
133    case FINGERPRINT_NOFINGER:
134      Serial.print(".");
135      break;
136    case FINGERPRINT_PACKETRECIEVEERR:
137      Serial.println("Communication error");
138      break;
139    case FINGERPRINT_IMAGEFAIL:
140      Serial.println("Imaging error");
141      break;
142    default:
143      Serial.println("Unknown error");
144      break;
145    }
146  }
147 
148  // OK success!
149 
150  p = finger.image2Tz(2);
151  switch (p) {
152    case FINGERPRINT_OK:
153      Serial.println("Image converted");
154      break;
155    case FINGERPRINT_IMAGEMESS:
156      Serial.println("Image too messy");
157      return p;
158    case FINGERPRINT_PACKETRECIEVEERR:
159      Serial.println("Communication error");
160      return p;
161    case FINGERPRINT_FEATUREFAIL:
162      Serial.println("Could not find fingerprint features");
163      return p;
164    case FINGERPRINT_INVALIDIMAGE:
165      Serial.println("Could not find fingerprint features");
166      return p;
167    default:
168      Serial.println("Unknown error");
169      return p;
170  }
171   
172   
173  // OK converted!
174  p = finger.createModel();
175  if (p == FINGERPRINT_OK) {
176    Serial.println("Prints matched!");
177  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
178    Serial.println("Communication error");
179    return p;
180  } else if (p == FINGERPRINT_ENROLLMISMATCH) {
181    Serial.println("Fingerprints did not match");
182    return p;
183  } else {
184    Serial.println("Unknown error");
185    return p;
186  }  
187   
188  p = finger.storeModel(id);
189  if (p == FINGERPRINT_OK) {
190    Serial.println("Stored!");
191  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
192    Serial.println("Communication error");
193    return p;
194  } else if (p == FINGERPRINT_BADLOCATION) {
195    Serial.println("Could not store in that location");
196    return p;
197  } else if (p == FINGERPRINT_FLASHERR) {
198    Serial.println("Error writing to flash");
199    return p;
200  } else {
201    Serial.println("Unknown error");
202    return p;
203  }  
204}

2) Fingerprint - распознавание

001/***************************************************
002  This is an example sketch for our optical Fingerprint sensor
003 
004  Designed specifically to work with the Adafruit BMP085 Breakout
006 
007  These displays use TTL Serial to communicate, 2 pins are required to
008  interface
009  Adafruit invests time and resources providing this open source code,
010  please support Adafruit and open-source hardware by purchasing
011  products from Adafruit!
012 
013  Written by Limor Fried/Ladyada for Adafruit Industries. 
014  BSD license, all text above must be included in any redistribution
015 ****************************************************/
016 
017 
018#include <Adafruit_Fingerprint.h>
019#if ARDUINO >= 100
020 #include <SoftwareSerial.h>
021#else
022 #include <NewSoftSerial.h>
023#endif
024 
025int getFingerprintIDez();
026 
027// pin #2 is IN from sensor (GREEN wire)
028// pin #3 is OUT from arduino  (WHITE wire)
029#if ARDUINO >= 100
030SoftwareSerial mySerial(2, 3);
031#else
032NewSoftSerial mySerial(2, 3);
033#endif
034 
035Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
036 
037void setup() 
038{
039  Serial.begin(9600);
040  Serial.println("fingertest");
041 
042  // set the data rate for the sensor serial port
043  finger.begin(57600);
044   
045  if (finger.verifyPassword()) {
046    Serial.println("Found fingerprint sensor!");
047  } else {
048    Serial.println("Did not find fingerprint sensor :(");
049    while (1);
050  }
051  Serial.println("Waiting for valid finger...");
052}
053 
054void loop()                     // run over and over again
055{
056  getFingerprintIDez();
057}
058 
059uint8_t getFingerprintID() {
060  uint8_t p = finger.getImage();
061  switch (p) {
062    case FINGERPRINT_OK:
063      Serial.println("Image taken");
064      break;
065    case FINGERPRINT_NOFINGER:
066      Serial.println("No finger detected");
067      return p;
068    case FINGERPRINT_PACKETRECIEVEERR:
069      Serial.println("Communication error");
070      return p;
071    case FINGERPRINT_IMAGEFAIL:
072      Serial.println("Imaging error");
073      return p;
074    default:
075      Serial.println("Unknown error");
076      return p;
077  }
078 
079  // OK success!
080 
081  p = finger.image2Tz();
082  switch (p) {
083    case FINGERPRINT_OK:
084      Serial.println("Image converted");
085      break;
086    case FINGERPRINT_IMAGEMESS:
087      Serial.println("Image too messy");
088      return p;
089    case FINGERPRINT_PACKETRECIEVEERR:
090      Serial.println("Communication error");
091      return p;
092    case FINGERPRINT_FEATUREFAIL:
093      Serial.println("Could not find fingerprint features");
094      return p;
095    case FINGERPRINT_INVALIDIMAGE:
096      Serial.println("Could not find fingerprint features");
097      return p;
098    default:
099      Serial.println("Unknown error");
100      return p;
101  }
102   
103  // OK converted!
104  p = finger.fingerFastSearch();
105  if (p == FINGERPRINT_OK) {
106    Serial.println("Found a print match!");
107  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
108    Serial.println("Communication error");
109    return p;
110  } else if (p == FINGERPRINT_NOTFOUND) {
111    Serial.println("Did not find a match");
112    return p;
113  } else {
114    Serial.println("Unknown error");
115    return p;
116  }  
117   
118  // found a match!
119  Serial.print("Found ID #"); Serial.print(finger.fingerID);
120  Serial.print(" with confidence of "); Serial.println(finger.confidence);
121}
122 
123// returns -1 if failed, otherwise returns ID #
124int getFingerprintIDez() {
125  uint8_t p = finger.getImage();
126  if (p != FINGERPRINT_OK)  return -1;
127 
128  p = finger.image2Tz();
129  if (p != FINGERPRINT_OK)  return -1;
130 
131  p = finger.fingerFastSearch();
132  if (p != FINGERPRINT_OK)  return -1;
133   
134  // found a match!
135  Serial.print("Found ID #"); Serial.print(finger.fingerID);
136  Serial.print(" with confidence of "); Serial.println(finger.confidence);
137  return finger.fingerID;
138}

Как работает "из коробки":

1) Модуль подключается к Ардуине, грузится скетч регистрации, регистрируется отпечаток, с клавиатуры ПК (запись в порт) вводится номер ячейки в которую будет сохранен "образ" отпечатка (изображение никуда не сохраняется, только некая контрольная сумма), после чего эта запись остается в энергонезависимой памяти самого модуля. 
Найденый глюк, выдаваемый за особенность службой поддержки - нельзя ввести номер ячейки без ввода печатного символа (буквы) до номера ячейки.
2) Основной (автономный) режим работы это сверка поступающих со сканера отпечатков и, при совпадении, выдача в порт номера ячейки, при ошибке распознавания - номера ошибки, итд. Для перехода в этот режим необходима загрузка отдельного скетча. При этом постоянно работает подсветка сканера, т.е. устройство ждет палец. Иногда подглючивает, подсветка пропадает на несколько секунд и, видимо, скетч перезагружается, после чего нормально работает снова. Может быть это обнуляется какой-то счетчик в скрипте.
Видео от Адафрут: https://www.youtube.com/watch?v=1diFaa5OsFg

Хотелось бы что бы единый скетч v.:0.1 работал следующим образом:

К Ардуино подключен модуль FPM10A;
На microSD (на Ethernet шилде), лежит текстовый файл cellstatus.txt с где на каждой из 24 строк, соответствующих номеру ячейки, стоит '1', '2', '3', '4' или '5' (статус "свободно", "занято", "открыто", "блокировано" или "прочее");
Подключены 4 кнопки (Положить, Открыть, Изъять, Отмена);
Пищалка для подачи звуковых сигналов (buzzer)

 

А) Не нажата ни одна из кнопок 

- модуль FPM10A отключен, например по питанию (не создаст ли это ошибки в выполнении скетча?), для того, чтобы не горела подсветка сканера 

B) Нажата кнопка 1 (Размещение на хранение):
тут в дальнейшем планируется разместить обработку событий купюроприемника и возврат количества купюр (часов на баланс), отображение информации на LCD
- включается FPM10A 
- скетч построчно читает cellstatus.txt , пишет номер первой строки с '1' в переменную freecell.
- вызывается процедура регистрации, из переменной freecell скетч сообщает модулю номер ячейки в которую следует записать данный отпечаток. 
- после успешной регистрации номер записывается в переменную unlockcell
* тут в дальнейшем планируется разместить вызов функции открытия ячейки (реле замка) с аргументом unlockcell, которая поместит '3' (открыто) на строку № unlockcell в файле cellstatus.txt, попищит buzzer'ом до получения от датчика замка сигнала о закрытии, затем запишет в строку '2' (занято).
тут в дальнейшем планируется разместить функции регистрации текущего времени с модуля RTC в отдельный файл, аналогичный файлу статусов ячеек. Например строки типа от ЧЧ:ММ ДД:ММ:ГГ до ЧЧ:ММ ДД:ММ:ГГ  (+1, +2, +3, итд часов, в зависимости от количества и номинала загруженных купюр)
- скетч выходит в режим "A"

C) Нажата кнопка 2 (Временное открытие):
- включается FPM10A, переключается в режим распознавания
- возвращается в режим "А" если нет ответа от FPM10A в течение 10 секунд
- возвращается в режим "А" если нажата кнопка 4 (Отмена)
- в случае распознания скетч получает от модуля номер ячейки, пишет его в переменную matchcell
* тут в дальнейшем планируется разместить функцию проверки времени на балансе ячейки № matchcell, при необходимости оплаты, вызова функции купюроприемника
- Присваевает unlockcell = matchcell
* тут в дальнейшем планируется разместить вызов функции открытия ячейки (реле замка) с аргументом == unlockcell, которая поместит '3' (открыто) на строку № unlockcell в файле cellstatus.txt, попищит buzzer'ом до получения от датчика замка сигнала о закрытии, затем запишет в строку '2' (занято).
- скетч выходит в режим "A"

D) Нажата кнопка 3 (Изъятие):
- включается FPM10A, переключается в режим распознавания
- возвращается в режим "А" если нет ответа от FPM10A в течение 10 секунд
- возвращается в режим "А" если нажата кнопка 4 (Отмена)
- в случае распознания скетч получает от модуля номер ячейки, пишет его в переменную matchcell
* тут в дальнейшем планируется разместить функцию проверки времени на балансе ячейки № matchcell, при необходимости оплаты, вызова функции купюроприемника
- Присваевает unlockcell = matchcell
* тут в дальнейшем планируется разместить вызов функции открытия ячейки (реле замка) с аргументом == unlockcell, которая поместит '3' (открыто) на строку № unlockcell в файле cellstatus.txt, попищит buzzer'ом до получения от датчика замка сигнала о закрытии, затем запишет в строку '1' (свободно).
- скетч выходит в режим "A"

 

vBoris
Offline
Зарегистрирован: 27.05.2014

Очень интересная тема. Жаль что разработчики к ней остались холодны. 

попробуем разобраться. Ваши два скеча имеют void loop() где и происходит обработка датчика отпечатков

берем код http://arduino.ru/forum/programmirovanie/rabota-s-knopkami-v-pomoshch-novichku

void setup()
02  {
03  
04  pinMode(13, OUTPUT);
05  
06  }
07  
08  void loop()
09  
10  {
11  if(digitalRead(14)==HIGH)//если кнопка нажата ...
12  {
13  digitalWrite(13,HIGH);//включаем светодиод
14  }
15  else//если не нажата...
16  {
17  digitalWrite(13,LOW);//выключаем
18  }
19  
20

 }

 

и в эти места вставляем команды на запись нового отпечатка 

vBoris
Offline
Зарегистрирован: 27.05.2014

для карточки вот схема включения  http://cxem.net/arduino/arduino2.php
там же пример работы чтение запись

а вот еще http://arduino.ua/ru/prog/SD 
с примером http://arduino.cc/en/Tutorial/ReadWrite

freearduino
Offline
Зарегистрирован: 21.04.2014

А кто нибудь купюроприемник приведенный автором топика использовал?
Может кто подскажет более дешевый купюроприемник, желательно хорошо работающий на Ардуине?

vBoris
Offline
Зарегистрирован: 27.05.2014

вот есть проект http://cxem.net/arduino/arduino85.php