Автоматическая камера хранения на 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 - регистрация

/*************************************************** 
  This is an example sketch for our optical Fingerprint sensor

  Designed specifically to work with the Adafruit BMP085 Breakout 
  ----> http://www.adafruit.com/products/751

  These displays use TTL Serial to communicate, 2 pins are required to 
  interface
  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#include <Adafruit_Fingerprint.h>
#if ARDUINO >= 100
 #include <SoftwareSerial.h>
#else
 #include <NewSoftSerial.h>
#endif

uint8_t getFingerprintEnroll(uint8_t id);


// pin #2 is IN from sensor (GREEN wire)
// pin #3 is OUT from arduino  (WHITE wire)
#if ARDUINO >= 100
SoftwareSerial mySerial(2, 3);
#else
NewSoftSerial mySerial(2, 3);
#endif

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);

void setup()  
{
  Serial.begin(9600);
  Serial.println("fingertest");

  // set the data rate for the sensor serial port
  finger.begin(57600);
  
  if (finger.verifyPassword()) {
    Serial.println("Found fingerprint sensor!");
  } else {
    Serial.println("Did not find fingerprint sensor :(");
    while (1);
  }
}

void loop()                     // run over and over again
{
  Serial.println("Type in the ID # you want to save this finger as...");
  uint8_t id = 0;
  while (true) {
    while (! Serial.available());
    char c = Serial.read();
    if (! isdigit(c)) break;
    id *= 10;
    id += c - '0';
  }
  Serial.print("Enrolling ID #");
  Serial.println(id);
  
  while (!  getFingerprintEnroll(id) );
}

uint8_t getFingerprintEnroll(uint8_t id) {
  uint8_t p = -1;
  Serial.println("Waiting for valid finger to enroll");
  while (p != FINGERPRINT_OK) {
    p = finger.getImage();
    switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.println(".");
      break;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      break;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      break;
    default:
      Serial.println("Unknown error");
      break;
    }
  }

  // OK success!

  p = finger.image2Tz(1);
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image converted");
      break;
    case FINGERPRINT_IMAGEMESS:
      Serial.println("Image too messy");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_FEATUREFAIL:
      Serial.println("Could not find fingerprint features");
      return p;
    case FINGERPRINT_INVALIDIMAGE:
      Serial.println("Could not find fingerprint features");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }
  
  Serial.println("Remove finger");
  delay(2000);
  p = 0;
  while (p != FINGERPRINT_NOFINGER) {
    p = finger.getImage();
  }

  p = -1;
  Serial.println("Place same finger again");
  while (p != FINGERPRINT_OK) {
    p = finger.getImage();
    switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.print(".");
      break;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      break;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      break;
    default:
      Serial.println("Unknown error");
      break;
    }
  }

  // OK success!

  p = finger.image2Tz(2);
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image converted");
      break;
    case FINGERPRINT_IMAGEMESS:
      Serial.println("Image too messy");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_FEATUREFAIL:
      Serial.println("Could not find fingerprint features");
      return p;
    case FINGERPRINT_INVALIDIMAGE:
      Serial.println("Could not find fingerprint features");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }
  
  
  // OK converted!
  p = finger.createModel();
  if (p == FINGERPRINT_OK) {
    Serial.println("Prints matched!");
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    return p;
  } else if (p == FINGERPRINT_ENROLLMISMATCH) {
    Serial.println("Fingerprints did not match");
    return p;
  } else {
    Serial.println("Unknown error");
    return p;
  }   
  
  p = finger.storeModel(id);
  if (p == FINGERPRINT_OK) {
    Serial.println("Stored!");
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    return p;
  } else if (p == FINGERPRINT_BADLOCATION) {
    Serial.println("Could not store in that location");
    return p;
  } else if (p == FINGERPRINT_FLASHERR) {
    Serial.println("Error writing to flash");
    return p;
  } else {
    Serial.println("Unknown error");
    return p;
  }   
}

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

/*************************************************** 
  This is an example sketch for our optical Fingerprint sensor

  Designed specifically to work with the Adafruit BMP085 Breakout 
  ----> http://www.adafruit.com/products/751

  These displays use TTL Serial to communicate, 2 pins are required to 
  interface
  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/


#include <Adafruit_Fingerprint.h>
#if ARDUINO >= 100
 #include <SoftwareSerial.h>
#else
 #include <NewSoftSerial.h>
#endif

int getFingerprintIDez();

// pin #2 is IN from sensor (GREEN wire)
// pin #3 is OUT from arduino  (WHITE wire)
#if ARDUINO >= 100
SoftwareSerial mySerial(2, 3);
#else
NewSoftSerial mySerial(2, 3);
#endif

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);

void setup()  
{
  Serial.begin(9600);
  Serial.println("fingertest");

  // set the data rate for the sensor serial port
  finger.begin(57600);
  
  if (finger.verifyPassword()) {
    Serial.println("Found fingerprint sensor!");
  } else {
    Serial.println("Did not find fingerprint sensor :(");
    while (1);
  }
  Serial.println("Waiting for valid finger...");
}

void loop()                     // run over and over again
{
  getFingerprintIDez();
}

uint8_t getFingerprintID() {
  uint8_t p = finger.getImage();
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.println("No finger detected");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }

  // OK success!

  p = finger.image2Tz();
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image converted");
      break;
    case FINGERPRINT_IMAGEMESS:
      Serial.println("Image too messy");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_FEATUREFAIL:
      Serial.println("Could not find fingerprint features");
      return p;
    case FINGERPRINT_INVALIDIMAGE:
      Serial.println("Could not find fingerprint features");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }
  
  // OK converted!
  p = finger.fingerFastSearch();
  if (p == FINGERPRINT_OK) {
    Serial.println("Found a print match!");
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    return p;
  } else if (p == FINGERPRINT_NOTFOUND) {
    Serial.println("Did not find a match");
    return p;
  } else {
    Serial.println("Unknown error");
    return p;
  }   
  
  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID); 
  Serial.print(" with confidence of "); Serial.println(finger.confidence); 
}

// returns -1 if failed, otherwise returns ID #
int getFingerprintIDez() {
  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK)  return -1;
  
  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID); 
  Serial.print(" with confidence of "); Serial.println(finger.confidence);
  return finger.fingerID; 
}

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

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