Калитка на сканере отпечатков пальцев CROW R503

vde69
Offline
Зарегистрирован: 10.01.2016

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

Ну а второе событие - меня обокрали :) сперли с участка велосипед, и хоть злодеев нашли и велосипед вернули но я стал укреплять передний край обороны от злодеев (датчики, камеры и т.д.).

И вот я добрался до калитки, стал думать как сделать и надежно и безопасно, сначала хотел купить датчик "таблетка", но потом понял, что это не удобно, и потерять можно и забыть дома (дети постоянно бегают то туда то сюда).

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

Начал я с библиотеки Adafruit_Fingerprint, но очень быстро понял, что она во первых сильно кривая, а во вторых в ней банально нет поддержки части нужных команд датчика R503. 

В результате я просто частично ее использовал в виде вставок в код.

На момент публикации у меня все работает в домашних условия (платы спаяны, код написан). Но поставить на улицу пока не могу (погода плохая), буду ждать выходных.

теперь ТТХ того, что получилось:

1. После обучения, распознает палец почти со 100% вероятностью, не срабатывает примерно 1 раз на 50 попыток. Чужие пальцы ни разу не привели к открытию (проверял на всей семье).

2. Время распознавания около 1 сек

3. Обучение на 1 палец занимает 2...3 минуты (необходимо создать 3 модели)

В 1 посте выложу код и схему (что бы можно было править)

vde69
Offline
Зарегистрирован: 10.01.2016

схема

файл R503.ino


// ---------------------------------------------------------
// Управление сканером отпечатка пальцев
//
// состав проекта:
//
// версия среды Arduino 1.8.14
// 
// автор vde69@mail.ru (с), процедуры главного алгоритма
// ---------------------------------------------------------

// ------------------------------------------------------------
//                   конфигурация прошивки                  
//
// перечень прошивок, все кроме одной должны быть закоментированы 
// детальная настройка прошивок  в файле "A_Config.h" 
// ------------------------------------------------------------

#define DOR_CLOCK       // конфигурация "уличная калитка"


#include "A_Config.h"  

// ------------------------------------------------------------
//     расширения и общие данные для всех подключаемых классов (модулей)
//
// вынесены в отдельные 2 файла "include/a_house_addon.h" и "include/a_house_addon.cpp"
// с целью возможности использовать данные как в основной сборке так и при
// синтаксическом контроле подключаемых классов расположеных в каталоге "include/"
// ------------------------------------------------------------

#include                                    
#include "Run.h"

// ------------------------------------------------------------
// загрузка библиотек, желательно загрузить все библиотеки до первого оператора
// при более позденй загрузки возможны неочевидные ошибки компиляции, 
// очередность загрузки библиотек важен!!!
// до загрузки библиотек не должно быть ничего кроме директив компилятору!!!

#ifdef GROW_R503 
  #include "GROW_R503.h"
#endif 


// ------------------------------------------------------------
// Общие переменные не зависящие от конфигурации устройства
unsigned long time_loop_new = 0;    // время начала цикла, глобальное время используется для отсчета времени событий

unsigned long time_loop_key = 0;    // время отсчета изменения статуса кнопки
boolean status_key = false;         // статус кнопки, true - кнопка нажата

const uint8_t pinKey = 6; // кнопка перехода в спец режимы на 6 пине
const unsigned long pauseKey = 5000; // время удержания кнопки необходимое для смены режима

const uint8_t pinOpen = 7;            // пин открытия замка
boolean status_open = false;         // статус кнопки, true - кнопка нажата

// ------------------------------------------------------------
// определение переменных и настроек для датчика GROW-R503
#ifdef GROW_R503 
  // 2pin на - TXD датчика (желтый)
  // 3pin на - RXD датчика (зеленый)
  // 5pin на - WAKEUP датчика (синий)
  // 4pin на - основное питание датчика (красный)
  SoftwareSerial SerialR503(2, 3);           
  Finger_R503 finger = Finger_R503(5,4,&SerialR503);
#endif 




void setup()
{
  // кнопка перехода в спец режимы на 6 пине
  pinMode(pinKey, INPUT);

  digitalWrite(pinOpen, LOW);    // сигнал на замок выключен
  pinMode(pinOpen, OUTPUT);
  status_open = false;

  Serial.begin(9600);                                           // Инициализация аппаратного UART на скорости 9600
  Serial.println("System START...");                             // Вывод сообщения начла работы
  
  #ifdef GROW_R503 
    finger.init();
    finger.set_mode(FINGER_MODE_SLEEP); // при включении установим спящий режим
  #endif 

}

//*************************************************************************************************
// главный цикл
//*************************************************************************************************
void loop() {

  // получим время начала цикла
  time_loop_new = millis();     

  // обработаем кнопку спец режимов
  poll(time_loop_new);

  #ifdef GROW_R503 
    // отвечает за обмен данными с датчиком R503
    finger.poll(time_loop_new);
  #endif 

}
}

 

файл A_Config.h

// ------------------------------------------------------------
// Управление электромеханическим замком
//
// автор vde69@mail.ru (с)
// ------------------------------------------------------------



// ВНИМАНИЕ, в данном модуле использовать можно ТОЛЬКО директивы компилятору #define, #ifdef, #endif, #undef
// в случае использовании любых других операторов будут неочевидные ошибки компиляции


// ------------------------------------------------------------------
// Значения по умолчанию, можно переопределять в конфигурациях через отмену #undef

// ------------------------------------------------------------------
// Конфигурация управления замком на калитке
//   состоит из 
//   Arduino NANO
//   GROW R503
//   Вызывная панель домофона P21GE
#ifdef DOR_CLOCK 
  #define GROW_R503             // определяем датчик 
  

#endif 

файл GROW_R503.cpp

/***************************************************
  Объект для работы со сканером отпечатка пальцев GROW R503

  Создан на основе библиотеки Adafruit_Fingerprint

 ****************************************************/

#include "GROW_R503.h"

#ifdef __AVR__
#include 
#include 
#endif

//-----------------------------------------------------------
//#define DEBUG_R503             // вывод сообщений модуля R503, в продакшене закоментить
//#define DEBUG_R503_READ_ECHO   // вывод данных получаемых от модуля R503, в продакшене закоментить

#ifdef DEBUG_R503_READ_ECHO
  uint8_t Debug_Read_Echo_Packet[] = {0x05,0x06,0x1f,0x0D}; // номера пакетов которые нужно выводить
#endif

// -----------------------------------------------------------
#ifdef __AVR__
Finger_R503::Finger_R503(int wPin, int pPin, SoftwareSerial *ss) {
  thePassword = 0;
  theAddress = 0xFFFFFFFF;

  hwSerial = NULL;
  swSerial = ss;
  mySerial = swSerial;
  wakerPin = wPin;
  powerPin = pPin;

}
#endif

