Доброго всем здоровья ! Заморочился на тему установки домой электромеханического замка, который будет открываться ключём от подъезда. Собрал девайс, написал код. Идея была такая: По нажатию кнопки можно записать ID ключа в EEPROM ардуины. "Длина" ключа 14-байт. Пакет данных всегда начинается с "2" и заканчивается "3" это первый и последний байт ключа. Ну а после того, как ключи записаны , собственно можно по ним открывать замок. Ну и классика жанра: В принципе всё работает, но работает не совсем так, как нужно. Одна из основных проблем в том, что когда ключ считывается, он считывается в буфер, размер которого как я понял - 64 байта. Считывается он туда многократно, всё зависит от времени нахождения ключа в зоне катушки. Так вот отсюда начинаются проблемы: реле срабатывает ровно столько раз, сколько раз ключ был считан в буфер Хотя должно сработать один раз и всё. Как бы очистить этот буфер после получения одного пакета с данными ? Код (C++): #include <SoftwareSerial.h> #include <EEPROM.h> //#include <Bounce2.h> //#define BUTTON_PIN 5 #define LED_PIN 13 SoftwareSerial RfidReader(2, 3); int num = 0; int count = 0; int prekey[14]; int key[12]; int testrom[122]; int index = 0; int i, a, b, h; int keys =0; bool keyOk = false; bool CheckOk = false; bool wrmem = false; int switchPin = 5; int Relay = 6; boolean lastButton = HIGH; boolean currentButton = HIGH; void setup(){ Serial.begin(9600); RfidReader.begin(9600); // Setup the button with an internal pull-up : pinMode(switchPin,INPUT_PULLUP); pinMode(LED_PIN,OUTPUT); pinMode(Relay, OUTPUT); } boolean debounce(boolean last) { boolean current = digitalRead(switchPin); if (last != current) { delay(5); current = digitalRead(switchPin); } return current; } void ReadID() { count = 0; for (count =0; count <=13; count++) { if (RfidReader.available() > 0) //Если присутствуют данные со считывателя карт { Serial.print("ReadID Function"); num = RfidReader.read(); // Присваеваем переменной num первый считанный байт prekey[count] = num ; // Заполняем массив от 0 до 13 значениями полученных байт в num Serial.print("count="); Serial.println(count); Serial.print("num="); Serial.println(num, DEC); if(count >= 13) //Если считали 14 байт, .... { if (prekey[0] == 2 && prekey[13] == 3) // Если первый элемент массива равен 2 а последний 3 { keyOk = true; Serial.print ("keyOk="); Serial.print (keyOk); Serial.println (' '); Serial.print ("key="); for (a=0; a<=11; a++) { b = a+1; key[a] = prekey[b]; Serial.print (key[a]); } return; } else { Serial.print ("Key is not very" ); keyOk = false; count = 0; num =0; } delay (1000); Serial.println (' '); Serial.print("prekey="); for(i=0; i<=13; i++) { int x= prekey[i]; Serial.print(x); } Serial.println(' '); count = 0; delay (1000); } } else { count = 0; num = 0; keyOk = false; break; } } } void CheckMem () // функция проверки наличия ключа в памяти { Serial.println("CheckMem function"); keys = testrom[0]; // num - ячейка количества записанных ключей int n = keys*12+2; // n - номер первой чистой ячейки int k; int g; int j; int CheckCount =0; for (k=0; k<=(n-1); k=k+12) // цикл пересчитывает блоки ключей, каждый блок 12 байт { Serial.print("k="); Serial.println(k); for (j=0; j<=11; j++) // цикл пересчитывает, сравнивает каждый байт массива ключа и массива памяти { g = (j+k+2); // переменная указывает на нужный байт в памяти соответствующий или не соответствующий байту ключа Serial.print("key_j="); Serial.println(key[j]); Serial.print("testrom_g="); Serial.println(testrom[g]); if (key[j] == testrom[g]) // сравнение ключа с сохранённым в памяти побайтово { CheckCount ++; // прибавляет единицу если байты соответствуют друг другу Serial.print("CheckCount="); Serial.println(CheckCount); } else { CheckCount =0; // если нет совпадения в байтах, сбросить счётчик в 0 } } if (CheckCount == 12) //Если все 12 байт подряд равны, { CheckOk = true; //Меняем значение переменной в true Serial.println(' '); Serial.print("Key found in memory"); break; //Прекращаем цикл, так как такой ключ есть в памяти устройства } else { Serial.println(' '); Serial.println("key not found"); CheckOk = false; // Стафим флаг , о том что такого ключа не найдено } } } void WriteMem () // Функция сохранения в энергонезависимой памяти устройства { Serial.println("WriteMem Function"); keys = testrom[0]; // В нулевой ячейке памяти сохраняется количество записанных ключей . Присваеваем переменной keys это значение int n = (keys*12+2); // Переменная n соответствует значению первой свободной ячейки памяти для записи нового ключа Serial.print("n="); Serial.println(n); for (int j=0; j<=11; j++) // Цикл записи байт данных в память { Serial.print("j="); Serial.println(j); int g = (n+j); Serial.print("g="); Serial.println(g); testrom[g] = key[j]; } for( int m=2; m<=13; m++) { Serial.print("Testrom="); Serial.println(testrom[m]); Serial.println(' '); } testrom[0]= testrom[0] + 1; // Увеличиваем ячейку памяти 0 на единицу wrmem = true; } void loop() { currentButton = debounce(lastButton); if (lastButton == HIGH && currentButton == LOW) { digitalWrite(LED_PIN, HIGH ); delay(100); while(wrmem == 0 || CheckOk ==0) { ReadID(); if ( keyOk == 1) { CheckMem(); if ( CheckOk == 0) { WriteMem(); } else { Serial.println("I have this key"); break; } } else {} } digitalWrite(LED_PIN, LOW ); } lastButton = currentButton; ReadID(); if ( keyOk == 1) { Serial.println (' '); Serial.print("CardNumberIS:"); for (a=0; a<=11; a++) { Serial.print (key[a]); } Serial.println (' '); CheckMem(); if ( CheckOk == 1) { Serial.println(' '); Serial.print("Open Door"); digitalWrite(Relay, HIGH ); delay(2500); digitalWrite(Relay, LOW ); } else { } } int prekey[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}; int key[] = {0,0,0,0,0,0,0,0,0,0,0,0}; num = 0; count = 0; index = 0; i, a, b, h =0; keyOk = false; CheckOk = false; wrmem = false; } Вместо EEPROM временно используется массив testrom[122] Может у кого есть идеи по оптимизации ?
Код (C++): // переменная для хранения времени последней проверки unsigned long lastCheking; //Директива задержки между срабатывания. (3 сек) #define DELAY 3000 //Открытие замка if (lastCheking < millis() - DELAY) { //Открыть дверь }
немного переписал код с начала. новый мне нравиться больше и работает стабильно, вроде. Дальше добавлю сюда датчик приближения для включения подъездного освещения, а так же световую и звуковую индикацию по событию ( запись ключа, чтение ключа, ключ - вражеский) Код (C++): #include <SoftwareSerial.h> //подключаем библиотеку програмного последовательного порта #include <EEPROM.h> // подключаем библиотеку EEPROM // #define LED_PIN 13 #define DELAY 3000 //время задержки повторного срабатывания реле SoftwareSerial RfidReader(2, 3); // подключаем RDM6300 к пинам 2,3 bool KeyOk = false; // флаг правильного прочтения ключа int key[12]; // массив содержаший ID считанного ключа int prekey[64]; // массив данных полученный после чтения ключа // int EEPROM[64]; bool CheckOk =false; // флаг проверки ключа с имеющимся в памяти bool wrmem = false; // флаг осуществления записи в EEPROM int switchPin = 5; // кнопка на 5 пине int Relay = 6; // реле на 6 пине boolean lastButton = HIGH; // антидребезг... boolean currentButton = HIGH; // антидребезг... unsigned long lastCheck ; // задержка повторного срабатывания void setup () { Serial.begin(9600); // стартум последовательный порт RfidReader.begin(9600); // стартуем программный последовательный порт pinMode(switchPin,INPUT_PULLUP); // режим ввода с внутренней подтяжкой HIGH pinMode(LED_PIN,OUTPUT); // режим пина на вывод для светодиода pinMode(Relay, OUTPUT); // режим пина на вывод для реле } void ReadId() // Ф-ция чтения ключа { if (RfidReader.available() > 0) // Если есть данные на порту { for (int count =0; count<=63; count++) // Считываем 64 байта из буфера { int num = RfidReader.read(); // Записываем байт в переменную prekey[count] = num; // записываем байт в массив if (count >= 63) // если записали 64 байта { if (prekey[0] == 2 && prekey[13] ==3) // если первый байт равен 2, а 14 байт равен 3 (начало и конец данных получаемых с ключа) { KeyOk = true; // ключ считан верно Serial.print ("key="); for (int i=0; i<=11; i++) // выделяем ID ключа из полученного массива { int pk = i+1; key[i] = prekey[pk]; Serial.print (key[i]); } count = 0; return; } else { Serial.println (' '); Serial.print ("Bad key" ); KeyOk = false; count = 0; break; } } } } } void CheckMem () // Ф-ция проверки наличия ключа в энергонезависимой памяти { int keys = EEPROM[0]; // Количество записанных в память ключей храниться в первой ячейке памяти, вторая ячейка оставлена "про запас" int n = (keys*12+2); // первый чистый байт в энергонезависимой памяти для записи ID ключа int CheckCount =0; CheckOk = false; // ставим флаг сравнения в 0 for (int k=2; k<=(n-1); k=(k+12)) // Цикл устанавливается на начало первого блока с ключами и "переключает" по 12 байт в зависимости от количества ключей (n) { CheckCount =0; for (int j=0; j<=11; j++) // Сравнение считанного и имеющегося в памяти ключа { int f=(k+j); Serial.println(' '); Serial.print("Key="); Serial.println(key[j]); Serial.print("EEPROM="); Serial.println(EEPROM[f]); if (key[j] == EEPROM[f]) { CheckCount++; } } if (CheckCount ==12) // Если совпали все 12 байт, значит ключ найден { Serial.println("CheckOk"); CheckOk =true; // Ставим флаг сравнения в 1 break; } } } void WriteMem () // Функция записи в энергонезависимую память { int keys = EEPROM[0]; int n = (keys*12+2); for (int j=0; j<=11; j++) { int g = (n+j); EEPROM[g] = key[j]; } EEPROM[0]= EEPROM[0] + 1; Serial.println("WriteOK"); wrmem = true; } boolean debounce(boolean last) //Функция антидребезга { boolean current = digitalRead(switchPin); if (last != current) { delay(5); current = digitalRead(switchPin); } return current; } void loop () { currentButton = debounce(lastButton); if (lastButton == HIGH && currentButton == LOW) { digitalWrite(LED_PIN, HIGH ); delay(100); while(wrmem == 0) { ReadId(); if ( KeyOk == 1) { CheckMem(); if ( CheckOk == 0) { WriteMem(); Serial.print("wrmem="); Serial.println(wrmem); } else { Serial.println("I have this key"); break; } } else {} } wrmem = false; digitalWrite(LED_PIN, LOW ); } lastButton = currentButton; ReadId(); if ( KeyOk == 1) { Serial.println (' '); Serial.print("CardNumberIS:"); for (int a=0; a<=11; a++) { Serial.print (key[a]); } Serial.println (' '); CheckMem(); if ( CheckOk == 1) { if (lastCheck < (millis() - DELAY)) { Serial.println(' '); Serial.println("Open Door"); digitalWrite(Relay, HIGH ); delay (2000); digitalWrite(Relay,LOW); lastCheck = millis(); } } else { digitalWrite(Relay, LOW ); } } int key[] = {0,0,0,0,0,0,0,0,0,0,0,0}; KeyOk = false; CheckOk = false; wrmem = false; }
Считали раз, отключили реакцию на считывание с обнулением буфера serial до того момента когда оно снова потребуется. Например, после закрытия двери. И что то код у вас огромный для такой простой задачи, не читал, но осуждаю.
Конкретнее ? Как отключить реакцию ? я вот решил это таким способом. может это костыль. но он рабочий. есть советы , жду
Каких еще библиотек? Класс Serial? Предлагаете человеку напрямую через регистры USART писать программу?
Самое простое - выставляете флаг, например, checkRFID. Если он true, то порт опрашивается, если false то нет. И соответственно: Код (C++): if (checkRFID) num = RfidReader.read(); Кстати, я бы просто опрашивал порт как стандартный Serial, этот RFID хорош тем, что просто выдает 14 байт в UART на скорости 9600, что позволяет упростить работу с ридером донельзя. Точнее, до нескольких строк.
Ок! И что будет управлять этим флагом ? Что будет отвечать за то что он true или false ? Ну по сути , именно сам опрос, занимает несколько строк в функции ReadId. Остальное - выделение 12 байт ID ключа Опыта маловато, конечно, но я учусь
12 байт выделяет цикл из 5 строк.. Побайтно сравнивается полученные данные с заданными, если все совпадает, то метка та самая. Проще всего увеличивать счетчик на 1 когда байт совпадает, если по окончании цикла счетчик == 12, то метка та самая. Что управляет флагом приема я не знаю, это зависит от логики программы. Когда нужно принимать данные - принимаем. Если нет, не принимаем.
Флагом приёма управлять должен поднесённый ключ. В принципе сейчас у меня всё работает. Дальше будем посмотреть.
А не подскажите какие библиотеки нужно использовать, чтобы написать аналогичный код , только на Си ??? Просто я понял, что среда Ардуино - это так, поморгать светодиодом ))) В любом случае, более или менее сложный проект на ардуино, делается уже со знанием языка Си, а если так, то зачем мне использовать ардуино, когда можно запрограммировать сам контроллер
Ардуино программируют на надмножестве C, если что. Чтобы не тратить время и деньги на изготовление своей обвязки, которая будет на 90% совпадать с ардуино.
Про Мини на "нашем всем" стоит 100 с небольшим руб. Чаще всего нет смысла городить свой огород с обвязкой.