Перейти к публикации
iT4iT.CLUB

Kitsum

Пользователи
  • Публикации

    424
  • Зарегистрирован

  • Посещение

  • Дней в лидерах

    234

Сообщения, опубликованные пользователем Kitsum


  1. 10 часов назад, svchekalin сказал:

    Дно у ящика открывается и содержимое в утиль... на сервере можно что то типа распознавания текста и там список спам фильтр...

    Это уже из мира фантастики.

    1. Большой расход электроэнергии
    2. Что делать если заказное письмо или какой ни-ть документ вложили в газету или рекламную брошюру
    3. На квитанциях за комунальные услуги тоже печатают рекламу
    4. Каких габаритов будет механизм пытающийся это все рассортировать
    5. Все это привлекает внимание как почтальона так и всех остальных

    Думаю, что стоит остановиться на попытки определить наличие чего-либо в почтовом ящике и озаботиться передачей этой информации на сервер. Тот в свою очередь передаст сообщение на мобильный телефон через систему Push уведомлений или иным способом.

    • Like 1

  2. Можно с помощью распознавания цвета. Обычно мелкие пакеты белого или желтого цвета. Распознать может сенсор TCS3200. Но это:

    1. Привлечет внимание
    2. Сожрет аккумулятор (если устройство работает автономно)
    3. Ложные срабатывания

    Подобное можно реализовать в частном доме или почтовый ящик должен быть закреплен непосредственно возле квартиры.

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


  3. Косяков нет, просто в ходе обсуждения я не видел жизни этой идеи и воспринял её как постоянный режим ON/OFF. Если честно, то я и сейчас не вижу потенциала объединения датчиков сигнализации с замком. Это разные системы и разработать сигнализацию отдельно было бы намного интереснее, а уже потом искать алгоритмы их объединения.

    Но если очень хочется, то нужно сделать следующее:

    Добавляем в описании пинов микроконтроллера дополнительную константу. Это будет пин A3. Сразу стоит подтянуть чего к земле через резистор с номиналом 4.7-10kOm.

    #define PIN_ALARM_FORCED  17       // ALARM FORCED

    В функции Setup описываем его как вход

    pinMode(PIN_ALARM_FORCED, INPUT);
    digitalWrite(PIN_ALARM_FORCED, HIGH);

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

    // Автоматическое отключение сигнализации
    if(alarmTimer != 0) {
      if(millis()/1000 - alarmTimer >60) {
        alarmTimer = 0;
        digitalWrite(PIN_ALARM_1M, invert ? HIGH : LOW);
        Serial.println(F("Automatic shutdown of the first signaling channel.\n"));
      }
    }

    и заменить его на

    // Автоматическое отключение сигнализации
    if(alarmTimer != 0) {
      if(millis()/1000 - alarmTimer > 60) {
        alarmTimer = 0;
        digitalWrite(PIN_ALARM_1M, invert ? HIGH : LOW);
        Serial.println(F("Automatic shutdown of the first signaling channel.\n"));
      }
    }
    else {
      if(digitalRead(PIN_ALARM_FORCED)) {
        alarmTimer = millis()/1000;
        digitalWrite(PIN_ALARM_1M, invert ? LOW : HIGH);
        digitalWrite(PIN_ALARM_LONG, invert ? LOW : HIGH);
      }
    }

    Это должно заставить принудительно подтягивать реле сигнализации при появлении высокого уровня на пине A3.

    PS: датчик активирующий сигнализацию должен подавать не более 5V на A3 и обязательно иметь общую массу с замком.


  4. 1 час назад, svchekalin сказал:

    Мы сейчас про железный ящик куда почтальон кидает извещения с алихи ?

    Оууу. Виноват, не про ту почту подумал, бывает.

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


  5. Можно на стороне сервера накидать скрипт, например на PHP, и с помощью Cron стартовать его раз в 5-10 минут. При обнаружении нового сообщения дергать контроллер, а тот в свою очередь будет сверкать, свистеть и махать флагами.


  6. 1 час назад, Alex13 сказал:

    Можно ли инвертировать выходы на реле управления соленоидом?

    Да, можно.

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

      // Инициализация используемых пинов
      // Реле
      pinMode(PIN_RELAY_1, OUTPUT);
      digitalWrite(PIN_RELAY_1, HIGH);
      pinMode(PIN_RELAY_2, OUTPUT);
      digitalWrite(PIN_RELAY_2, HIGH);

    Необходимо заменить HIGH на LOW

    За управление замком отвечает функция lock

    void lock(bool lock) {
      digitalWrite(lock ? PIN_RELAY_1 : PIN_RELAY_2, LOW);
      delay(400);
      digitalWrite(lock ? PIN_RELAY_1 : PIN_RELAY_2, HIGH);
      lockStat = lock;
    }

    Аналогичным образом необходимо заменить значения LOW и HIGH на противоположные


  7. 4 часа назад, Alex13 сказал:

    Реле не напрямую же цепляется к пину!

    435747929_674.jpg.dcbf1294a9ca8ddef4f1de

    Стандартный китайский модуль, состоящий из группы реле, в данном случае 8 штук. Имеет два ряда пинов:

    1. GND, IN1, IN2, ..., IN8, VCC
    2. GND, VCC, JD-VCC

    Первый (левый ряд) служит для управления реле через оптопару. Она необходима чтобы развязать логику от самой нагрузки и обезопасить контроллер в случае пробоя реле. IN1, IN2, ... входы управления реле. VCC - питание оптопар.

    Второй (правый ряд) необходим для питания катушек реле. Имеются два варианта подачи питания:

    1. Если установлена перемычка между VCC и JD-VCC, то катушки будут запитаны от VCC левого ряда пинов, соответственно от самого контроллера. Нужно учитывать, что для контроллера это может стать непосильной ношей.
    2. Если убрать перемычку, то питание на катушки можно подать, от стороннего источника питания воспользовавшись пинами VCC и GND (земля общая по всему модулю)

  8. 57 минут назад, Alex13 сказал:

    Видео, было бы не плох!!! Если Вам не сложно снимите!!! Может я чего не так понимаю! :$

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

    На видео создание мастер ключа и проверка всех почти всех режимов работы. Реле подписаны. Их задержка уменьшена до 0.5 сек.


  9. 2 часа назад, Alex13 сказал:

    Так с предыдущей прошивкой сигнализация работает, загружаем последнюю и всё....... тишина.

    Проверил последнюю программу на живом контроллере, все работает:

    1. реле открыть/закрыть
    2. сигнализация
    3. режимы программирования
    4. режимы закрытия авто/вручную

    Предлагаю сделать следующее:

    1. Внимательно проверить весь монтаж и привести его к тому, что показано на схеме
    2. Использовать только описанные в схеме элементы (на время тестирования)
    3. Убедиться, что в предыдущую версию не вносились собственные изменения. Если таковые имелись, то привести последний вариант программы в соответствие с теми изменениями.
    4. Даже если все проверили, перепроверить еще раз

    PS: Будьте внимательнее, у Вас ошибка на поверхности и скорее всего по невнимательности! Могу снять видео с демонстрацией работы последней версией программы.


  10. to Alex13

    Если проверять светодиодами, то оба будут гореть постоянно т.к управление китайскими релейными модулями инвертировано. По сути оба светодиода горят, но в момент открытия или закрытия соответствующий светодиод погаснет на 1 секунду (думаю, что это время можно уменьшить до 0.5 сек). В любом случае управление автомобильным соленоидом происходит с помощью 12V и придется задействовать реле или транзистор для развязки, как сделано на схеме.

    Сама сигнализация осталось не тронутой. Думаю, что стоит перепроверить состояние пина A0 (включена сигнализация или нет).


  11. 3 часа назад, svchekalin сказал:

    это ты меня так акуратно в сторону леса послал  ?

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


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


  13. to Alex13

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

    Схема подключения

    nrf_z_a2.thumb.png.5a20a6b9d24393301d7c6

    Листинг программы

    Скрытый текст
    
    /*
     * Программа управления нагрузкой с помощью RFID-RC522 13.56 мГц
     *  
     * - РАСПИНОВКА ----------------------------------------------------------------------------
     *             MFRC522      Arduino       Arduino   Arduino    Arduino          Arduino
     *             Reader/PCD   Uno           Mega      Nano v3    Leonardo/Micro   Pro Micro
     * Signal      Pin          Pin           Pin       Pin        Pin              Pin
     * -----------------------------------------------------------------------------------------
     * RST/Reset   RST          9             5         D9         RESET/ICSP-5     RST
     * SPI SS      SDA(SS)      10            53        D10        10               10
     * SPI MOSI    MOSI         11 / ICSP-4   51        D11        ICSP-4           16
     * SPI MISO    MISO         12 / ICSP-1   50        D12        ICSP-1           14
     * SPI SCK     SCK          13 / ICSP-3   52        D13        ICSP-3           15
     * -----------------------------------------------------------------------------------------
     *
     */
    
    // Необходимые библиотеки
    #include <avr/wdt.h>
    #include <EEPROM.h>
    #include <SPI.h>
    #include <MFRC522.h>
    #include <Bounce2.h>
    
    // Необходимые пины
    #define PIN_RESET         4        // RESER MEMORY
    #define PIN_OPEN          2        // OPEN
    
    #define PIN_RELAY_1       6        // RELAY CLOSE
    #define PIN_RELAY_2       7        // RELAY OPEN
    #define PIN_TONE          3        // TONE
    
    #define PIN_RST           9        // RFID
    #define PIN_SS            10       // RFID
    
    #define PIN_MODE          8        // MODE
    
    #define PIN_ALARM         14       // ALARM ON/OFF
    #define PIN_ALARM_1M      15       // ALARM SIGNAL 1M
    #define PIN_ALARM_LONG    16       // ALARM SIGNAL LONG TIME         
    
    // Инициализация RFID ридера
    MFRC522 mfrc522(PIN_SS, PIN_RST);
    
    // Переменные необходимые для работы со списком ключей
    byte **keys;
    byte keys_count = EEPROM.read(0);
    
    // Переменные необходимые для режима программирования
    byte modeProgTime           = 5;     // Количество секунд удержания мастер ключа для входа\выхода в\из режим\а программирования
    bool mode                   = false; // НЕ МЕНЯТЬ!
    bool modeLock               = false; // НЕ МЕНЯТЬ!
    byte modeClean              = 0;     // НЕ МЕНЯТЬ!
    unsigned long modeTimer     = 0;     // НЕ МЕНЯТЬ!
    unsigned long resetTimer    = 0;     // НЕ МЕНЯТЬ!
    
    // Переменные для сигнализации
    bool invert                 = true;  // Инвертировать выходы сигнализации (A1, A2)
    bool alarm                  = false; // НЕ МЕНЯТЬ!
    byte alarmCount             = 0;     // НЕ МЕНЯТЬ!
    unsigned long alarmTimer    = 0;     // НЕ МЕНЯТЬ!
    unsigned long alarmInterval = 0;     // НЕ МЕНЯТЬ!
    
    // Управление замком
    unsigned long openTimer     = 0;
    bool lockStat               = false;
    
    // Защита кнопок от дребезга
    Bounce key_reset = Bounce();
    Bounce key_open  = Bounce();
    /*
      Якобы программный reset, но мы с Вами знаем, что это не так ;)
    */
    void(* resetFunc) (void) = 0;
    /*
      Функция звукового оповещения.
      Принимает параметры: количество звуковых сигналов, частота в герцах, продолжительность звука, пауза в милесекундах (не обязательно)
    */
    void squeaker(byte count, unsigned int Hz, unsigned int duration, unsigned int sleep = 0) 
    {
      for(int i=0; i<count; i++) {
        tone(PIN_TONE, Hz, duration);
        if(sleep > 0) delay(sleep);
      }
    }
    /*
      Функция читает EEPROM и составляет список активных ключей
      Первый байт в памяти содержит количество ключей
      UID ключа содержит 4 байта
      Общая память 1 + количество ключей * 4
      Максимум можно записать 255 ключей
    */
    void keysRead() {
      // Выводим количество ключей
      Serial.print(F("KEYS COUNT: "));
      Serial.println(keys_count);
      int eb = 0;
      keys = (byte**)malloc(sizeof(byte*)*keys_count);
      // Читаем список ключей из EPROM
      Serial.println(F("------------------------------"));
      for(byte i=0; i<keys_count; i++) {
        Serial.print(F("KEY: "));Serial.print(i);Serial.print(" | ");
        keys[i] = (byte*)malloc(sizeof(byte)*4);
        for(byte b=0; b<4; b++) {
          keys[i][b] = EEPROM.read(++eb);
          Serial.print(keys[i][b]);
          if(b < 3) Serial.print(F(" "));
        }
        Serial.println();
      }
      Serial.println(F("------------------------------"));
      Serial.println();
    }
    /*
      Функция выводит UID ключа и, при необходимости, сопроводительное сообщение
    */
    void uidPrint(String text = "") {
      Serial.print(F("UID: "));
      for(byte i=0; i<mfrc522.uid.size; i++) {
        Serial.print(mfrc522.uid.uidByte[i]);
        if(i < mfrc522.uid.size - 1) Serial.print(F(" "));
      }
      Serial.println();
      if(text.length() != 0) Serial.println(text + "\n");
    }
    /*
      Управление соленоидом с использованием пары реле.
      1 - закрытие, 0 - открытие
    */
    void lock(bool lock) {
      digitalWrite(lock ? PIN_RELAY_1 : PIN_RELAY_2, LOW);
      Serial.println(lock ? "RELAY CLOSE (PIN 6) ACTIVATED" : "RELAY OPEN (PIN 7) ACTIVATED");
      delay(400);
      digitalWrite(lock ? PIN_RELAY_1 : PIN_RELAY_2, HIGH);
      Serial.println(lock ? "RELAY CLOSE (PIN 6) DEACTIVATED" : "RELAY OPEN (PIN 7) DEACTIVATED");
      lockStat = lock;
    }
    /*
      Инициализация программы
    */
    void setup() {
      // Настраиваем сторожевой таймер
      wdt_disable();
      delay(8000); // <- ЗАКОМЕНТИРУЙТЕ ЭТУ СТРОКУ, ЕСЛИ ЗНАЕТЕ, ЧТО ТАКОЕ wdt_enable(WDTO_8S);
      wdt_enable(WDTO_8S);
        
      // Инициализация используемых пинов
      // Реле
      pinMode(PIN_RELAY_1, OUTPUT);
      digitalWrite(PIN_RELAY_1, HIGH);
      pinMode(PIN_RELAY_2, OUTPUT);
      digitalWrite(PIN_RELAY_2, HIGH);
      
      // Кнопка сброса памяти
      pinMode(PIN_RESET,INPUT_PULLUP);
      key_reset.attach(PIN_RESET);
      key_reset.interval(5);
      
      // Кнопка открытия двери
      pinMode(PIN_OPEN,INPUT_PULLUP);
      key_open.attach(PIN_OPEN);
      key_open.interval(5);   
    
      // Перемычка выбора режима работы (0 - автоматическое закрытие двери, 1 - закрытие двери по ключу)
      pinMode(PIN_MODE, INPUT);
    
      // Переменные для сигнализации
      pinMode(PIN_ALARM, INPUT_PULLUP);
    
      pinMode(PIN_ALARM_1M, OUTPUT);
      digitalWrite(PIN_ALARM_1M, invert ? HIGH : LOW);
    
      pinMode(PIN_ALARM_LONG, OUTPUT);
      digitalWrite(PIN_ALARM_LONG, invert ? HIGH : LOW);
      
      // Инициализация консоли
      Serial.begin(9600);
      while (!Serial);
      // Приглашаем в гости
      Serial.println(F("iT4iT CLUB (C) 2015\nhttps://it4it.club\n"));
      
      // Инициализация ридера
      SPI.begin();
      mfrc522.PCD_Init();
    
      // Читаем количество ключей
      // Значение должно быть равным или больше 1 т.к первый ключ это мастер
      // В случае утери мастер ключа, мы можем сбросить EEPROM и прикрутить новый мастер ключ
      if(keys_count > 0 and keys_count < 255) {
        keysRead();
        lock(true);
      }
      else {
        keys_count = 0;
        Serial.println(F("The master key is not in memory. The first presentation to the key will be the master!\n"));
        lock(false);
      }
    }
    /*
      Лупаем, что происходит
    */
    void loop() {
      // Сбрасываем сторожевой таймер микроконтроллера
      wdt_reset();
    
      if(alarmInterval > millis()+10000) alarmInterval = 0;
      if(alarmTimer > millis()+10000) alarmTimer = 0;
      if(resetTimer > millis()+10000) resetTimer = 0;
      if(openTimer > millis()+10000) openTimer = 0;
       
      // Изменение режима работы закрытия
      modeLock = !digitalRead(PIN_MODE);
      // Изменение режима работы сигнализации
      alarm    = !digitalRead(PIN_ALARM);
      // Очистка памяти
      key_reset.update();
      if(key_reset.read()) {
        if(resetTimer == 0) resetTimer = millis();
        else {
          if((millis()-resetTimer)/1000 > 5) {
            Serial.println(F("Launched memory cleaning"));
            squeaker(4, 1600, 300, 200);
            wdt_disable();
            for(int i=1; i<=EEPROM.length(); i++) {
              EEPROM.write(i, 0);
              if(!(i%50)) Serial.println(F("#")); else Serial.print(F("#"));
            }
            Serial.println(F("\nMemory cleaning is completed\n"));
            delay(1000);
            resetFunc();
          }
        }
      }
      else if(resetTimer != 0) resetTimer = 0;
      // Открытие двери с кнопки
      key_open.update();
      if(!key_open.read() and openTimer == 0) {
        if(keys_count > 0) {
          if(modeLock or (!modeLock and digitalRead(PIN_RELAY_2) == HIGH)) {
            openTimer = millis()/1000;
            lock(false);
            Serial.println(F("The door opened from the inside\n"));
            squeaker(5, 3200, 100, 300);
          }
        }
        else Serial.println(F("\nAttempting to open the lock from the inside.\nAccess denied. The master key is unknown. The castle is always open.\n"));
        delay(2000);
      }
      // Автоматическое отключение сигнализации
      if(alarmTimer != 0) {
        if(millis()/1000 - alarmTimer > 60) {
          alarmTimer = 0;
          digitalWrite(PIN_ALARM_1M, invert ? HIGH : LOW);
          Serial.println(F("Automatic shutdown of the first signaling channel.\n"));
        }
      }
      // Сброс счетчика подбора ключа
      if(alarmInterval != 0) {
        if(millis()/1000 - alarmInterval > 60) {
          alarmInterval = 0;
          alarmCount = 0;
          Serial.println(F("Time selection key expired.\n"));
        }
      }
      // Автоматическое закрытие двери
      if(openTimer != 0) {
        if(millis()/1000 - openTimer > 5) {
          openTimer = 0;
          if(modeLock) {
            lock(true);
            Serial.println(F("* closed lock\n"));
          }
        }
      }
      // Если ключ отсутствует или не читается, не выполняем дальнейший код
      if(!mfrc522.PICC_IsNewCardPresent()) {
        // Очистка таймера входа в режим программирования, в случае если ридер свободен
        if(modeTimer != 0) {
          if(++modeClean > 5) modeTimer = modeClean = 0;
        }
        return;
      }
      if(!mfrc522.PICC_ReadCardSerial()) return;
      // Останавливаем режим очистки
      modeClean = 0;
      
      // Кривое создание мастер ключа
      if(keys_count == 0) {
        for(byte i=0; i<4; i++) EEPROM.write(i+1, mfrc522.uid.uidByte[i]);
        EEPROM.write(0, keys_count = 1);
        uidPrint(F("master key is created"));
        lock(true);
        keysRead();
        squeaker(8, 1200, 100, 100);
        delay(2000);
        return;
      }
      
      // Проверка ключа на соответствие
      bool access = false;
      bool master = false;
      for(byte i=0; i<keys_count; i++) {
        for(byte b=0; b<4; b++) {
          if(keys[i][b] != mfrc522.uid.uidByte[b]) break;
          if(b == 3) {
            access = true;
            if(i == 0) master = true;
            // Останавливаем проверку костылем т.к "break 2;" не работает
            i = keys_count;
          }
        }
      }
      // ========================================================================================
      // Следующая секция имеет двойное назначение:
      //  1. В обычном режиме - контроль доступа
      //  2. В режиме программирования - запись ключа при его отсутствие в EEPROM
      // ========================================================================================
      // ОБЫЧНЫЙ РЕЖИМ
      // ========================================================================================
      if(access and !mode and !master) {
        // Доступ разрешен
        if(modeLock) {
          openTimer = millis()/1000;
          lock(false); 
        }
        else lock(!lockStat);
        if(alarm) {
          alarmInterval = alarmTimer = alarmCount = 0;
          if((invert and (!digitalRead(PIN_ALARM_1M) or !digitalRead(PIN_ALARM_LONG))) or (!invert and (digitalRead(PIN_ALARM_1M) or digitalRead(PIN_ALARM_LONG)))) {
            digitalWrite(PIN_ALARM_1M, invert ? HIGH : LOW);
            digitalWrite(PIN_ALARM_LONG, invert ? HIGH : LOW);
            Serial.println(F("Clear active alarms signaling\n"));
          }
        }
        uidPrint(F("access allow"));
        squeaker(2, 2200, 200, 200);
        delay(2000);
      }
      else if(!access and !mode and !master) {
        if(alarm) {
          if(alarmInterval == 0) alarmInterval = millis()/1000;
          if(++alarmCount >= 3) {
            if(alarmCount > 3) alarmCount = 3;
            alarmTimer = millis()/1000;
            digitalWrite(PIN_ALARM_1M, invert ? LOW : HIGH);
            digitalWrite(PIN_ALARM_LONG, invert ? LOW : HIGH);
            Serial.println(F("Attention! Attempt selection key! Activate both signaling channel!\n"));
          }
        }
        // Доступ запрещен
        uidPrint(F("access dany"));
        squeaker(1, 500, 1000);
        delay(2000);
      }
      // ========================================================================================
      // РЕЖИМ ПРОГРАММИРОВАНИЯ
      // ========================================================================================
      else if(access and mode and !master) {
        // Попытка записи существующего ключа
        uidPrint(F("error: key elrady exists in eeprom"));
        squeaker(2, 500, 300);
        delay(2000);
      }
      else if(!access and mode and !master) {
        // Записываем новый ключ
        // Максимум 255 ключей (с учетом первого байта) для 328 камня
        if(keys_count < 255) {
          for(byte i=0; i<4; i++) EEPROM.write(1 + keys_count*4 + i, mfrc522.uid.uidByte[i]);
          EEPROM.write(0, ++keys_count);
          uidPrint(F("add key in eeprom"));
          keysRead();
          squeaker(2, 2200, 200, 200);      
        }
        else {
          uidPrint(F("error: not enough memory for recording key!"));
          squeaker(2, 500, 300);
        }
        delay(2000);
      }
      // ========================================================================================
      // РАБОТА С МАСТЕР КЛЮЧОМ
      // ========================================================================================
      else if(access and master) {
        if(alarm) {
          alarmInterval = alarmTimer = alarmCount = 0;
          if((invert and (!digitalRead(PIN_ALARM_1M) or !digitalRead(PIN_ALARM_LONG))) or (!invert and (digitalRead(PIN_ALARM_1M) or digitalRead(PIN_ALARM_LONG)))) {
            digitalWrite(PIN_ALARM_1M, invert ? HIGH : LOW);
            digitalWrite(PIN_ALARM_LONG, invert ? HIGH : LOW);
            Serial.println(F("Clear active alarms signaling\n"));
          }
        }
        // Мастер ключ в обычном режиме
        if(modeTimer == 0) {
          modeTimer = millis()/1000;
          if(!mode) {
            if(modeLock) {
              openTimer = millis()/1000;
              lock(false); 
            }
            else lock(!lockStat);
            // Сигнал о наличии мастер ключа в обычном режиме
            uidPrint(F("MASTER KEY"));
            squeaker(2, 2200, 200, 200);
          }
        }
        else {
          if(millis()/1000 - modeTimer > modeProgTime and modeTimer != 0) {
            modeTimer = 0;
            if((mode = !mode) == true) {
              // Вход в режим программирования
              lock(false);
              uidPrint(F("MASTER PROGRAMMING MODE ON"));
              squeaker(4, 1200, 200, 200);
            }
            else {
              // Выход из режима программирования
              lock(true);
              uidPrint(F("MASTER PROGRAMMING MODE OFF"));
              squeaker(4, 2200, 200, 200);
            }
          }
          delay(2000);
          // Дополнительные действия по таймеру modeProgTime
        }
        // Мастер ключ удерживается у ридера
      }
    }

     

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


  14. Продолжим тему и модернизируем скрипт так, чтобы он мог работать с сервисом no-ip.com

    Ничего кардинально нового тут нет, скорее выбросили лишнее от предыдущего скрипта т.к логика работы сервиса проста и не требует принудительного "пинка" если ip адрес не менялся длительное время.

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

    Для запроса обновления данных зоны используется следующий http запрос

    http://username:password@dynupdate.no-ip.com/nic/update?hostname=mytest.testdomain.com&myip=1.2.3.4

    Нам нужно передать

    1. логин учетной записи сервиса
    2. пароль
    3. dns имя обновляемого узла
    4. новый ip адрес

    Все делаем по описанию в предыдущем посте за исключением самого скрипта.

    :local login "login";
    :local pass "password";
    :local ddnshost "you_zone.ddns.net";
    :local wan "wan";
    :local tmpFile "myddns.info";
    :local ddnsip [:resolve $ddnshost];
    :local localip [/ip addres get [/ip address find interface=$wan ] address];
    :local localip [:pick $localip 0 [:find $localip "/"]];
    :global ddnsinterval;
    :if ($localip != $ddnsip) do={
        /tool fetch url="http://$login:$pass@dynupdate.no-ip.com/nic/update?hostname=$ddnshost&myip=$localip" dst-path="$tmpFile"
        delay 2;
        :local fileid [/file find name="$tmpFile"];
        :local fileContent [/file get $fileid contents];
        /file remove $fileid;
        :log info "DDNS: mismatch DNS records, correction is made. $ddnshost ($ddnsip) changed to $localip";
    }

    Параметры:

    1. login - логин учетной записи no-ip.com
    2. pass - пароль
    3. ddnshost - Ваш домен. Указывается полностью. Помним, что no-ip.com предоставляет несколько зон для бесплатного использования
    4. wan - имя интерфейса, смотрящего в интернет 
    5. tmpFile - имя временного файла который будет создаваться в ходе работы скрипта и содержать ответ сервера

    Как и в предыдущий раз, прописываем удобный интервал запуска и тестируем.

    • Like 1

  15. to Alex13

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

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

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


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

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


  17. 50 минут назад, EndWar сказал:

    Что значит "или откроется" .?! Самим механизмом запора как я понял является электромагнит... отключи питание и двери настежь... )))

    Я уже думал об этом. Согласен, электромагнит это не тот вариант, который необходим для: квартиры, дома, дачи или иного жилого помещения. Такой тип замка используется сейчас в помещении на производстве, люди находятся в нем 24/7 и система питания помещения, как и самого здания, намного надежней, нежели в обычных жилых домах. На практике питание контроллера идет от 5V щины, а электромагнита от 12V. Управление открытием идет через реле. Если снять питание с контроллера, то дверь останется закрытой. В будущем планировалось поставить источник бесперебойного питания, но это уже будет реализовывать текущий эксплуататор в лице моих коллег и друзей.

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

    Если немного пофантазировать, то я бы попробовал реализовать свою копию RFID сканера на базе RC522, но с антенной, спрятанной в дверной ручке. В продаже также имеются RFID кольца различных размеров, цветов и форм. От классических перстней, до золотых с кожаными вставками. Ну а далее суть ясна, взялся за ручку и дверь разблокирована. Добро пожаловать домой.


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

    atom-clock.jpg

    Но вот, что делать, если хочется высокой точности? Верно, начинаем синхронизироваться с любым попавшимся NTP сервером в интернете, он в свою очередь синхронизируется с другим (возможно даже более точным, а может быть и нет) сервером. Цепочка может быть достаточно длинной, но в один прекрасный день может появится желание убрать посредников и найти самые точные часы на планете и спрашивать время у них.

    А какие часы самые точные? Атомные! Кстати на wikipedia имеется отличная статья про время! И вот тут пораскинув мозгами, начинаем судорожно вспоминать, в каком это супермаркете мы последний раз видели их в продаже и желательно с USB интерфейсом, можно даже розового цвета... Вы тоже не видели? Печаль.

    А ведь у нас над головой, на высоте порядка 19400 км висят куча спутников систем позиционирования (GPS / ГЛОННАС), а чтобы они работали им нужно знать точное время. Именно для этой цели у каждого из этих трудяг на борту имеются атомные часы! Отлично, давайте узнавать время у этих ребят.

    И так, немного задержавшись на сайте MikroTik можно найти пакет gps-xx.xx.npk который позволяет расширить возможности маршрутизатора и позволит ему определять свои точные координаты на поверхности нашей планеты. Чтобы установить пакет, достаточно просто скопировать его на маршрутизатор (сделать это можно перетащив .npk файл в файловый менеджер WinBox) и перезагрузиться.

    Теперь нам нужен GPS приемник. Общение будет идти по протоколу NMEA 0183, PDF.

    Скрытый текст
    
    Simple Text Output Format:
    
    The simple text (ASCII) output contains time, position, and velocity data in
    the fixed width fields (not delimited) defined in the following table:
    
        FIELD DESCRIPTION:      WIDTH:  NOTES:
        ----------------------- ------- ------------------------
        Sentence start          1       Always '@'
        ----------------------- ------- ------------------------
       /Year                    2       Last two digits of UTC year
      | ----------------------- ------- ------------------------
      | Month                   2       UTC month, "01".."12"
    T | ----------------------- ------- ------------------------
    i | Day                     2       UTC day of month, "01".."31"
    m | ----------------------- ------- ------------------------
    e | Hour                    2       UTC hour, "00".."23"
      | ----------------------- ------- ------------------------
      | Minute                  2       UTC minute, "00".."59"
      | ----------------------- ------- ------------------------
       \Second                  2       UTC second, "00".."59"
        ----------------------- ------- ------------------------
       /Latitude hemisphere     1       'N' or 'S'
      | ----------------------- ------- ------------------------
      | Latitude position       7       WGS84 ddmmmmm, with an implied
      |                                 decimal after the 4th digit
      | ----------------------- ------- ------------------------
      | Longitude hemishpere    1       'E' or 'W'
      | ----------------------- ------- ------------------------
      | Longitude position      8       WGS84 dddmmmmm with an implied
    P |                                 decimal after the 5th digit
    o | ----------------------- ------- ------------------------
    s | Position status         1       'd' if current 2D differential GPS position
    i |                                 'D' if current 3D differential GPS position
    t |                                 'g' if current 2D GPS position
    i |                                 'G' if current 3D GPS position
    o |                                 'S' if simulated position
    n |                                 '_' if invalid position
      | ----------------------- ------- ------------------------
      | Horizontal posn error   3       EPH in meters
      | ----------------------- ------- ------------------------
      | Altitude sign           1       '+' or '-'
      | ----------------------- ------- ------------------------
      | Altitude                5       Height above or below mean
       \                                sea level in meters
        ----------------------- ------- ------------------------
       /East/West velocity      1       'E' or 'W'
      |     direction
      | ----------------------- ------- ------------------------
      | East/West velocity      4       Meters per second in tenths,
      |     magnitude                   ("1234" = 123.4 m/s)
    V | ----------------------- ------- ------------------------
    e | North/South velocity    1       'N' or 'S'
    l |     direction
    o | ----------------------- ------- ------------------------
    c | North/South velocity    4       Meters per second in tenths,
    i |     magnitude                   ("1234" = 123.4 m/s)
    t | ----------------------- ------- ------------------------
    y | Vertical velocity       1       'U' (up) or 'D' (down)
      |     direction
      | ----------------------- ------- ------------------------
      | Vertical velocity       4       Meters per second in hundredths,
       \    magnitude                   ("1234" = 12.34 m/s)
        ----------------------- ------- ------------------------
        Sentence end            2       Carriage return, '0x0D', and
                                        line feed, '0x0A'
        ----------------------- ------- ------------------------
    
    If a numeric value does not fill its entire field width, the field is padded
    with leading '0's (eg. an altitude of 50 meters above MSL will be output as
    "+00050").
    
    Any or all of the data in the text sentence (except for the sentence start
    and sentence end fields) may be replaced with underscores to indicate
    invalid data.

     

    Мой маршрутизатор собран на базе стационарного компьютера, а следовательно можно смело брать приемник с USB интерфейсом. В наличии имеется отличный экземпляр от GlobalSat - BU-353U4.

    Скрытый текст

    m-ntp-gps-2.jpg

    Скрытый текст

    m-ntp-gps-1.jpg

    Скрытый текст

    m-ntp-gps-3.jpg

    Скрытый текст

    m-ntp-gps-4.jpg

    После подключения приемника переходим к настройки маршрутизатора.

    [admin@Kitsum] > port print
    Flags: I - inactive 
     #   DEVICE NAME                              CHANNELS USED-BY                            BAUD-RATE
     0          serial0                                  1 Serial Console                     9600     
     1   3:2    usb2                                     1                                    4800     
    

    Отлично, маршрутизатор видит что-то на порту usb2, запоминаем его Flag (1) и выставляем настройки согласно техническим характеристикам от производителя. Естественно, что для Вашего "свистка" настройки могут отличаться.

    [admin@Kitsum] > /port
    [admin@Kitsum] /port> set numbers=1 baud-rate=4800 data-bits=8 parity=none flow-control=none stop-bitsrs=1

    Все настройки с портами Вы можете провести через Web интерфейс или WinBox в разделе System -> Ports

    Скрытый текст

    m-ntp-gps-5.png

    Теперь осталось дело за малым. Сообщим маршрутизатору, что на порту usb2 висит GPS приемник, и он может им овладеть, то есть воспользоваться, ... блин использовать! И естественно, мы желаем, чтобы время, полученное в телеграмме от спутников считалось эталонным и использовалось как системное.

    [admin@Kitsum] > /system gps
    [admin@Kitsum] /system gps> set enabled=yes port=usb2 set-system-time=yes
    Скрытый текст

    m-ntp-gps-6.png

    Теперь мы может посмотреть, какими именно данными мы начали располагать в следствии, этих сверх сложных манипуляций!

    Внимание: естественно, что при холодном старте, данные начнут поступать не сразу.

    [admin@Kitsum] > /system gps 
    [admin@Kitsum] /system gps> monitor 
            date-and-time: feb/08/2016 14:44:42
                 latitude: N 00 18' 13.236''
                longitude: E 00 54' 5.236''
                 altitude: 325.299988m
                    speed: 0.000000 km/h
      destination-bearing: none
             true-bearing: 133.419998 deg. True
         magnetic-bearing: none
                    valid: yes
               satellites: 9
    -- [Q quit|D dump|C-z pause]

    В данном случае получилось поймать сигнал с 9 спутников. Каждый из них передает нам свои координаты, а самое главное - время со встроенных атомных часов. Большая часть информации вычисляется уже по месту (скорость, высота ...).  Мы практически добились нужного результата.

    Естественно мы получаем время по Гринвичу (UTC+0), меня это не устраивает, видимо и Вас тоже. Необходимо подправить часовой пояс. Это можно сделать в System -> Clock

    [admin@Kitsum] > /system clock
    [admin@Kitsum] /system clock> set time-zone-autodetect=no time-zone-name=Europe/Moscow
    Скрытый текст

    m-ntp-gps-7.png

    И так, теперь Ваш маршрутизатор знает самое точное время на планете и им стоит поделиться хотя бы со всеми желающими в локальной сети. А таких поверьте, будет не мало. В современном мире все устройства жаждут обладать этой информации. Как же без этого Вам кофеварка сварит благородный напиток, или телевизор выключится и оградит молодое поколение от всего того, что оно и так потом узнает, а пока должно делать уроки, ммм?

    Необходимо поднять собственный NTP сервер и делается это сверх сложной и длинной командой.

    [admin@Kitsum] > /system ntp server
    [admin@Kitsum] /system ntp server> set enabled=yes manycast=yes

    Ну, или просто выставлением соответствующей галочки в System -> NTP Server

    И все было бы просто великолепно, если бы устройства в сети знали, что мы владеем более точным временем, чем time.windows.comtime.nist.gov да и сотни других серверов. Первая мысль, которая посетила меня - просто подменю ip адреса в записях DNS и "вуаля", но я даже понятия не имею с чем синхронизируется мой телефон, телевизор и у кого, после очередного обновления, захочет узнать время мой компьютер.

    Мы знаем, что NTP протокол использует 123 UDP порт для общения. Предлагаю перехватывать все запросы, из нашей локальной сети, адресованные на этот порт во внешний мир и заворачивать их на внутренний IP маршрутизатора!

    У меня все сетевые интерфейсы локальной сети объедены в Bridge и весь трафик ходит во внешний мир через через NAT и masquerade, внутренний адрес маршрутизатора 10.10.10.1

    Добавим новое правило в NAT

    [admin@Kitsum] > /ip firewall nat
    [admin@Kitsum] /ip firewall nat> add action=dst-nat chain=dstnat dst-port=123 in-interface=bridge1 protocol=udp to-addresses=10.10.10.1 to-ports=123

    На этом все.

    PS: зачем? потому что хочется и есть возможность!

    • Like 2

  19. В продолжение темы будем пробовать подружить Arduino + RFID ридер MFRC522 c Ethernet и MySQL базой данных, в которой будут храниться наши ключи. Автором идеи является svchekalin.

    Это первый вариант реализации СКУД на Arduino с использование БД, уверен, что в дальнейшем получится значительно улучшить работу программы.

    rfid_e1.thumb.jpg.6aadfae386a718db5286f6

    Первым делом необходимо озаботиться созданием самой базы для ключей. Подразумевается, что раз Вас интересует вопрос хранения ключей не в EEPROM, а во внешней СУБД, то Вы имеете представление о MySQL, работе с ней и сам сервер базы данных у Вас имеется. Я не буду описывать тут процесс его установки и настройки т.к. эта тема не для этого раздела.

    Создадим базу данных с именем "test" и добавим в ней таблицу с именем rc522 со следующей схемой:

    CREATE TABLE IF NOT EXISTS `rc522` (
      `uid` bigint(12) NOT NULL,
      `type` enum('0','1','2') NOT NULL DEFAULT '2',
      `description` varchar(250) DEFAULT NULL,
      UNIQUE KEY `uid` (`uid`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    1. uid - содержит id ключа
    2. type - тип ключа. Может быть один из трех вариантов: 0 - ключ в базе, но деактивирован, 1 - мастер ключ, 2 - обычный ключ
    3. description - поле не используется в работе замка. Подразумевается, что в дальнейшем будет использоваться некая web форма для работы с ключами, и данное поле будет содержать некое описание, например ФИО владельца ключа.

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

    iT4iT CLUB (C) 2015
    https://it4it.club
    
    Connected to server version 5.5.47-0+deb7u1
    KEY: 3177510
    access dany
    Disconnected.

    Чтобы добавить ключ 3177510 в базу и сделать его мастером необходимо выполнить SQL запрос:

    INSERT INTO `rc522` (`uid`, `type`) VALUES (3177510, '1');

    База и таблица созданы, мастер ключ добавлен, переходим к монтажу электронной части.

    Я использовал Arduino UNOEthernet Shield на чипе W5100 (если Вы хотите подвязать ENC28J60, сразу советую отказаться от этой идеи). Сам шилд и библиотека Ethernet.h используют заранее определенные контакты SPI шины, это:

    • 13 - SCK
    • 12 - MISO
    • 11 - MOSI
    • 10 - SS

    В тоже время RFID сканер MFRC522 тоже использует SPI шину, и чтобы он чувствовал себя не ущемленным в правах, необходимо его пины SS и RST перенести:

    • 13 - SCK
    • 12 - MISO
    • 11 - MOSI
    • 9 - SS
    • 8 - RST

    Все это дело будет выглядеть следующим образом.

    nrf_ethernet_z1.thumb.jpg.27f68f50ed9caa

    Переходим к скетчу. Для его работы понадобятся следующие дополнительные библиотеки:

    1. RFID-RC522 (NRF)
    2. Bounce2 (избавляет от дребезга контактов при нажатии кнопок)
    3. MySQL Connector (реализует работу с базой данных)

    PayPal.png

    Сам скетч

    Скрытый текст
    
    // Необходимые библиотеки
    #include <avr/wdt.h>          
    #include <SPI.h>
    #include <MFRC522.h>
    #include <Bounce2.h>
    #include <Ethernet.h>
    #include <MySQL_Connection.h>
    #include <MySQL_Cursor.h>
    
    // Необходимые пины
    #define PIN_SS         9  // RFID
    #define PIN_RST        8  // RFID
    
    #define PIN_RELAY      7  // RELAY
    #define PIN_MODE       6  // MODE
    #define PIN_TONE       3  // TONE
    #define PIN_OPEN       2  // OPEN
    
    MFRC522 mfrc522(PIN_SS, PIN_RST);
    
    byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
    IPAddress server(10, 10, 10, 254);  // IP адрес MySQL сервера
    char user[] = "login";               // MySQL username
    char password[] = "password";        // MySQL password
    
    const char QUERY_S[] = "SELECT type FROM test.rc522 WHERE uid = %s;";
    const char QUERY_I[] = "INSERT INTO test.rc522 (uid) VALUES ('%s');";
    char query[128];
    
    EthernetClient client;
    MySQL_Connection conn((Client *)&client);
    MySQL_Cursor cur = MySQL_Cursor(&conn);
    
    //
    String key, lastKey;
    String typeKey;
    char toQuery[12];
    unsigned long lastKeyTimer  = 0;
    
    // Переменные необходимые для режима программирования
    byte modeProgTime           = 5;     // Количество секунд удержания мастер ключа для входа\выхода в\из режим\а программирования
    bool mode                   = false; // НЕ МЕНЯТЬ!
    bool modeLock               = false; // НЕ МЕНЯТЬ!
    byte modeClean              = 0;     // НЕ МЕНЯТЬ!
    unsigned long modeTimer     = 0;     // НЕ МЕНЯТЬ!
    
    // Управление замком
    unsigned long openTimer = 0;
    
    // Защита кнопок от дребезга
    Bounce key_open  = Bounce();
    /*
      Функция звукового оповещения.
      Принимает параметры: количество звуковых сигналов, частота в герцах, продолжительность звука, пауза в миллисекундах (не обязательно)
    */
    void squeaker(byte count, unsigned int Hz, unsigned int duration, unsigned int sleep = 0) 
    {
      for(int i=0; i<count; i++) {
        tone(PIN_TONE, Hz, duration);
        if(sleep > 0) delay(sleep);
      }
    }
    /*
      Функция читает EEPROM и составляет список активных ключей
      Первый байт в памяти содержит количество ключей
      UID ключа содержит 4 байта
      Общая память 1 + количество ключей * 4
      Максимум можно записать 255 ключей
    */
    void setup() {
      // Настраиваем сторожевой таймер
      wdt_disable();
      delay(8000);
      wdt_enable(WDTO_8S);
    
      // Инициализация используемых пинов
      // Реле
      pinMode(PIN_RELAY, OUTPUT);
      digitalWrite(PIN_RELAY, HIGH);
       
      // Кнопка открытия двери
      pinMode(PIN_OPEN,INPUT_PULLUP);
      key_open.attach(PIN_OPEN);
      key_open.interval(5);   
    
      // Перемычка выбора режима работы (0 - автоматическое закрытие двери, 1 - закрытие двери по ключу)
      pinMode(PIN_MODE, INPUT);
      digitalWrite(PIN_MODE, HIGH);
    
      // Инициализация консоли
      Serial.begin(115200);
      while (!Serial);
    
      // Инициализация ридера
      SPI.begin();
      mfrc522.PCD_Init();
    
      Ethernet.begin(mac);
      Serial.print(F("IP: "));
      Serial.println(Ethernet.localIP());
      Serial.println();
      
      // Приглашаем в гости
      Serial.println(F("iT4iT CLUB (C) 2015\nhttps://it4it.club\n"));      
    }
    
    void loop() {
      // Сбрасываем сторожевой таймер микроконтроллера
      wdt_reset();
    
      if(openTimer > millis()+10000) openTimer = 0;
      if(lastKeyTimer > millis()+10000) lastKeyTimer = 0;
    
      // Изменение режима работы закрытия
      modeLock = !digitalRead(PIN_MODE);
    
      // Открытие двери с кнопки
      key_open.update();
      if(!key_open.read() and openTimer == 0 and !mode) {
        if(modeLock or (!modeLock and digitalRead(PIN_RELAY))) {
          openTimer = millis()/1000;
          digitalWrite(PIN_RELAY, LOW);
          Serial.println(F("The door opened from the inside\n"));
          squeaker(5, 3200, 100, 300);
        }
        delay(2000);
      }
      
      // Автоматическое закрытие двери
      if(openTimer != 0) {
        if(millis()/1000 - openTimer > 5) {
          openTimer = 0;
          if(modeLock) {
            digitalWrite(PIN_RELAY, HIGH);
            Serial.println(F("* closed lock"));
          }
          else {
            if(!digitalRead(PIN_RELAY)) digitalWrite(PIN_RELAY, HIGH);
          }
        }
      }
      
      // Если ключ отсутствует или не читается, не выполняем дальнейший код
      if(!mfrc522.PICC_IsNewCardPresent()) {
        // Очистка таймера входа в режим программирования, в случае если ридер свободен
        if(modeTimer != 0) {
          if(++modeClean > 5) modeTimer = modeClean = 0;
        }
        // Таймер для контроля времени хранения последнего ключа в памяти
        if(lastKeyTimer != 0) {
          if((millis()-lastKeyTimer) > 100) {
            lastKey = "";
            lastKeyTimer = 0;
          }
        }
        return;
      }
      if(!mfrc522.PICC_ReadCardSerial()) return;
      // Останавливаем режим очистки
      modeClean = 0;
    
      // Читаем ключ
      for(byte i=0; i<4; i++) key += mfrc522.uid.uidByte[i];
      // Один ключ = Один запрос к MySQL серверу. Не флудим.
      if(key != lastKey) {
        if(!conn.connected()) {
          if(!conn.connect(server, 3306, user, password)) {
            squeaker(5, 1000, 200, 200);
            return;
          }
        }
        
        key.toCharArray(toQuery, key.length()+1);
        sprintf(query, QUERY_S, toQuery);
        cur.execute(query);
    
        column_names *cols = cur.get_columns();
        row_values *row = NULL;
        Serial.println("KEY: " + key);
        if((row = cur.get_next_row()) != NULL) typeKey = String(row->values[0]); else typeKey = "0";
        if(typeKey == F("1") or typeKey == F("2")) {
          if(!mode) {
            Serial.println(F("access allow"));
            // Доступ разрешен
            if(modeLock) {
              openTimer = millis()/1000;
              digitalWrite(PIN_RELAY, LOW); 
            }
            else digitalWrite(PIN_RELAY, !digitalRead(PIN_RELAY));
            squeaker(2, 2200, 200, 200);
          }
          else {
            if(typeKey != F("1")) {
              Serial.println(F("error: key elrady exists in eeprom"));
              squeaker(2, 500, 300);
            }
          }
        }
        else {
          if(!mode) {
            Serial.println(F("access dany"));
            squeaker(1, 500, 1000);
          }
          else {
            Serial.println(F("add key in MySQL"));
            sprintf(query, QUERY_I, toQuery);
            cur.execute(query);
            squeaker(2, 2200, 200, 200);
          }
        }
    
        lastKey = key;
        cur.close();
        conn.close();
      }
      else {
        if(typeKey == F("1")) {
          if(modeTimer == 0) modeTimer = millis()/1000;
          else {
            if(millis()/1000 - modeTimer > modeProgTime and modeTimer != 0) {
              modeTimer = openTimer = 0;
              if((mode = !mode) == true) {
                // Вход в режим программирования
                digitalWrite(PIN_RELAY, LOW);
                Serial.println(F("MASTER PROGRAMMING MODE ON"));
                squeaker(4, 1200, 200, 200);
              }
              else {
                // Выход из режима программирования
                digitalWrite(PIN_RELAY, HIGH);
                Serial.println(F("MASTER PROGRAMMING MODE OFF"));
                squeaker(4, 2200, 200, 200);
              }
              delay(2000);
            }
          }
        }
      }
      
      lastKeyTimer = millis();
      key = "";
    }

     

    Сразу хочу уточнить некоторые моменты.

    В программе Вам необходимо указать имя пользователя и пароль к MySQL серверу. Пользователь должен обладать достаточными правами для доступа к созданной в начале статьи базе. Также не забудьте указать IP адрес самого сервера в Вашей локальной сети.

    IPAddress server(10, 10, 10, 254);  // IP адрес MySQL сервера
    char user[] = "login";               // MySQL username
    char password[] = "password";        // MySQL password

    Если Вы решили изменить имя базы или таблицы на какие-нибудь другие, то необходимо отредактировать константы, содержащие SQL запросы.

    const char QUERY_S[] = "SELECT type FROM test.rc522 WHERE uid = %s;";
    const char QUERY_I[] = "INSERT INTO test.rc522 (uid) VALUES ('%s');";

    Как видите, имя базы и таблицы указаны через разделитель точку ".", и в данном примере база имеет имя test а таблица rc522, что соответствует записи test.rc522

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

    Связь с базой и последующая обработка ответа происходит с некоторой задержкой, это связано с производительностью контроллера, особенностями работы библиотеки, самой программы и т.д и т.п. В моем случае, время с момента начала считывания метки до реакции на неё занимало до 3 сек. Плюс ко всему, из-за определенных обстоятельств, MySQL сервер пришлось поднимать на Raspberry PI, а это далеко не лучший вариант для СУБД.

    Скрытый текст

    rfid_e2.thumb.jpg.b212a959413c4f42f5b3f6

    В данный момент на одну аутентификацию метки требуется создать одно подключение и один запрос, это тоже увеличивает время реакции. Можно устанавливать соединение с базой при подачи питания на Arduino и просто клепать запросы. Такой вариант уменьшит время реакции в два раза, НО есть ложка дегтя в этой бочке. Если по каким либо причинам связь с сервером будет прервана (а оно так и будет, т.к. 100% гарантию дает только страховой полис), то контроллер зависнет при попытке выполнить заброс к БД. Проснуться он сможет только с помощью сторожевого таймера. В общем, этот момент еще будет проработан в следующих версиях замка. Думаю, что с нормальной СУБД время реакции уменьшится до 2 сек.

    Для входа в режим программирования необходимо, после того как замок разблокируется, удерживать мастер ключ еще 5 секунд у RFID сканера. Произойдет звуковой сигнал, а замок откроется, и будет находиться в таком состоянии пока Вы не выйдите из режима программирования (повторно удерживаем мастер ключ у сканера до звукового сигнала). В режиме программирования, как и в предыдущих версиях замка, все новые ключи будут записаны в MySQL.

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

    IP: 10.10.10.97                               # <- IP адрес полученный от DHCP сервера
    
    iT4iT CLUB (C) 2015
    https://it4it.club
    
    Connected to server version 5.5.47-0+deb7u1   # <- Поднесен новый ключ. Подключаемся к MySQL серверу
    KEY: 26214177213                              # <- ID текущего ключа у RC522 сканера
    access allow                                  # <- Доступ разрешен (ключ в базе с меткой 1 - мастер)
    Disconnected.                                 # <- Отключаемся от сервера
    * closed lock                                 # <- Замок автоматически закрылся, НО КЛЮЧ ВСЕ ЕЩЕ У СКАНЕРА!
    MASTER PROGRAMMING MODE ON                    # <- После 5сек. Вход в режим программирования.
    Connected to server version 5.5.47-0+deb7u1   # <- Поднесен новый ключ. Подключаемся к MySQL серверу
    KEY: 204641111                                # <- ID текущего ключа у RC522 сканера 
    error: key elrady exists in eeprom            # <- ID ключа уже прописан на сервере <!>
    Disconnected.                                 # <- Отключаемся от сервера
    Connected to server version 5.5.47-0+deb7u1   # <- Поднесен новый ключ. Подключаемся к MySQL серверу
    KEY: 3177510                                  # <- ID текущего ключа у RC522 сканера 
    add key in MySQL                              # <- ID ключа не найден в СУБД. Arduino завписывает новый ключ в базу.
    Disconnected.                                 # <- Отключаемся от сервера
    Connected to server version 5.5.47-0+deb7u1   # <- ID текущего ключа у RC522 сканера 
    KEY: 26214177213                              # <- ID ключа найден в СУБД (ключ в базе с меткой 1 - мастер)
    Disconnected.                                 # <- Отключаемся от сервера
    MASTER PROGRAMMING MODE OFF                   # <- Выходим из режима программирования

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

    • Like 5

  20. 1 час назад, svchekalin сказал:

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

    Думаю, что точного ответа не существует в природе. Надо поднимать спецификацию на SPI, информацию о кабеле, внешней среде и погружаться с калькулятором и кучей формул в расчеты.

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

×
×
  • Создать...