Замок на RFID и сервоприводе
Содержание
ToggleЗадача
- Разработать модуль управления электронным замком с доступом по RFID карте
- Механизм запирания – сервопривод и щеколда
- Кнопка-концевик на створке для автоматического запирания
- “Внутренняя” кнопка для открытия двери и записи/удаления карт
- Световая и звуковая индикация
- Запись и удаление ID карт в память “на лету” без перепрошивки
- Хранение ID в EEPROM памяти
Базовые уроки
Подключение
- Серво: D2
- Зуммер: D3
- Красный: D4
- Зелёный: D5
- Кнопка открытия/записи: D8
- Концевик двери: D9
- RFID RC522
- 3.3V: 3V3
- RST: D6
- GND: GND
- MISO: D12
- MOSI: D11
- SCK: D13
- SDA: D7
Библиотеки
- Servo – стандартная библиотека для работы с серво
- MFRC522 – RFID ридер
Программа
Код прошивки
/* Электронный замок с бесконтактным доступом по технологии RFID Использован RFID модуль MFRC522 Индикация состояния - зеленый и красный светодиод (можно использовать сдвоенный или RGB) Подача звукового сигнала при помощи баззера Остальные функции реализуются при помощи кнопки на внутренней стороне двери В качестве механизма запирания используется серво, но можно использовать что угодно! Запись нового ключа: поднесите метку при открытой двери и зажатой кнопке до сигнала (2 писка) Удаление записанного ключа: аналогично поднесите метку при зажатой кнопке до сигнала (3 писка) Удаление всех ключей: зажать кнопку на 3 секунды после подачи питания до сигнала При отсутствии записанных ключей дверь блокироваться не будет. */ #include <Servo.h> // Библиотека серво, если используется серво #include <SPI.h> // Библиотека SPI для MFRC522 #include <MFRC522.h> // Библиотека RFID модуля MFRC522 #include <EEPROM.h> // Библиотека EEPROM для хранения ключей #define LOCK_TIMEOUT 1000 // Время до блокировки замка после закрытия двери в мс #define MAX_TAGS 3 // Максимальное количество хранимых меток - ключей #define SERVO_PIN 2 // Пин серво #define BUZZER_PIN 3 // Пин баззера #define RED_LED_PIN 4 // Пин красного светодиода #define GREEN_LED_PIN 5 // Пин зеленого светодиода #define RST_PIN 6 // Пин RST MFRC522 #define CS_PIN 7 // Пин SDA MFRC522 #define BTN_PIN 8 // Пин кнопки #define DOOR_PIN 9 // Пин концевика двери, подтянут к VCC #define EE_START_ADDR 0 // Начальный адрес в EEPROM #define EE_KEY 100 // Ключ EEPROM, для проверки на первое вкл. MFRC522 rfid(CS_PIN, RST_PIN); // Обьект RFID Servo doorServo; // Обьект серво #define DECLINE 0 // Отказ #define SUCCESS 1 // Успешно #define SAVED 2 // Новая метка записана #define DELITED 3 // Метка удалена bool isOpen(void) { // Функция должна возвращать true, если дверь физически открыта return digitalRead(DOOR_PIN); // Если дверь открыта - концевик размокнут, на пине HIGH } void lock(void) { // Функция должна блокировать замок или нечто иное doorServo.attach(SERVO_PIN); doorServo.write(170); // Для примера - запиранеие замка при помощи серво delay(1000); doorServo.detach(); // Детачим серво, чтобы не хрустела Serial.println("lock"); } void unlock(void) { // Функция должна разблокировать замок или нечто иное doorServo.attach(SERVO_PIN); doorServo.write(10); // Для примера - отпирание замка при помощи серво delay(1000); doorServo.detach(); // Детачим серво, чтобы не хрустела Serial.println("unlock"); } bool locked = true; // Флаг состояния замка bool needLock = false; // Служебный флаг uint8_t savedTags = 0; // кол-во записанных меток void setup() { // Инициализируем все Serial.begin(9600); SPI.begin(); rfid.PCD_Init(); // Настраиваем пины pinMode(BTN_PIN, INPUT_PULLUP); pinMode(DOOR_PIN, INPUT_PULLUP); pinMode(BUZZER_PIN, OUTPUT); pinMode(RED_LED_PIN, OUTPUT); pinMode(GREEN_LED_PIN, OUTPUT); // Полная очистка при включении при зажатой кнопке uint32_t start = millis(); // Отслеживание длительного удержания кнопки после включения bool needClear = 0; // Чистим флаг на стирание while (!digitalRead(BTN_PIN)) { // Пока кнопка нажата if (millis() - start >= 3000) { // Встроенный таймаут на 3 секунды needClear = true; // Ставим флаг стирания при достижении таймаута indicate(DELITED); // Подаем сигнал удаления break; // Выходим из цикла } } // Инициализация EEPROM if (needClear or EEPROM.read(EE_START_ADDR) != EE_KEY) { // при первом включении или необходимости очистки ключей for (uint16_t i = 0; i < EEPROM.length(); i++) EEPROM.write(i, 0x00); // Чистим всю EEPROM EEPROM.write(EE_START_ADDR, EE_KEY); // Пишем байт-ключ } else { // Обычное включение savedTags = EEPROM.read(EE_START_ADDR + 1); // Читаем кол-во меток в памяти } // Начальное состояние замка if (savedTags > 0) { // Если метки в памяти есть if (isOpen()) { // И дверь сейчас открыта ledSetup(SUCCESS); // Зеленый лед locked = false; // Замок открыт unlock(); // На всякий случай дернем замок } else { // Метки есть, но дверь закрыта ledSetup(DECLINE); // Красный лед locked = true; // Замок закрыт lock(); // Блокируем замок } } else { // Если меток записано ledSetup(SUCCESS); // Зеленый лед locked = false; // Замок разлочен unlock(); // На всякий случай разблокируем замок } } void loop() { static uint32_t lockTimeout; // Таймер таймаута для блокировки замка // Открытие по нажатию кнопки изнутри if (locked and !digitalRead(BTN_PIN)) { // Если дверь закрыта и нажали кнопку indicate(SUCCESS); // Зеленый лед unlock(); // Разблокируем замок lockTimeout = millis(); // Запомнили время locked = false; // Замок разлочен } // Проверка концевика двери if (isOpen()) { // Если дверь открыта lockTimeout = millis(); // Обновляем таймер } // Блокировка замка по таймауту (ключей > 0, замок разлочен, таймаут вышел) if (savedTags > 0 and !locked and millis() - lockTimeout >= LOCK_TIMEOUT) { ledSetup(DECLINE); // Красный лед lock(); // Блокируем locked = true; // Ставим флаг } // Поднесение метки static uint32_t rfidTimeout; // Таймаут рфид if (rfid.PICC_IsNewCardPresent() and rfid.PICC_ReadCardSerial()) { // Если поднесена карта if (isOpen() and !digitalRead(BTN_PIN) and millis() - rfidTimeout >= 500) { // И дверь открыта + кнопка нажата saveOrDeleteTag(rfid.uid.uidByte, rfid.uid.size); // Сохраняем или удаляем метку } else if (locked) { // Иначе если замок заблокирован if (foundTag(rfid.uid.uidByte, rfid.uid.size) >= 0) { // Ищем метку в базе indicate(SUCCESS); // Если нашли - подаем сигнал успеха unlock(); // Разблокируем lockTimeout = millis(); // Обновляем таймаут locked = false; // Замок разблокирован } else if (millis() - rfidTimeout >= 500) { // Метка не найдена (с таймаутом) indicate(DECLINE); // Выдаем отказ } } rfidTimeout = millis(); // Обвновляем таймаут } // Перезагружаем RFID каждые 0.5 сек (для надежности) static uint32_t rfidRebootTimer = millis(); // Таймер if (millis() - rfidRebootTimer > 500) { // Каждые 500 мс rfidRebootTimer = millis(); // Обновляем таймер digitalWrite(RST_PIN, HIGH); // Дергаем резет delay(1); digitalWrite(RST_PIN, LOW); rfid.PCD_Init(); // Инициализируем модуль } } // Устанавливаем состояние светодиодов void ledSetup(bool state) { if (state) { // Зеленый digitalWrite(GREEN_LED_PIN, HIGH); digitalWrite(RED_LED_PIN, LOW); } else { // Красный digitalWrite(GREEN_LED_PIN, LOW); digitalWrite(RED_LED_PIN, HIGH); } } // Звуковой сигнал + лед void indicate(uint8_t signal) { ledSetup(signal); // Лед switch (signal) { // Выбираем сигнал case DECLINE: Serial.println("DECLINE"); for (uint8_t i = 0; i < 2; i++) { tone(BUZZER_PIN, 100); delay(300); noTone(BUZZER_PIN); delay(100); } return; case SUCCESS: Serial.println("SUCCESS"); tone(BUZZER_PIN, 890); delay(330); noTone(BUZZER_PIN); return; case SAVED: Serial.println("SAVED"); for (uint8_t i = 0; i < 2; i++) { tone(BUZZER_PIN, 890); delay(330); noTone(BUZZER_PIN); delay(100); } return; case DELITED: Serial.println("DELITED"); for (uint8_t i = 0; i < 3; i++) { tone(BUZZER_PIN, 890); delay(330); noTone(BUZZER_PIN); delay(100); } return; } } // Сравнение двух массивов известного размера bool compareUIDs(uint8_t *in1, uint8_t *in2, uint8_t size) { for (uint8_t i = 0; i < size; i++) { // Проходим по всем элементам if (in1[i] != in2[i]) return false; // Если хоть один не сошелся - массивы не совпадают } return true; // Все сошлись - массивы идентичны } // Поиск метки в EEPROM int16_t foundTag(uint8_t *tag, uint8_t size) { uint8_t buf[8]; // Буфер метки uint16_t address; // Адрес for (uint8_t i = 0; i < savedTags; i++) { // проходим по всем меткам address = (i * 8) + EE_START_ADDR + 2; // Считаем адрес текущей метки EEPROM.get(address, buf); // Читаем метку из памяти if (compareUIDs(tag, buf, size)) return address; // Сравниваем - если нашли возвращаем асдрес } return -1; // Если не нашли - вернем минус 1 } // Удаление или запись новой метки void saveOrDeleteTag(uint8_t *tag, uint8_t size) { int16_t tagAddr = foundTag(tag, size); // Ищем метку в базе uint16_t newTagAddr = (savedTags * 8) + EE_START_ADDR + 2; // Адрес крайней метки в EEPROM if (tagAddr >= 0) { // Если метка найдена - стираем for (uint8_t i = 0; i < 8; i++) { // 8 байт EEPROM.write(tagAddr + i, 0x00); // Стираем байт старой метки EEPROM.write(tagAddr + i, EEPROM.read((newTagAddr - 8) + i)); // На ее место пишем байт последней метки EEPROM.write((newTagAddr - 8) + i, 0x00); // Удаляем байт последней метки } EEPROM.write(EE_START_ADDR + 1, --savedTags); // Уменьшаем кол-во меток и пишем в EEPROM indicate(DELITED); // Подаем сигнал } else if (savedTags < MAX_TAGS) { // метка не найдена - нужно записать, и лимит не достигнут for (uint16_t i = 0; i < size; i++) EEPROM.write(i + newTagAddr, tag[i]); // Зная адрес пишем новую метку EEPROM.write(EE_START_ADDR + 1, ++savedTags); // Увеличиваем кол-во меток и пишем indicate(SAVED); // Подаем сигнал } else { // лимит меток при попытке записи новой indicate(DECLINE); // Выдаем отказ ledSetup(SUCCESS); } }
Управление
- При открытой двери: зажать кнопку и поднести карту. Два пика – карта записана, три пика – удалена
- При открытой двери: закрыть дверь (концевик) – замок закроется
- При закрытой двери: нажать кнопку – дверь откроется
- Удерживать кнопку при запуске 3 секунды: очистить список карт
Возможные доработки
- Серво можно заменить на соленоидную щеколду