Finger_R503::Finger_R503(int wPin, int pPin, HardwareSerial *ss) {
  thePassword = 0;
  theAddress = 0xFFFFFFFF;

#ifdef __AVR__
  swSerial = NULL;
#endif
  hwSerial = ss;
  mySerial = hwSerial;
  wakerPin = wPin;
  powerPin = pPin;
}


void Finger_R503::RunStep(unsigned long time_loop ) {

  boolean TecFirstStep = FirstStep; FirstStep = false;

  if (stepMode == 0) {
    if      (mode == FINGER_MODE_SLEEP)   { set_stepMode (0);  }
    else if (mode == FINGER_MODE_SCAN)    { set_stepMode (1);  }
    else if (mode == FINGER_MODE_ADD)     { set_stepMode (20); }
    else if (mode == FINGER_MODE_ADD_A)   { set_stepMode (10); }    
    else if (mode == FINGER_MODE_CLEAR)   { set_stepMode (90); }
    else if (mode == FINGER_MODE_CLEAR_A) { set_stepMode (90); }
    else                                  { set_stepMode (0);  }
    return;
  }

  // ------------------------------------------------------------------------------------
  if (stepMode == 1) {
    if (verifyPassword()) {
      if (TecFirstStep) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_BLUE, 0);
      if (getImage() == FINGER_OK) set_stepMode (2);
    } else FirstStep = TecFirstStep;
    return;
  }

  if (stepMode == 2) {
    if (verifyPassword()) {
      if (image2Tz(1) == FINGER_OK) set_stepMode (3);
      else set_stepMode (1);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 3) {
    if (verifyPassword()) {
      if (Search(0x01) == FINGER_OK) set_stepMode (100);
      else set_stepMode (101);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 100) { // есть выход по тайму
    // это событие "скан сработал"
    if (TecFirstStep) {
      if (verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_PURLE, 0);
      else FirstStep = TecFirstStep;
      time_loop_step = time_loop;
    }
    if (getDelayTime(time_loop_step, time_loop) > 2000) {
      set_stepMode (0);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 101) { // есть выход по тайму
    if (TecFirstStep) {
      if (verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_RED, 0);
      else FirstStep = TecFirstStep;
      time_loop_step = time_loop;
    }
    if (getDelayTime(time_loop_step, time_loop) > 2000) {
      set_stepMode (0);
      time_loop_step = time_loop;
    }
    return;
  }

  // ------------------------------------------------------------------------------------

  if (stepMode == 10) {
    if (verifyPassword()) {
      leteNum = GetFreeIndex(0);
      if (leteNum == 0xff) {}      // ошибка, пробуем еще
      else if (leteNum >= 199) {}  // нет места в библиотеке, никуда не уходим, ждем смены режима на сон или на очистку
      else if (leteNum == 0) {     // отпечатков в базе нет, переходим к добавлению
        set_stepMode (20);
        Num = 1;
        time_loop_step = time_loop;
      } else if (mode == FINGER_MODE_ADD) { // отпечаток был предоставлен ранее
        set_stepMode (20);
        Num = 1;
        time_loop_step = time_loop;
      } else {                     // есть отпечатки, требуется подтверждение
        set_stepMode (12);
        time_loop_step = time_loop;
      }
    }
    return;
  }

  if (stepMode == 12) {
    if (verifyPassword()) {
      if (TecFirstStep) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_BLUE, 0);
      if (getImage() == FINGER_OK) {
        set_stepMode (13);
        time_loop_step = time_loop;
      }
    } else FirstStep = TecFirstStep;
    return;
  }

  if (stepMode == 13) {
    if (verifyPassword()) {
      if (image2Tz(1) == FINGER_OK) set_stepMode (14);
      else set_stepMode (11);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 14) {
    if (verifyPassword()) {
      if (Search(0x01) == FINGER_OK) {
        set_stepMode (102);
        Num = 1;
      }
      else set_stepMode (103);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 102) { // есть выход по тайму
    if (TecFirstStep) {
      if (verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_PURLE, 0);
      else FirstStep = TecFirstStep;
      time_loop_step = time_loop;
    }
    if (getDelayTime(time_loop_step, time_loop) > 2000) {
      set_mode(FINGER_MODE_ADD);
      Num = 1;
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 103) { // есть выход по тайму
    if (TecFirstStep) {
      if (verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_RED, 0);
      else FirstStep = TecFirstStep;
      time_loop_step = time_loop;
    }
    if (getDelayTime(time_loop_step, time_loop) > 2000) {
      set_stepMode (10);
      time_loop_step = time_loop;
    }
    return;
  }

  // ------------------------------------------------------------------------------------

  if (stepMode == 20) {
    if (verifyPassword()) {
      if (TecFirstStep) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_PURLE, 0);
      if (getImage() == FINGER_OK) {
        set_stepMode (21);
        time_loop_step = time_loop;
      }
    } else {
      FirstStep = TecFirstStep;
    }
    return;
  }

  if (stepMode == 21) {
    if (verifyPassword()) {
      if (image2Tz(Num) == FINGER_OK) set_stepMode (22);
      else set_stepMode (20);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 22) {
    if (verifyPassword()) {
      if (Search(Num) != FINGER_OK) set_stepMode (104);
      else set_stepMode (105);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 104) { // есть выход по тайму
    if (TecFirstStep) {
      if (verifyPassword()) {
        LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_BLUE, 0);
        time_loop_step = time_loop;
        Num = Num + 1;
      }
      else FirstStep = TecFirstStep;
    }
    if (getDelayTime(time_loop_step, time_loop) > 2000) {
      if (Num > 2) set_stepMode (60);
      else set_stepMode (20);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 105) { // есть выход по тайму
    if (TecFirstStep) {
      if (verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_RED, 0);
      else FirstStep = TecFirstStep;
      time_loop_step = time_loop;
    }
    if (getDelayTime(time_loop_step, time_loop) > 2000) {
      set_stepMode (20);
      Num = 1;
      time_loop_step = time_loop;
    }
    return;
  }

  // ------------------------------------------------------------------------------------



  if (stepMode == 60) {
    if (verifyPassword()) {
      if (TecFirstStep) {
        LedConfig(FINGER_LED_1, 0, FINGER_COLOR_PURLE, 0);
        if (RegModel() == FINGER_OK) set_stepMode (61);
        else set_stepMode (106);
        time_loop_step = time_loop;
      } else FirstStep = TecFirstStep;
    }
    return;
  }

  if (stepMode == 61) {
    if (verifyPassword()) {
      if (store(leteNum) == FINGER_OK) {
        set_mode(FINGER_MODE_ADD);
        Num = 1;
        set_stepMode (10);
        #ifdef DEBUG_R503
              Serial.println("ADD SCAN BASE");
        #endif
                
      }
      else set_stepMode (106);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 106) { // есть выход по тайму
    if (TecFirstStep) {
      if (verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_RED, 0);
      else FirstStep = TecFirstStep;
      time_loop_step = time_loop;
    }
    if (getDelayTime(time_loop_step, time_loop) > 2000) {
      set_mode(FINGER_MODE_ADD);
      set_stepMode (10);
      Num = 1;
      time_loop_step = time_loop;
    }
    return;
  }

//----------------------------------------------------------------------------------------------

  if (stepMode == 90) {
    if (verifyPassword()) {
      if (TecFirstStep) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_BLUE, 0);
      if (getImage() == FINGER_OK) {
        set_stepMode (91);
        time_loop_step = time_loop;
      }
    } else FirstStep = TecFirstStep;
    return;
  }

  if (stepMode == 91) {
    if (verifyPassword()) {
      if (image2Tz(1) == FINGER_OK) set_stepMode (92);
      else set_stepMode (90);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 92) {
    if (verifyPassword()) {
      if (Search(0x01) == FINGER_OK) set_stepMode (93);
      else set_stepMode (109);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 93) { // есть выход по тайму
    if (TecFirstStep) {
      if (verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_PURLE, 0);
      else FirstStep = TecFirstStep;
      time_loop_step = time_loop;
    }
    if (getDelayTime(time_loop_step, time_loop) > 2000) {
      set_stepMode (94);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 94) {
    if (verifyPassword()) {
      if (TecFirstStep) LedConfig(FINGER_LED_1, 0, FINGER_COLOR_RED, 0);
      if (getImage() == FINGER_OK) {
        set_stepMode (95);
        time_loop_step = time_loop;
      }
    } else FirstStep = TecFirstStep;
    return;
  }

  if (stepMode == 95) {
    if (verifyPassword()) {
      if (image2Tz(1) == FINGER_OK) set_stepMode (96);
      else set_stepMode (94);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 96) {
    if (verifyPassword()) {
      if (Search(0x01) == FINGER_OK) {
         emptyDatabase();
         set_mode(FINGER_MODE_SLEEP);
      }
      else set_stepMode (109);
      time_loop_step = time_loop;
    }
    return;
  }

  if (stepMode == 109) { // есть выход по тайму
    if (TecFirstStep) {
      if (verifyPassword()) LedConfig(FINGER_LED_ON, 0, FINGER_COLOR_RED, 0);
      else FirstStep = TecFirstStep;
      time_loop_step = time_loop;
    }
    if (getDelayTime(time_loop_step, time_loop) > 2000) {
      set_stepMode (90);
      time_loop_step = time_loop;
    }
    return;
  }




}


// -----------------------------------------------------------
void Finger_R503::poll(unsigned long time_loop) {

  boolean waker = !digitalRead(wakerPin);

  if (waker) {
    if (mode == FINGER_MODE_SLEEP ) {
      set_mode (FINGER_MODE_SCAN);
    }
    time_loop_mode = time_loop;
  } else {
    if (mode != FINGER_MODE_SLEEP ) {
      if (getDelayTime(time_loop_mode, time_loop) > TIME_RUN) {
        set_mode (FINGER_MODE_SLEEP);
      }
    }
  }
  RunStep(time_loop );

}


// -----------------------------------------------------------
boolean Finger_R503::LedConfig(uint8_t Ctrl, uint8_t Speed, uint8_t Color, uint8_t Count) {
  uint8_t packet[] = {0x35,
                      Ctrl,
                      Speed,
                      Color,
                      Count
                     };
  writePacket(theAddress, FINGER_COMMANDPACKET, sizeof(packet) + 2, packet);
  uint8_t packetR[2];
  uint8_t len = getReply(packetR, packet[0]);

  if ((len == 1) && (packetR[0] == FINGER_ACKPACKET) && (packetR[1] == FINGER_OK))  return true;
  return false;
}


// -----------------------------------------------------------
boolean Finger_R503::HandShake(void) {
  uint8_t packet[] = {0x40};
  writePacket(theAddress, FINGER_COMMANDPACKET, sizeof(packet) + 2, packet);
  uint8_t packetR[2];
  uint8_t len = getReply(packetR, packet[0]);

  if ((len == 1) && (packetR[0] == FINGER_ACKPACKET) && (packetR[1] == FINGER_OK))  return true;
  return false;
}

// -----------------------------------------------------------
uint8_t Finger_R503::TempleteNum(void) {
  uint8_t packet[] = {0x1d};
  writePacket(theAddress, FINGER_COMMANDPACKET, sizeof(packet) + 2, packet);
  uint8_t packetR[2];
  uint8_t len = getReply(packetR, packet[0]);

  if ((len != 1) && (packetR[0] != FINGER_ACKPACKET)) {
    return 0;
  }
  return packetR[1];
}

// -----------------------------------------------------------
uint8_t Finger_R503::GetFreeIndex(uint8_t page = 0) {
  uint8_t packet[] = {0x1f, page};
  writePacket(theAddress, FINGER_COMMANDPACKET, sizeof(packet) + 2, packet);
  uint8_t packetR[35];
  uint8_t len = getReply(packetR, packet[0]);

  if ((len != 33) || (packetR[0] != FINGER_ACKPACKET) || (packetR[1] != FINGER_OK)) {
    return 0xff;
  }

  uint8_t r = 0;
  uint8_t ri = 0;
  for (int i = 2; i <= 33; i++) {
    ri = packetR[i];
    for (int ii = 0; ii <= 7; ii++) {
      if (bitRead(ri, ii) == 0) {
        return r;
      }
      r++;
    }
  }

  return 0;
}

// -----------------------------------------------------------
uint8_t Finger_R503::getImage(void) {
  uint8_t packet[] = {0x01};
  writePacket(theAddress, FINGER_COMMANDPACKET, sizeof(packet) + 2, packet);
  uint8_t packetR[2];
  uint8_t len = getReply(packetR, packet[0]);

  if ((len != 1) || (packetR[0] != FINGER_ACKPACKET)) {
    return 100;
  }
  return packetR[1];
}

// -----------------------------------------------------------
uint8_t Finger_R503::image2Tz(uint8_t slot) { // нумерация 1...6
  uint8_t packet[] = {0x02, slot};
  writePacket(theAddress, FINGER_COMMANDPACKET, sizeof(packet) + 2, packet);
  uint8_t packetR[2];
  uint8_t len = getReply(packetR, packet[0]);

  if ((len != 1) || (packetR[0] != FINGER_ACKPACKET)) {
    return 100;
  }
  return packetR[1];
}

// -----------------------------------------------------------
uint16_t Finger_R503::Search(uint8_t slot) {
  uint16_t StartPage = 0x0000;
  uint16_t PageNum = 0x00c8;
  uint8_t packet[] = {0x04,
                      slot,
                      (StartPage >> 8),
                      StartPage,
                      (PageNum >> 8),
                      PageNum
                     };

  writePacket(theAddress, FINGER_COMMANDPACKET, sizeof(packet) + 2, packet);
  uint8_t packetR[6];
  uint8_t len = getReply(packetR, packet[0]);

  if ((len != 5) || (packetR[0] != FINGER_ACKPACKET)) {
    return -1;
  }
// с 0 по 5
  uint16_t fingerID = 0xFFFF;
  fingerID = packetR[2];
  fingerID <<= 8;
  fingerID |= packetR[3];

  return packetR[1];
}


// -----------------------------------------------------------
uint8_t Finger_R503::RegModel(void) {
  uint8_t packet[] = {0x05};
  writePacket(theAddress, FINGER_COMMANDPACKET, sizeof(packet) + 2, packet);
  uint8_t packetR[2];
  uint8_t len = getReply(packetR, packet[0]);

  if ((len != 1) || (packetR[0] != FINGER_ACKPACKET))
    return -1;
  return packetR[1];
}



// -----------------------------------------------------------
uint8_t Finger_R503::store(uint16_t id) {
  uint8_t packet[] = {0x06, 0x01, id >> 8, id & 0xFF};
  writePacket(theAddress, FINGER_COMMANDPACKET, sizeof(packet) + 2, packet);
  uint8_t packetR[2];
  uint8_t len = getReply(packetR, packet[0]);

  if ((len != 1) || (packetR[0] != FINGER_ACKPACKET))
    return -1;
  return packetR[1];
}


// -----------------------------------------------------------
boolean Finger_R503::verifyPassword(void) {
  uint8_t packet[] = {FINGER_VERIFYPASSWORD,
                      (thePassword >> 24), (thePassword >> 16),
                      (thePassword >> 8), thePassword
                     };
  writePacket(theAddress, FINGER_COMMANDPACKET, 7, packet);
  uint8_t packetR[2];
  uint8_t len = getReply(packetR, packet[0]);

  if ((len == 1) && (packetR[0] == FINGER_ACKPACKET) && (packetR[1] == FINGER_OK)) return true;
  
  return false;
}

// -----------------------------------------------------------
uint8_t Finger_R503::emptyDatabase(void) {
  uint8_t packet[] = {0x0D};
  writePacket(theAddress, FINGER_COMMANDPACKET, sizeof(packet) + 2, packet);
  uint8_t len = getReply(packet, packet[0]);

  if ((len != 1) || (packet[0] != FINGER_ACKPACKET))
    return -1;
  return packet[1];
}



void Finger_R503::writePacket(uint32_t addr, uint8_t packettype, uint16_t len, uint8_t *packet) {

  while (mySerial->available()) {
    mySerial->read();
  }

#if ARDUINO >= 100
  mySerial->write((uint8_t)(FINGER_STARTCODE >> 8));
  mySerial->write((uint8_t)FINGER_STARTCODE);
  mySerial->write((uint8_t)(addr >> 24));
  mySerial->write((uint8_t)(addr >> 16));
  mySerial->write((uint8_t)(addr >> 8));
  mySerial->write((uint8_t)(addr));
  mySerial->write((uint8_t)packettype);
  mySerial->write((uint8_t)(len >> 8));
  mySerial->write((uint8_t)(len));
#else
  mySerial->print((uint8_t)(FINGER_STARTCODE >> 8), BYTE);
  mySerial->print((uint8_t)FINGER_STARTCODE, BYTE);
  mySerial->print((uint8_t)(addr >> 24), BYTE);
  mySerial->print((uint8_t)(addr >> 16), BYTE);
  mySerial->print((uint8_t)(addr >> 8), BYTE);
  mySerial->print((uint8_t)(addr), BYTE);
  mySerial->print((uint8_t)packettype, BYTE);
  mySerial->print((uint8_t)(len >> 8), BYTE);
  mySerial->print((uint8_t)(len), BYTE);
#endif

  uint16_t sum = (len >> 8) + (len & 0xFF) + packettype;
  for (uint8_t i = 0; i < len - 2; i++) {
#if ARDUINO >= 100
    mySerial->write((uint8_t)(packet[i]));
#else
    mySerial->print((uint8_t)(packet[i]), BYTE);
#endif
    sum += packet[i];
  }
#if ARDUINO >= 100
  mySerial->write((uint8_t)(sum >> 8));
  mySerial->write((uint8_t)sum);
#else
  mySerial->print((uint8_t)(sum >> 8), BYTE);
  mySerial->print((uint8_t)sum, BYTE);
#endif
}




uint8_t Finger_R503::getReply(uint8_t packet[], uint8_t id_packet, uint16_t timeout) {
  uint8_t reply[58], idx;
//  uint8_t reply[42], idx;
  uint16_t timer = 0;

  idx = 0;
#ifdef DEBUG_R503_READ_ECHO
  boolean Echo_ON = false;
  for (uint8_t i1 = 0; i1 < sizeof(Debug_Read_Echo_Packet); i1++) { if (Debug_Read_Echo_Packet[i1] == id_packet) {Echo_ON = true;} }
  if (Echo_ON) {Serial.print(" 0x"); Serial.print(String(id_packet, HEX));Serial.print(":<--- ");}
#endif
  while (true) {
	  
	if (idx >= 58) { 
#ifdef DEBUG_R503_READ_ECHO
		if (Echo_ON) {Serial.print(" Bad length-"); Serial.println(idx);}
#endif	
		return FINGER_BADPACKET; 		
	}
	
    while (!mySerial->available()) {
      delay(1);
      timer++;
      if (timer >= timeout) return FINGER_TIMEOUT;
    }
    // something to read!
    reply[idx] = mySerial->read();
#ifdef DEBUG_R503_READ_ECHO
    if (Echo_ON) {Serial.print(" 0x"); Serial.print(reply[idx], HEX);}
#endif
    if ((idx == 0) && (reply[0] != (FINGER_STARTCODE >> 8)))  continue;
    idx++;

    // check packet!
    if (idx >= 9) {
      if ((reply[0] != (FINGER_STARTCODE >> 8)) ||
          (reply[1] != (FINGER_STARTCODE & 0xFF)))
        return FINGER_BADPACKET;
      uint8_t packettype = reply[6];
      //Serial.print("Packet type"); Serial.println(packettype);
      uint16_t len = reply[7];
      len <<= 8;
      len |= reply[8];
      len -= 2;
      //Serial.print("Packet len"); Serial.println(len);
      if (idx <= (len + 10)) continue;
      packet[0] = packettype;
      for (uint8_t i = 0; i < len; i++) {
        packet[1 + i] = reply[9 + i];
      }
#ifdef DEBUG_R503_READ_ECHO
      if (Echo_ON) {Serial.println();}
#endif
      return len;
    }
  }
}



// -----------------------------------------------------------
void Finger_R503::powerOn        (void) {
  digitalWrite(powerPin, HIGH);
#ifdef DEBUG_R503
  Serial.println("Power - On");
#endif
  begin();
  if (!verifyPassword()) {
  #ifdef DEBUG_R503
    Serial.println("Not sensor");
  #endif
    powerOff(); // при неудачной попытке включения питания переходим в спящий режим
  } else {
  }

}

// -----------------------------------------------------------
void Finger_R503::powerOff       (void) {
  set_mode (FINGER_MODE_SLEEP); // при выключении питания в любом случае переходим в спящий режим
  if (hwSerial) hwSerial->end();
#ifdef __AVR__
  if (swSerial) swSerial->end();
#endif
  digitalWrite(powerPin, LOW);
#ifdef DEBUG_R503
  Serial.print(powerPin);
  Serial.print(">");
  Serial.println("Power - Off");
#endif

}

// -----------------------------------------------------------
void Finger_R503::begin(void) {
  delay(TIME_PAUSE_P);  // delay to let the sensor 'boot up'

  if (hwSerial) hwSerial->begin(baudrate, SERIAL_8N2);
#ifdef __AVR__
  if (swSerial) swSerial->begin(baudrate);
#endif
}


// -----------------------------------------------------------
uint8_t Finger_R503::get_mode (void) {
  return mode;
}


// -----------------------------------------------------------
void Finger_R503::set_stepMode (int new_stepMode) {
  if (stepMode != new_stepMode) {
    FirstStep = true;
    #ifdef DEBUG_R503
      Serial.print("step: ");
      Serial.print(stepMode);
      Serial.print(" > ");
      Serial.println(new_stepMode);
    #endif
  }
  stepMode = new_stepMode;
}

// -----------------------------------------------------------
int Finger_R503::get_stepMode  (void){
  return stepMode;   
}

// -----------------------------------------------------------
void Finger_R503::set_mode (uint8_t newMode) {

  if (newMode == FINGER_MODE_SLEEP) {
    if (mode != FINGER_MODE_SLEEP) {
      mode = newMode;
      set_stepMode (0);
      FirstStep = true;
#ifdef DEBUG_R503
      Serial.println("SLEEP");
#endif
      powerOff();
    }
  } else if (newMode == FINGER_MODE_ADD) {
    if (mode != FINGER_MODE_ADD) {
      if (mode == FINGER_MODE_SLEEP) powerOn();
      mode = newMode;
      Num = 1;
      set_stepMode (0);
      FirstStep = true;
      time_loop_mode = millis();
#ifdef DEBUG_R503
      Serial.println("ADD");
#endif
    }
  } else if (newMode == FINGER_MODE_ADD_A) {
    if (mode != FINGER_MODE_ADD_A) {
      if (mode == FINGER_MODE_SLEEP) powerOn();
      mode = newMode;
      Num = 1;
      set_stepMode (0);
      FirstStep = true;
      time_loop_mode = millis();
#ifdef DEBUG_R503
      Serial.println("ADD+");
#endif
    }
  } else if (newMode == FINGER_MODE_CLEAR) {
    if (mode != FINGER_MODE_CLEAR) {
      if (mode == FINGER_MODE_SLEEP) powerOn();
      mode = newMode;
      set_stepMode (0);
      FirstStep = true;
      time_loop_mode = millis();
#ifdef DEBUG_R503
      Serial.println("CLEAR");
#endif
    }
  } else if (newMode == FINGER_MODE_CLEAR_A) {
    if (mode != FINGER_MODE_CLEAR_A) {
      if (mode == FINGER_MODE_SLEEP) powerOn();
      mode = newMode;
      set_stepMode (0);
      FirstStep = true;
      time_loop_mode = millis();
#ifdef DEBUG_R503
      Serial.println("CLEAR+");
#endif
    }
  } else {
    if (mode != FINGER_MODE_SCAN) {
      if (mode == FINGER_MODE_SLEEP) powerOn();
      mode = FINGER_MODE_SCAN;
      set_stepMode (0);
      FirstStep = true;
      time_loop_mode = millis();
#ifdef DEBUG_R503
      Serial.println("SCAN");
#endif
    }
  }
}

// -----------------------------------------------------------
void Finger_R503::init (void) {
  pinMode(powerPin, OUTPUT);
  digitalWrite(powerPin, LOW);
  pinMode(wakerPin, INPUT);
  digitalWrite(powerPin, HIGH);
}

файл GROW_R503.h


/*************************************************** 
  Объект для работы со сканером отпечатка пальцев GROW R503

  Создан на основе библиотеки Adafruit_Fingerprint

  Общий алгоритм работы
      stepMode=0 -   ничего не делается, состояние инициализации
  1. Режим SCAN
      stepMode=1 -   LedConfig, включить синий цвет, означает "готов"
                     getImage, сканируем палец
      stepMode=2 -   image2Tz, обрабатываем скан пальца 
      stepMode=3 -   Search, ищем отпечаток в базе 
      stepMode=100 - LedConfig, включить фиолетовый цвет, означает "палец распознан"
                     pause, ожидание для открытия замка, этот статус должен обрабатыватся в основном цикле 
      stepMode=101 - LedConfig, включить красный цвет, означает "ошибка"
                     pause, ожидание, ничего не происходит просто показываем красную подсветку 
  2. Режим ADD подтверждение режима (библиотека не пустая)
      stepMode=10  - TempleteNum, найдем номер в библиотеке
      stepMode=11  - LedConfig, включить моргание фиолетовым цветом, означает "готов"
                     getImage, сканируем палец дяя подтверждения возможности добавления (только если библиотека не пустая)
      stepMode=12  - image2Tz, обрабатываем скан пальца 
      stepMode=13  - Search, ищем отпечаток в базе 
      stepMode=102 - LedConfig, включить красный цвет, означает "ошибка подтверждения"
                     pause, ожидание, ничего не происходит просто показываем красную подсветку 
 3. Режим ADD ввод новых сканов в библиотеку
      // 1
      stepMode=20  - LedConfig, включить фиолетовый цвет, означает "готов"
                     getImage, сканируем палец
      stepMode=21 -  image2Tz, обрабатываем скан пальца 
      stepMode=22 -  Search, ищем отпечаток в базе 
      stepMode=103 - LedConfig, включить синий цвет, означает "вариант пальца принят"
                     pause, ожидание, ничего не происходит просто показываем синюю подсветку 
      stepMode=104 - LedConfig, включить красный цвет, означает "ошибка или палец уже в базе"
                     pause, ожидание, ничего не происходит просто показываем красную подсветку 
      // 2
      stepMode=30  - LedConfig, включить фиолетовый цвет, означает "готов"
                     getImage, сканируем палец
      stepMode=31 -  image2Tz, обрабатываем скан пальца 
      stepMode=32 -  Search, ищем отпечаток в базе 

      // 3
      stepMode=40  - LedConfig, включить фиолетовый цвет, означает "готов"
                     getImage, сканируем палец
      stepMode=41 -  image2Tz, обрабатываем скан пальца 
      stepMode=42 -  Search, ищем отпечаток в базе 

      // 4
      stepMode=50  - LedConfig, включить фиолетовый цвет, означает "готов"
                     getImage, сканируем палец
      stepMode=51 -  image2Tz, обрабатываем скан пальца 
      stepMode=52 -  Search, ищем отпечаток в базе 

      // 5
      stepMode=50  - LedConfig, включить фиолетовый цвет, означает "готов"
                     getImage, сканируем палец
      stepMode=51 -  image2Tz, обрабатываем скан пальца 
      stepMode=52 -  Search, ищем отпечаток в базе 

      // ----
      stepMode=60  - LedConfig, моргание фиолетовым цветом, означает "пальцы сосканированы идет расчет"
                     RegModel, создаем модель отпечатка
      stepMode=61 -  Store, сохраняем модель отпечатка в библиотеке 

      stepMode=105 - LedConfig, включить красный цвет, означает "ошибка сборки модели"
                     pause, ожидание, ничего не происходит просто показываем красную подсветку 
      
  4. Режим CLEAR подтверждение очистки
      stepMode=90  - LedConfig, включить моргание красным цветом, означает "готов"
                     getImage, сканируем палец дяя подтверждения возможности добавления (только если библиотека не пустая)
      stepMode=91  - image2Tz, обрабатываем скан пальца 
      stepMode=92  - Search, ищем отпечаток в базе 

      stepMode=106 - LedConfig, включить красный цвет, означает "ошибка подтверждения"
                     pause, ожидание, ничего не происходит просто показываем красную подсветку 
      
      stepMode=107 - LedConfig, включить фиолетовый цвет, означает "очистка завершена"
                     pause, ожидание, ничего не происходит просто показываем фиолетовую подсветку 

  
****************************************************/

#ifndef GROW_R503_H        
#define GROW_R503_H

#include "Run.h"
#include "Arduino.h"
#ifdef __AVR__
  #include 
#endif


#define TIME_PAUSE_W 50     // пауза включения waker, для избежания ложных срабатываний,
#define TIME_PAUSE_P 500    // пауза включения порта (до начала приема команд), минимально по документации 200 
#define TIME_RUN 30000      // время работы по сигналу waker, потом идет выключение


#define FINGER_LED_1 0x01       // мерцающий
#define FINGER_LED_2 0x02       // мигающий
#define FINGER_LED_ON 0x03      // включить
#define FINGER_LED_OFF 0x04     // выключить
#define FINGER_LED_ON_L 0x05    // включить плавно
#define FINGER_LED_OFF_L 0x06   // выключить плавно

#define FINGER_COLOR_RED 0x01   // 
#define FINGER_COLOR_BLUE 0x02  //  
#define FINGER_COLOR_PURLE 0x03 //   

#define FINGER_MODE_SLEEP 0x00  // режим сна
#define FINGER_MODE_SCAN 0x01   // режим скана
#define FINGER_MODE_ADD 0x02    // режим добавления отпечатков
#define FINGER_MODE_ADD_A 0x03  // режим авторизации добавления отпечатков
#define FINGER_MODE_CLEAR 0x04  // режим сброса всех отпечатков 
#define FINGER_MODE_CLEAR_A 0x05// режим авторизации для сброса всех отпечатков 



#define FINGER_STARTCODE 0xEF01
#define FINGER_COMMANDPACKET 0x1
#define FINGER_DATAPACKET 0x2
#define FINGER_ACKPACKET 0x7
#define FINGER_ENDDATAPACKET 0x8



#define FINGER_OK 0x00
#define FINGER_PACKETRECIEVEERR 0x01
#define FINGER_NOFINGER 0x02
#define FINGER_IMAGEFAIL 0x03
#define FINGER_IMAGEMESS 0x06
#define FINGER_FEATUREFAIL 0x07
#define FINGER_NOMATCH 0x08
#define FINGER_NOTFOUND 0x09
#define FINGER_ENROLLMISMATCH 0x0A
#define FINGER_BADLOCATION 0x0B
#define FINGER_DBRANGEFAIL 0x0C
#define FINGER_UPLOADFEATUREFAIL 0x0D
#define FINGER_PACKETRESPONSEFAIL 0x0E
#define FINGER_UPLOADFAIL 0x0F
#define FINGER_DELETEFAIL 0x10
#define FINGER_DBCLEARFAIL 0x11
#define FINGER_PASSFAIL 0x13
#define FINGER_INVALIDIMAGE 0x15
#define FINGER_FLASHERR 0x18
#define FINGER_INVALIDREG 0x1A
#define FINGER_ADDRCODE 0x20
#define FINGER_PASSVERIFY 0x21



#define FINGER_TIMEOUT 0xFF
#define FINGER_BADPACKET 0xFE

#define FINGER_REGMODEL 0x05
#define FINGER_STORE 0x06
#define FINGER_LOAD 0x07
#define FINGER_UPLOAD 0x08
#define FINGER_DELETE 0x0C
#define FINGER_EMPTY 0x0D
#define FINGER_VERIFYPASSWORD 0x13
#define FINGER_TEMPLATECOUNT 0x1D

#define DEFAULTTIMEOUT 5000  // milliseconds


class Finger_R503 {
 public:

  uint16_t fingerID, confidence, templateCount;


#ifdef __AVR__
  Finger_R503(int wPin,int pPin,SoftwareSerial *);
#endif
  Finger_R503(int wPin,int pPin,HardwareSerial *);

  uint8_t get_mode      (void); // возвращает текущий режим
  void    set_mode      (uint8_t newMode); 
  void    set_stepMode  (int new_stepMode); 
  int     get_stepMode  (void); 

  void init     (void);                   // должна вызыватся из setup, устанавливает параметры ввода вывода контроллера
  void poll     (unsigned long time_loop);// должна вызыватся из loop, отрабатывает сигнал WAKEUP
  void RunStep  (unsigned long time_loop);// должна вызыватся из loop, отрабатывает состояния Step
  void begin    (void);
  
  boolean  LedConfig      (uint8_t Ctrl, uint8_t Speed, uint8_t Color, uint8_t Count);
  boolean  HandShake      (void);
  uint8_t  getImage       (void);
  uint8_t  TempleteNum    (void);
  uint8_t  image2Tz       (uint8_t slot = 1);
  uint16_t Search         (uint8_t slot);
  uint8_t  GetFreeIndex   (uint8_t page = 0);
  boolean  verifyPassword (void);
  uint8_t  RegModel       (void);
  uint8_t  store          (uint16_t id);
  uint8_t  emptyDatabase  (void);

  void    writePacket   (uint32_t addr, uint8_t packettype, uint16_t len, uint8_t *packet);
  uint8_t getReply      (uint8_t packet[], uint8_t id_packet, uint16_t timeout=DEFAULTTIMEOUT);


 private: 

  int wakerPin = 0;
  int powerPin = 0;
  int Num = 1;
  boolean FirstStep = false;
  uint16_t baudrate = 57600;

  unsigned long time_loop_mode = 0;   // время начала работы во временном режиме 
  unsigned long time_loop_step = 0;   // время начала паузы по степу

  uint8_t mode = FINGER_MODE_SCAN;    // текущий режим, 

  int stepMode = 0;                   // текущий шаг для режима (-1 для ошибок)
  uint8_t leteNum = 0;				        // пустой индекс библиотеки отпечатков
  uint32_t thePassword;
  uint32_t theAddress;

  Stream *mySerial;
#ifdef __AVR__
  SoftwareSerial *swSerial;
#endif
  HardwareSerial *hwSerial;

  void powerOn  (void);
  void powerOff (void);

  
  
};



#endif 

файл Run.cpp

// *************************************************************************************************
// процедура сравнивает два времени и возвращает разницу в виде числа, учитывает переход времени через 0
//   start_time - начальное время
//   end_time - конечное время
//
// !!!! процедура чуствительна к разрядности исполняемого кода !!!!
// !!!! процедура может работать неправильно при двойном переходе времени через 0 !!!!
// *************************************************************************************************
unsigned long getDelayTime(unsigned long start_time, unsigned long end_time){
  unsigned long result;
  if (start_time <= end_time) {
    result = end_time - start_time;
  }
  else {
    result = 4294967295 - end_time + start_time;
  }
  return result;
}

файл Run.h



#ifndef RUN_H        
#define RUN_H


// ------------------------------------------------------------
// Описания функций используемых в объектах и в проекте
unsigned long getDelayTime(unsigned long start_time, unsigned long end_time);


#endif 

файл other.ino

// ---------------------------------------------------------
// Дополнительные процедуры
//
// ---------------------------------------------------------



// -----------------------------------------------------------
void poll(unsigned long time_loop) {

  boolean buttonState = digitalRead(pinKey);

  if (!buttonState) {
    // кнопка отжата, обновим время и статус
    time_loop_key = time_loop;
    status_key = false;
  } else if (!status_key) {
    // кнопка нажата, но это первый цикл
    time_loop_key = time_loop;
    status_key = true;
  } else if (getDelayTime(time_loop_key, time_loop) > pauseKey) {
    // кнопка зажата давно, изменим режим и начнем считать заново
    time_loop_key = time_loop;
    status_key = false;

    #ifdef GROW_R503 
      if (finger.get_mode() == FINGER_MODE_SLEEP) {
        finger.set_mode(FINGER_MODE_ADD_A);
      } else if (finger.get_mode() == FINGER_MODE_SCAN) {
        finger.set_mode(FINGER_MODE_ADD_A);
      } else if (finger.get_mode() == FINGER_MODE_ADD) {
        finger.set_mode(FINGER_MODE_CLEAR_A);
      } else if (finger.get_mode() == FINGER_MODE_ADD_A) {
        finger.set_mode(FINGER_MODE_CLEAR_A);
      } else {
        finger.set_mode(FINGER_MODE_SLEEP);        
      }          

    #endif 
    
  }


  #ifdef GROW_R503 
    if (finger.get_stepMode() == 100) {
      if (!status_open) { 
        digitalWrite(pinOpen, HIGH); 
        status_open = true;
        Serial.println("=== DOOR OPEN ===");
      }
    } else {
      if (status_open) { 
        digitalWrite(pinOpen, LOW); 
        status_open = false;
        Serial.println("=== DOOR CLOSE ===");
      }
    }
  #endif 
  
}
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vde69 пишет:
Но поставить на улицу пока не могу (погода плохая)
Не бросайте, пожалуйста, тему. Больше всего интересует как раз на улице в плохую погоду. Дождь, влажная взвесь, изморозь, иней, роса - все вот эти вот прелести. Так что, как поставите на улице, напишите потом как оно себя показало.

sadman41
Offline
Зарегистрирован: 19.10.2016

У меня на мобиле только чистые пальцы распознаются. Стоит только мелким ремонтом (с наличием пыли) позаниматься - все, начинаются капризы. Мол пальцы не те...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вот и я ж про то же. А на улице пальцы то замёрзли, то увлажнились, то ещё чего. Потому и спрашиваю про плохую погоду.

kost82
Offline
Зарегистрирован: 30.11.2015

Сканер отпечатков в условиях улицы - ИМХО это баловство. Либо надо его весить в сухом и теплом месте.

У нас в офисе я ставил заводской сканер отпечатков на входной двери. Работает нормально, если руки сухие. Чуть только немного влажные - все, не открывает. Ну и плюс нужно ручное дублирование открывания замка. Пару раз замок глючил, если бы он с ключа не открывался - куча народа оказалось бы запертыми в офисе.

В детском саду у нас около года на калитках (на улице) стоят примерно вот такие замки 

Я такой замок тоже настраивал и устанавливал. У него есть возможность привязать брелок (как у домофона), код менять можно хоть каждый день. Единственный минус - от частого использования некоторые цифры на кнопках "облезли". И на одной панели перегорела подсветка одной из клавиш. Настроить можно различные типы замков, и еще есть куча настроек. Цена замка в районе 1-2 тыс. руб. и выглядит вполне прилично. Если прикинуть, что его каждый день открывают по несколько сотен (если не тысяч) раз - вполне достойный вариант. И ключи таскать не надо.

Есть еще замки в антивандальном исполнении, типа таких 

они на вид попрочнее, программируются тоже легко. Только вроде брелки к ним не привязываются.

Если решите все-таки ставить отпечаток пальцев - отпишитесь о полевых испытаниях.

vde69
Offline
Зарегистрирован: 10.01.2016

ставить на улицу буду в выходные (если дождя не будет),

пока провел следующие испытания

1. сильно потер пальцы (до красноты), эмитация физ нагрузки - все ОК

2. подложил под палец 2 нитки, эмитация мусора  (пробовал под разными положениями) - все ОК

3. втер в палец цемент, эмитация грязных рук - все ОК

4. сильно намочил палец - НЕ работает

5. сухой палец и влажный датчик (из пульеверизатора, аналог росы) если не слегка влажно - все ОК, если прямо каплями - НЕ работает

6. через тонкий слой пленки - вообще не срабатывает датчик касания

 

vde69
Offline
Зарегистрирован: 10.01.2016

Поставил на улицу, правда не до конца (но об этом чуть ниже)

На улице 0 градусов, влажно и что-то моросит, короче мерзость, провозился около 3х часов сам замерз, но точно могу сказать датчик то-же замерз :)

Итого - сам сканер работает почти так-же как дома, чуть хуже стал работать датчик наличия пальца (контакт WakeUp), но распознавание и поиск работаю хорошо.

Сами пальцы от мороза то же "потеряли форму", и стали давать примерно 20% ошибок, но дополнительное "дообучение" (повторил обучение и он добавил по 1 модели на палец), в результате правильное распознование более примерно 95%

 

теперь о том что я не доделал, и тут нужна помощь сообщества

Короче у меня есть явная проблема в электрической схеме, я не зря запаял джампер "test", без него вся схема работает нормально, но само собой замок не открывается.

Когда я его ставлю у меня проседает напряжение на датчике и он перестает работать,  

Средний провод разьема отмеченный как "Key" идет из вызывной панели и на нем постоянные +4,5v, это провод должен идти на внутренний выключатель "Открыть замок".

То есть если замкнуть "Key" на землю, то замок открывается.

 

Как правильно реализовать такое замыкание? понятно, что можно поставить реле, но очень не хочется...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vde69 пишет:

Средний провод разьема отмеченный как "Key" идет из вызывной панели и на нем постоянные +4,5v, это провод должен идти на внутренний выключатель "Открыть замок".

То есть если замкнуть "Key" на землю, то замок открывается.

Вы точно уверены, что логика именно такова? У меня вот всё по другому. Открыванием занимается сама вызывная панель по сигналу с домофона. Она (панель) замыкает питание на соленоид замка. А когда она ничего не замыкает, то мультиметр показывает на проводе те же 12В, что и должны быть при открывании за счёт тока утечки ключа.

vde69
Offline
Зарегистрирован: 10.01.2016

ЕвгенийП пишет:

vde69 пишет:

Средний провод разьема отмеченный как "Key" идет из вызывной панели и на нем постоянные +4,5v, это провод должен идти на внутренний выключатель "Открыть замок".

То есть если замкнуть "Key" на землю, то замок открывается.

Вы точно уверены, что логика именно такова? У меня вот всё по другому. Открыванием занимается сама вызывная панель по сигналу с домофона. Она (панель) замыкает питание на соленоид замка. А когда она ничего не замыкает, то мультиметр показывает на проводе те же 12В, что и должны быть при открывании за счёт тока утечки ключа.

Уверен, вот схема 

 

кроме того если я замыкаю этот контакт на землю действительно замок открывается

vde69
Offline
Зарегистрирован: 10.01.2016

 

Вот как установил сам датчик, по идее он закрыт от ветра и дождя (с верху над калиткой навес)

 

vde69
Offline
Зарегистрирован: 10.01.2016

Все теперь у меня работает. Была проблема с оптроном, он банально не рабочий был.

заодно поменял резистр на 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 сек

 

vde69
Offline
Зарегистрирован: 10.01.2016

Прошла неделя, за это время был и снегопад и ледяной дождь. В целом все работает хорошо. Для датчика единственно чего ему мешает - это когда он мокрый, все остальное работает. Проверка проходит на 4х членах семьи, возраст от 8 до 50 лет, полет хороший. 

сегодня наконец запихнул платы во временную коробочку, когда будет тепло надо будет переделать с макеток на нормальную плату и сделать коробочку по размеру побольше.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vde69 пишет:
когда будет тепло надо будет переделать с макеток на нормальную плату и сделать коробочку по размеру побольше.
А монтажная коробка не подойдёт? Там уже в ввод проводов готовый и можно погодостойкую взять.

vde69
Offline
Зарегистрирован: 10.01.2016

ЕвгенийП пишет:

vde69 пишет:
когда будет тепло надо будет переделать с макеток на нормальную плату и сделать коробочку по размеру побольше.
А монтажная коробка не подойдёт? Там уже в ввод проводов готовый и можно погодостойкую взять.

 

у меня сейчас такая https://www.stroyportal.ru/catalog/section-korobki-montazhnye-2668/korobka-raspayachnaya-80h80h25-otkrytoy-provodki-b-684306313/ но висит на видном месте, по этому надежную и страшную не хочется.

сейчас из бутылки феном сделал сверху колпак, то есть сейчас ничего не промокнет, но во первых нужно отверстие для кнопки обучения, наверно нужно светодиод вывести.

Короче надо будет весной посмотреть, сейчас все равно этим заниматься холодно, к весне может 3д принтер куплю и напечатаю или подберу чего...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, для видного места я сам на принтере напечатал такую, что сверху у неё вообще никаких щелей нет от слова совсем, т.е. никакой дождь и снег ей не страшны. Там у меня профильная труба 40х25, вот я и сделал коробку для клемм в аккурат шириной 25мм, чтобы как родная стала. Могу показать, если интересно.

vde69
Offline
Зарегистрирован: 10.01.2016

Всем кого интересовали полевые испытания:

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

наверно буду проект дорабатывать, поставлю sd карту и заложу программу самообучение, на основе принятых отпечатков буду строить варианты с теми которые не прошли, по идее те которые совсем чужие не смогут объединится в пару.

Короче если до НГ придет блок питания для сверлильного станка (3 посылки зависли на почте) доделаю его, если не придет займусь этой доработкой...

vde69
Offline
Зарегистрирован: 10.01.2016

Прошла зима.

Разобрал макетку, вытравил плату, перепаял, залачил цапон лаком, на плату нанес технические надписи. Внес небольшие изменения, в основном в схему питания.

Исправил в коде 1 серьезную ошибку (переполнение буфера) и немного улучшил стабильность и скорость.

Напечатал нормальный корпус.

Все работает, ничего переделывать больше не планирую.

 

РЕЗЮМЕ: в целом данный датчик вполне подходит для средней полосы для работы на улице без прямого попадания на него осадков (под козырьком)

 

зы

в 1м посту обновил и схему и код