RFID, геркон и все-все-все

Тема в разделе "Arduino & Shields", создана пользователем xsash, 31 авг 2016.

  1. xsash

    xsash Нерд

    Добрый! Ну что ж, прошла неделя с покупки набора ардуино и пробы "пера", а вот теперь появились вопросы. Прошу помощи у сообщества, если вопросы глупые - дайте хоть вектор куда копать, какой код глянуть

    Суть - пишу простую GMS сигнализацию (коих достаточно, но интересен сам процесс набивания шишек). Прикладываем ключ, система ставится под охрану. Второй раз прикладываем - снимается с охраны. Пока в режиме охраны должен идти опрос геркона. Датчик пока один - геркон на двери, потом планирую добавить.

    Итого надо в цикле опрашивать все датчики + считывать данные с RFID

    Дано: ардуино мега, RFID считыватель (RC522), простой геркон, пара LED (красный и зеленый).



    Сразу столкнулся с тем, что поиск карты останавливает loop(). Использовал за основу стандартный код.
    Код (Text):
    if ( ! mfrc522.PICC_IsNewCardPresent()) { return; }
    В итоге, пока решил использовать прерывание (для одного датчика, хотя для 3-4-5 это имхо не выход).
    Но вот тут столкнулся с тем, что при срабатывании геркона всегда получаю "единицу" на пин 2.
    Приложил магнит, убрал магнит - всегда считывается 1. (Аппаратного или программного "анитдребезга" нет)

    Сам код полностью (он страшноват, вижу часть моментов, которые надо переделать)
    Код (C++):

    #include <SPI.h>
    #include <MFRC522.h>

    #define SS_PIN 53
    #define RST_PIN 9

    // объект MFRC522
    MFRC522 mfrc522(SS_PIN, RST_PIN);

    // для отображения номера карточки в десятичном формате
    unsigned long idKey, idKeyTemp;
    byte bCounter, readBit;
    unsigned long ticketNumber;

    /* Переменные Pin */
    int pinRed = 6;
    int pinGreen = 7;
    int pinDoor = 0;

    /* Массив ключей */
    char* myKeys[] = {"1111111111", "2222222222"};

    String txtKey = "";

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


    void printInfo(String txt)
    {
        Serial.println(txt);
    }


    boolean checkKey(String txt)
    {
        boolean validKey = false;

        for (int i = 0; i < 2; i++)
        {
            if ((txtKey == myKeys[i]) && (!validKey))
            {
                validKey = true;
            }
        }

        if (validKey)
        {
            return true; //ключ найден в массиве
        }
        else
        {
            return false; //ключ не найден в массиве
        }
    }


    void setup()
    {
        Serial.begin(9600);

        // инициализация SPI
        SPI.begin();

        // инициализация MFRC522
        mfrc522.PCD_Init();

        pinMode(pinRed, OUTPUT);
        pinMode(pinGreen, OUTPUT);

        pinMode(pinDoor, INPUT);
        attachInterrupt(pinDoor, swap, CHANGE );
    }

    void swap()
    {
    /*
        if (digitalRead(pinDoor) == LOW)
        {
            Serial.println("Door open");
        }
        else
        {
            Serial.println("Door close");
        }
    */


        Serial.println(digitalRead(pinDoor));
        //тут всегда единица
    }


    void loop()
    {
        // Поиск новой карточки
        if ( ! mfrc522.PICC_IsNewCardPresent()) { return; }

        // Выбор карточки
        if ( ! mfrc522.PICC_ReadCardSerial()) { return; }

        idKey = 0;

        // Выдача серийного номера карточки
        printInfo("Card UID:");

        for (byte i = 0; i < mfrc522.uid.size; i++)
        {
            idKeyTemp = mfrc522.uid.uidByte[i];
            idKey = idKey*256+idKeyTemp;
        }
        txtKey = String(idKey);
        printInfo(txtKey);

        if (checkKey(txtKey))
        {
            digitalWrite(pinGreen, HIGH);
            digitalWrite(pinRed, LOW);
        }
        else
        {
            digitalWrite(pinRed, HIGH);
            digitalWrite(pinGreen, LOW);
        }

        // Halt PICC
        mfrc522.PICC_HaltA();
    }
     
    В перывании менял режим обработки. Все равно при срабатывании на выходе единица, а хотелось бы считывания состояние геркона, чтобы понимать открыта дверь или нет (как вариант можно менять переменную логическую при срабатывании геркона).

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

    ostrov Гуру

    Для начала рекомендую библиотеку MFRC522.h заменить на RFID.h она менее глючная и код получается короче, что иногда критично.
     

    Вложения:

    • RFID.zip
      Размер файла:
      6 КБ
      Просмотров:
      366
    Tomasina и xsash нравится это.
  3. DIYMan

    DIYMan Guest

    Странный вы - pinDoor у вас описан как 0, читаете вы, соответственно, с пина аппаратного UART. Вы чего там хотите увидеть-то? Если хотите юзать прерывания, то это пины 2 и 3, соответственно код будет:
    Код (C++):
    #define pinDoor 2
    void swap()
    {
    Serial.println(digitalRead(pinDoor));
    }
    void setup()
    {
     attachInterrupt(digitalPinToInterrupt(pinDoor),swap,CHANGE);
    }
     
    Разницу видите? ;)
     
    xsash нравится это.
  4. xsash

    xsash Нерд

    Физически геркон у меня висит на втором пине, а ноль указан в соответствии с этим видео



    тем более с "нулевого" пина я получаю событие о срабатывании, но именно событие, а не состояние.

    попробую упростить вопрос - как параллельно считывать ключи и опрашивать N датчиков. Библиотеку гляну после обеда, спасибо, может там будет проще
     
  5. DIYMan

    DIYMan Guest

    Ещё раз: вы читаете состояние не второго пина, а нулевого. Вы вообще разницу понимаете между номером прерывания и номером пина? Я вам привёл полный пример, как номер пина в рамках Arduino IDE переводится в номер прерывания. Хотите делать по своему - пожалуйста, но тогда не удивляйтесь, почему не работает.

    Повторюсь: прерывания на Uno доступны на пинах 2 и 3, прерывание на пине 2 имеет номер 0, прерывание на пине 3 - номер 1. Для переносимости кода под другие дуины есть функция digitalPinToInterrupt, строго рекомендуется юзать именно её, а не писать какашечный код, руководствуясь какашечными видео. А если хотите читать из пина, на который повешен обработчик прерывания - то надо указывать в digitalRead не номер прерывания, как вы делаете сейчас, а номер пина.
     
  6. xsash

    xsash Нерд

    Да, у меня были вчера такие мысли, но не успел попробовать на практике, уже сморило. Конечно, скорее всего вы правы )

    Тем не менее в моем случае получится, что я смогу задействовать столько датчиков, сколько есть прерываний. имхо логичнее в loop() пробегать по всем датчикам, проверяя их состояние, но как тогда считывать карту...
     
  7. DIYMan

    DIYMan Guest

    Юзайте таймеры, они работают по прерываниям. Возьмите, например, библиотеку Timer One, она лежит на офсайте ардуино - и всё будет. Настройте интервал между опросами датчиков, взведите таймер - и по каждому прерыванию с таймера читайте с очередного датчика. что при этом происходит в loop - уже никого не волнует :)
     
  8. xsash

    xsash Нерд

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

    Я правильно понимаю, что вынести rfid в принципе из loop не возможно (без дополнительных кнопок, датчиков...)? Либо городить отдельный контроллер на ардуино со считывателем?
     
  9. Tomasina

    Tomasina Сушитель лампочек Модератор

    все возможно :)
    Вынеси в отдельные функции чтение RFID, опрос датчиков, работу с LED и пр.
    А в loop просто вызывай их с нужной периодичностью.
     
  10. ostrov

    ostrov Гуру

    Честно говоря так и не понял в чем проблема. Не получается одновременно опрашивать геркон и рфид?
     
  11. xsash

    xsash Нерд

    да, сейчас попробую резюмировать

    1) если использовать MFRC522.h и стандартный пример dumpinfo, то там loop выглядит следующим образом
    Код (C++):
    void loop() {
        // Look for new cards
        if ( ! mfrc522.PICC_IsNewCardPresent()) {
            return;
        }

        // Select one of the cards
        if ( ! mfrc522.PICC_ReadCardSerial()) {
            return;
        }

        // Dump debug info about the card; PICC_HaltA() is automatically called
        mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
    }
    т.е. код не выполнится, пока не будет приложена карта/ключ.
    Значит надо либо проверку ключа выносить из loop(), либо проверку датчиков.
    Как варианты выше - отдельный датчик вешать на прерывание, либо использовать прерывание по таймеру.

    2) если использовать RFID.h, то код будет уже выглядеть так
    Код (C++):

    if (rfid.isCard())
        {
            if (rfid.readCardSerial())
                {
                    Serial.print(rfid.serNum[0]);
                    Serial.print(",");
                    Serial.print(rfid.serNum[1]);
                    Serial.print(",");
                    Serial.print(rfid.serNum[2]);
                    Serial.print(",");
                    Serial.print(rfid.serNum[3]);
                    Serial.print(",");
                    Serial.print(rfid.serNum[4]);
                    Serial.println("");

                    for (int x = 0; x < sizeof(cards); x++)
                        {
                            for (int i = 0; i < sizeof(rfid.serNum); i++ )
                                {
                                    if (rfid.serNum[i] != cards[x][i])
                                        {
                                            access = false;
                                            break;
                                        }
                                    else
                                        {
                                            access = true;
                                        }
                                }

                            if (access) break;
                        }
                }

            if (access)
                {
                    Serial.println("Key accept!");
                }
            else
                {
                    Serial.println("Key not found!");
                }
        }

    rfid.halt();
     
    этот код не останавливает выполнение любого другого


    Но в первом случае есть некий плюс - карту поднесли и можно держать, она считывается один раз, во втором случае - карта читается "много" раз, пока не уберешь
     
    Крапивин нравится это.
  12. ostrov

    ostrov Гуру

    Домашнее задание, перепишите вот это:
    Код (C++):
                    Serial.print(rfid.serNum[0]);
                    Serial.print(",");
                    Serial.print(rfid.serNum[1]);
                    Serial.print(",");
                    Serial.print(rfid.serNum[2]);
                    Serial.print(",");
                    Serial.print(rfid.serNum[3]);
                    Serial.print(",");
                    Serial.print(rfid.serNum[4]);
                    Serial.println("");
    одной строкой.

    Не помню чтобы MFRC522.h тормозил всю программу. Он работает аналогично с Serial.available, то есть опрашивает рфид на предмет приложена ли метка, если нет то проскакивает дальше, если да, то считывает данные в массив и опять таки проскакивает дальше. Ваше дело отследить получены ли данные и что то с ними делать. Библиотека RFID.h хороша тем, что чувствует когда метку убрали.
     
    Последнее редактирование: 1 сен 2016
  13. ostrov

    ostrov Гуру

    Вот например так хреначит без задержек, перед последней фигурной скобкой ставьте что угодно и вперед. Ну и, само собой, в этом случае заменить delay() на работу с millis():
    Код (C++):
    void loop() {
      if ( ! mfrc522.PICC_IsNewCardPresent())
        //    cardUID[0] = 0;
        return;
      if ( ! mfrc522.PICC_ReadCardSerial())
        //    cardUID[0] = 0;
        return;
      dump_byte_array(mfrc522.uid.uidByte, 4); // mfrc522.uid.size);
      compareUID();
      mfrc522.PICC_IsNewCardPresent();
      mfrc522.PICC_ReadCardSerial();

      delay(500);
    }
    Вот эти две строки:
    Код (C++):
      mfrc522.PICC_IsNewCardPresent();
      mfrc522.PICC_ReadCardSerial();
    нужны потому, что второй раз подряд эти функции возвращают ерунду.
     
    Крапивин и xsash нравится это.
  14. ostrov

    ostrov Гуру

    Но я все же настоятельно рекомендую RFID.h Тогда можно записать такую функцию:
    Код (C++):
    bool readUID () {         // читаем UID если карта поднесена
      if (!rfid.isCard())
        return false;
      if (!rfid.readCardSerial())
        return false;
      for (int i = 0; i < 5; i++) {
        cardUID[i] = rfid.serNum[i];
      }
      rfid.isCard();
      return true;
    }
    Которую следует вызывать в loop(), она возвращает 0 если карты нет и 1 если есть. Во втором случае в массиве cardUID[] чудесным образом появляется номер поднесенной метки. При этом в loop можно делать что угодно еще, например опрашивать геркон, а то и два!
     
    Tomasina и Крапивин нравится это.
  15. ostrov

    ostrov Гуру

    Вот вам до кучи функция сверки массива считанной метки с массивом эталонной метки:
    Код (C++):
    bool validCard (byte * cardMassiv) { // сверка cardUID с заданным массивом 1 - совпало, 0 - не совпало
      byte valNum = 0;
      for (byte i = 0; i < 5; i++) {
        if (*(cardMassiv + i) == cardUID[i]) valNum++;
      }
      if (valNum == 5) {
        return 1;
      } else {
        return 0;
      }
    }
     
    Tomasina нравится это.
  16. xsash

    xsash Нерд

    Уважаемый, вы не оставляете мне шансов набить своих шишек )))

    Благодарю
     
    Крапивин нравится это.
  17. ostrov

    ostrov Гуру

    Значит ставьте вопрос правильно, мол дайте код но с ошибками чтобы я их нашел и сделал лучше чем было!
     
    Крапивин нравится это.