Стартовый набор для arduino uno r3 rfid инструкция

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

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

Что такое Arduino и для чего оно нужно?

Arduino — это электронный конструктор, который позволяет любому человеку создавать разнообразные электро-механические устройства. Ардуино состоит из программной и аппаратной части. Программная часть включает в себя среду разработки (программа для написания и отладки прошивок), множество готовых и удобных библиотек, упрощенный язык программирования. Аппаратная часть включает в себя большую линейку микроконтроллеров и готовых модулей для них. Благодаря этому, работать с Arduino очень просто!

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

проекты на Arduino

проекты на Arduino

Стартовый набор Arduino

Для того что бы начать изучать Arduino необходимо обзавестись самой платой микроконтроллера и дополнительными деталями. Лучше всего приобрести стартовый набор Ардуино, но можно и самостоятельно подобрать все необходимое. Я советую выбрать набор, потому что это проще и зачастую дешевле. Вот ссылки на лучшие наборы и на отдельные детали, которые обязательно пригодятся вам для изучения:

Базовый набор ардуино для начинающих: Купить
Большой набор для обучения и первых проектов: Купить
Набор дополнительных датчиков и модулей: Купить
Ардуино Уно самая базовая и удобная модель из линейки: Купить
Беспаечная макетная плата для удобного обучения и прототипирования: Купить
Набор проводов с удобными коннекторами: Купить
Комплект светодиодов: Купить
Комплект резисторов: Купить
Кнопки: Купить
Потенциометры: Купить

Среда разработки Arduino IDE

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

Язык программирования Ардуино

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

Для программирования Arduino используется упрощенная версия языка C++ с предопределенными функциями. Как и в других Cи-подобных языках программирования есть ряд правил написания кода. Вот самые базовые из них:

  • После каждой инструкции необходимо ставить знак точки с запятой (;)
  • Перед объявлением функции необходимо указать тип данных, возвращаемый функцией или void если функция не возвращает значение.
  • Так же необходимо указывать тип данных перед объявлением переменной.
  • Комментарии обозначаются: // Строчный и /* блочный */

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

Все прошивки для Arduino должны содержать минимум 2 функции. Это setup() и loop().

Функция setup

Функция setup() выполняется в самом начале и только 1 раз сразу после включения или перезагрузки вашего устройства. Обычно в этой функции декларируют режимы пинов, открывают необходимые протоколы связи, устанавливают соединения с дополнительными модулями и настраивают подключенные библиотеки. Если для вашей прошивки ничего подобного делать не нужно, то функция все равно должна быть объявлена. Вот стандартный пример функции setup():

void setup() {
	Serial.begin(9600);	// Открываем serial соединение
	pinMode(9, INPUT);	// Назначаем 9 пин входом
	pinMode(13, OUTPUT); // Назначаем 13 пин выходом
}

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

Функция loop

Функция loop() выполняется после функции setup(). Loop в переводе с английского значит «петля». Это говорит о том что функция зациклена, то есть будет выполняться снова и снова. Например микроконтроллер ATmega328, который установлен в большинстве плат Arduino, будет выполнять функцию loop около 10 000 раз в секунду (если не используются задержки и сложные вычисления). Благодаря этому у нас есть большие возможности.

Макетная плата Breadbord

Вы можете создавать простые и сложные устройства. Для удобства я советую приобрести макетную плату (Breadbord) и соединительные провода. С их помощью вам не придется паять и перепаивать провода, модули, кнопки и датчики для разных проектов и отладки. С беспаечной макетной платой разработка становится более простой, удобной и быстрой. Как работать с макетной платой я рассказывал в этом уроке. Вот список беспаечных макетных плат:

Макетная плата на 800 точек с 2 шинами питания, платой подачи питания и проводами: Купить
Большая макетная плата на 1600 точек с 4 шинами питания: Купить
Макетная плата на 800 точек с 2 шинами питания: Купить
Макетная плата на 400 точек с 2 шинами питания: Купить
Макетная плата на 170 точек: Купить
Соединительные провода 120 штук: Купить

Первый проект на Arduino

Давайте соберем первое устройство на базе Ардуино. Мы просто подключим тактовую кнопку и светодиод к ардуинке. Схема проекта выглядит так:

Управление яркостью светодиода

Управление яркостью светодиода

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

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

 // переменные с пинами подключенных устройств
int switchPin = 8;
int ledPin = 11;

// переменные для хранения состояния кнопки и светодиода
boolean lastButton = LOW;
boolean currentButton = LOW;
boolean ledOn = false;

void setup() {
	pinMode(switchPin, INPUT);
	pinMode(ledPin, OUTPUT);
}

// функция для подавления дребезга
boolean debounse(boolean last) {
	boolean current = digitalRead(switchPin);
	if(last != current) {
		delay(5);
		current = digitalRead(switchPin);
	}
	return current;
}

void loop() {
	currentButton = debounse(lastButton);
	if(lastButton == LOW && currentButton == HIGH) {
		ledOn = !ledOn;
	}
	lastButton = currentButton;
	digitalWrite(ledPin, ledOn);
}

В этом скетче я создал дополнительную функцию debounse для подавления дребезга контактов. О дребезге контактов есть целый урок на моем сайте. Обязательно ознакомьтесь с этим материалом.

ШИМ Arduino

Широтно-импульсная модуляция (ШИМ) — это процесс управления напряжением за счет скважности сигнала. То есть используя ШИМ мы можем плавно управлять нагрузкой. Например можно плавно изменять яркость светодиода, но это изменение яркости получается не за счет уменьшения напряжения, а за счет увеличения интервалов низкого сигнала. Принцип действия ШИМ показан на этой схеме:

ШИМ ардуино

ШИМ ардуино

Когда мы подаем ШИМ на светодиод, то он начинает быстро зажигаться и гаснуть. Человеческий глаз не способен увидеть это, так как частота слишком высока. Но при съемке на видео вы скорее всего увидите моменты когда светодиод не горит. Это случится при условии что частота кадров камеры не будет кратна частоте ШИМ.

В Arduino есть встроенный широтно-импульсный модулятор. Использовать ШИМ можно только на тех пинах, которые поддерживаются микроконтроллером. Например Arduino Uno и Nano имеют по 6 ШИМ выводов: это пины D3, D5, D6, D9, D10 и D11. В других платах пины могут отличаться. Вы можете найти описание интересующей вас платы в этом разделе.

Для использования ШИМ в Arduino есть функция analogWrite(). Она принимает в качестве аргументов номер пина и значение ШИМ от 0 до 255. 0 — это 0% заполнения высоким сигналом, а 255 это 100%. Давайте для примера напишем простой скетч. Сделаем так, что бы светодиод плавно загорался, ждал одну секунду и так же плавно угасал и так до бесконечности. Вот пример использования этой функции:

 // Светодиод подключен к 11 пину
int ledPin = 11;

void setup() {
	pinMode(ledPin, OUTPUT);
}

void loop() {
	for (int i = 0; i < 255; i++) {
		analogWrite(ledPin, i);
		delay(5);
	}

	delay(1000);

	for (int i = 255; i > 0; i--) {
		analogWrite(ledPin, i);
		delay(5);
	}
}

Аналоговые входы Arduino

Как мы уже знаем, цифровые пины могут быть как входом так и выходом и принимать/отдавать только 2 значения: HIGH и LOW. Аналоговые пины могут только принимать сигнал. И в отличии от цифровых входов аналоговые измеряют напряжение поступающего сигнала. В большинстве плат ардуино стоит 10 битный аналогово-цифровой преобразователь. Это значит что 0 считывается как 0 а 5 В считываются как значение 1023. То есть аналоговые входы измеряют, подаваемое на них напряжение, с точностью до 0,005 вольт. Благодаря этому мы можем подключать разнообразные датчики и резисторы (терморезисторы, фоторезисторы) и считывать аналоговый сигнал с них.

Для этих целей в Ардуино есть функция analogRead(). Для примера подключим фоторезистор к ардуино и напишем простейший скетч, в котором мы будем считывать показания и отправлять их в монитор порта. Вот так выглядит наше устройство:

Подключение фоторезистора к Ардуино

Подключение фоторезистора к Ардуино

В схеме присутствует стягивающий резистор на 10 КОм. Он нужен для того что бы избежать наводок и помех. Теперь посмотрим на скетч:

int sensePin = 0; // Пин к которому подключен фоторезистор
 
void setup() {
 analogReferense(DEFAULT); // Задаем опорное значение напряжения. Эта строка не обязательна.
 Serial.begin(9600); // Открываем порт на скорости 9600 бод.
}
 
void loop() {
 Serial.println(analogRead(sensePin)); // Считываем значение и выводим в порт
 delay(500); // задержка для того что бы значений было не слишком много
}

Вот так из двух простейших элементов и четырех строк кода мы сделали датчик освещенности. На базе этого устройства мы можем сделать умный светильник или ночник. Очень простое и полезное устройство.

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

СОДЕРЖАНИЕ ►

  • RFID RC522 Arduino распиновка, характеристики
  • Подключение модуля RFID RC522 к плате Ардуино
    • Программа для получения UID (идентификатора метки)
  • Подключение RFID RC522 для идентификации доступа
    • Программа для создания системы доступа по метке

RFID Arduino (Radio Frequency IDentification) — это бесконтактная идентификация транспондера (метки) при помощи радиочастотного канала связи. Система широко используется во многих отраслях: для контроля доступа, отслеживания товаров в цепочках поставок, создания системы начисления бонусов и т.д. Рассмотрим подключение устройства для считывания меток RFID-RC522 к плате Ардуино.

Модуль RFID-RC522 характеристики, распиновка

RFID система для плат Ардуино состоит из двух компонентов: транспондера (метки) в виде пластиковой карточки или брелка и кардридера (устройства для считывания меток). Идентификация производится по уникальному идентификатору, который имеет каждая rfid метка. Модуль RC522 излучает электромагнитные волны, а метки отправляют в ответ свой идентификационный номер, данные из памяти и т.д.

Модуль RFID-RC522 Arduino распиновка

Модуль RFID-RC522 Arduino распиновка, характеристики

Характеристики RFID RC522

  • Рабочее напряжение: 3.3 Вольт
  • Потребляемый ток: 13-26 мкА
  • Рабочая частота: 13.56 MHz
  • Дальность считывания: 0 — 60 мм
  • Интерфейс: SPI
  • Размер: 40 мм х 60 мм

RFID-метки бывают активные и пассивные (без встроенного источника энергии), работают они на разной частоте: LF (125 — 134 кГц), HF (13.56 МГц), UHF (860 — 960 МГц). Модули, которые читают и записывают информацию на метки называются ридерами (считывателями). Модуль RFID MFRC522 Arduino выполнен на основе микросхемы MFRC522, которая обеспечивает работу с метками HF (13,56 МГц).

Подключение RFID к Ардуино с помощью RC522

Для этого занятия потребуется:

  • Arduino Uno / Arduino Nano / Arduino Mega;
  • модуль RFID RC522;
  • светодиоды и резисторы;
  • макетная плата;
  • провода «папа-мама»;
  • библиотека MFRC522.h и SPI.h

Подключение RFID к Ардуино с помощью RC522

Подключение метки RFID к Ардуино с помощью RC522
RC522 Arduino Uno Arduino Nano Arduino Mega
GND GND GND GND
VCC 3,3V 3,3V 3,3V
MOSI 11 11 51
MISO 12 12 50
SCK 13 13 52
SDA 10 10 10
RST 9 9 9

Модуль подключается к Ардуино по интерфейсу SPI, схема подключения представлена на рисунке выше. Напомним, что шина SPI на платах Arduino Uno и Mega размещена на разных портах. Для работы с модулем RC522 используется популярная библиотека MFRC522.h. После подключения кардридера к микроконтроллеру, загрузите следующий код для получения типа и UID метки (уникального идентификатора ключа).

Скетч для получения UID (уникального идентификатора)

#include "SPI.h"
#include "MFRC522.h"

#define RST_PIN  9 // RES pin
#define SS_PIN  10 // SDA (SS) pin

MFRC522 mfrc522(SS_PIN, RST_PIN); // создание объекта mfrc522

void setup() {
   Serial.begin(9600);
   SPI.begin();
   mfrc522.PCD_Init();
   delay(4);
   mfrc522.PCD_DumpVersionToSerial();
   Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks..."));
}

void loop() {
   // сброс цикла, если на считывателе нет карты
   if ( ! mfrc522.PICC_IsNewCardPresent()) {
      return;
   }

   if ( ! mfrc522.PICC_ReadCardSerial()) {
      return;
   }

   // вывод информации о карте на монитор порта
   mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
}

Пояснения к коду:

  1. на мониторе порта будет отображаться идентификатор UID метки, когда вы поднесете карту или брелок с меткой к картридеру;
  2. для работы скетча необходимо также установить библиотеку SPI.h

Считывание RFID меток с помощью модуля RC522

Считывание RFID меток с помощью RC522

Считывание RFID меток с помощью модуля RC522

Рассмотрим пример, с помощью которого можно будет создать полноценную систему доступа. Программа будет сканировать с метки уникальный идентификатор, если UID карточки соответствует установленному в скетче значению (cardID), то доступ будет предоставлен. Если к ридеру будет поднесена неизвестная метка, то доступ будет запрещен, и при этом будет включаться красный или зеленый светодиод.

Скетч для определения RFID меток с модулем RC522 Arduino

#include "SPI.h"
#include "MFRC522.h"

#define RST_PIN 9 // RES pin
#define SS_PIN  10 // SDA (SS) pin

byte readCard[4];
String cardID = "20C3935E"; // замените на ID своей метки
String tagID = "";

MFRC522 mfrc522(SS_PIN, RST_PIN); // создание объекта mfrc522

void setup() {
  Serial.begin(9600);
  SPI.begin();
  mfrc522.PCD_Init();
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
}

void loop() {
  while (getID()) {
    if (tagID == cardID) {
      Serial.println("Access Granted!");
      digitalWrite(5, LOW);
      digitalWrite(6, HIGH);
    }
    else {
      Serial.println("Access Denied!");
    }
    Serial.print("ID: ");
    Serial.println(tagID);
    delay(2000);
    digitalWrite(6, LOW);
  }
}

boolean getID() {
  if (! mfrc522.PICC_IsNewCardPresent()) {
    return false;
  }

  if (! mfrc522.PICC_ReadCardSerial()) {
    return false;
  }

  tagID = "";
  
  for (uint8_t i = 0; i < 4; i++) {
    tagID.concat(String(mfrc522.uid.uidByte[i], HEX));
  }

  tagID.toUpperCase();
  mfrc522.PICC_HaltA();
  return true;
}

Пояснения к коду:

  1. вместо зеленого светодиода, к Ардуино можно подключить твердотельное реле и управлять с его помощью, например, дверным звонком;
  2. значение cardID в скетче следует поставить свое, которое получено было от метки в первой программе.

Заключение. Модуль RC522 Arduino предназначен для создания электромагнитного поля, которое используется для связи с RFID метками. Ридер может общаться с микроконтроллером Ардуино с максимальной скоростью передачи данных до 10 Мбит в секунду через последовательный периферийный интерфейс SPI или протокол связи I2C (порты SDA и SCL) по которому можно подключить одновременно до 128 устройств.

Arduino — самая популярная платформа любительской и образовательной робототехники.  Arduino (Ардуино) – это серия плат ввода-вывода. Платы имеют аналоговые и цифровые порты, к которым можно подключать различные устройства (DIY-компоненты): светодиоды, датчики, кнопки, моторы, сервоприводы и т.д. В продаже есть отдельные платы Arduino нескольких видов, их аналоги, различные дополнительные компоненты, готовые наборы, содержащие плату и компоненты, и готовые роботы на основе Arduino.
Наиболее эффективно начинать изучение Arduino с наборов Starter для Ардуино. Эти комплекты содержат самую популярную Arduino-плату –  Arduino UNO и другие электронные компоненты, необходимые для начального изучения Arduino: светодиоды, резисторы, сервопривод, мотор, кнопку, жидкокристаллический экран, пьезоэлемент, датчики и прочее.
Практически любой из таких наборов содержит необходимый минимум для начинающих и подходит для освоения платформы Arduino.
Стартовый набор для Arduino. Starter №3 содержит следующие компоненты:

1 шт UNO R3

1 шт USB кабель

1 шт ЖК дисплей 1602 с IIC

1 шт Модуль RC522

1 шт Ключь карта

1 шт Ключь брелок

1 шт Модуль Джойстик

1 шт Клавиатура

1 шт Часы реального времени

1 шт Датчик уровня воды

1 шт Датчик влажности

1 шт RGB модуль

1 шт Драйвер мотора

1 шт Мотор

1 шт Реле на 1 канал

1 шт Макетная плата MB-102

65 шт перемычек

1 шт Шлейф F-M 10 проводов

1 шт Датчик звука

1 шт Пульт

1 шт 10 кОм реостат

1 шт Светодиодный дисплей 1 разряд

1 шт Светодиодный дисплей 4 разряда

1 шт Матричный дисплей

1 шт Сервопривод 9G

1 шт Звуко-излучатель

2 шт датчика удара

3 шт свето-резистра

5 шт переключателей с кнопками

1 шт 9В коннектор «Крона»

15шт светодиодов

30 шт резисторов

1 шт Датчик воспламенения

1 шт Датчик инфра-красного излучения

1 шт Регистр сдвига 74HC595

1 шт Датчик температуры LM35DZ 

Автор: Nich1con

Технология RFID (Радиочастотная идентификация) позволяет при помощи радиосигнала быстро и безопасно передавать данные между специальными “считывателями” и “метками” – карточками, брелоками, браслетами и т.д. на небольшом расстоянии.
Одно из широко известных развитий технологии – NFC, при помощи которого можно оплачивать покупки или подключать устройства бесконтактно. Нам же доступны менее сложные, но не менее полезные и интересные применения, о которых будет сказано ниже.

Применение


Комплект RFID модуль + метки может быть использован:

  • Как часть самодельных охранных систем
  • При создании простых электронных замков (метка является ключом)
  • В системах контроля доступа (однократный, многократный пропуск)
  • В качестве электронного “кошелька” внутри собственного предприятия
  • В роли интерактивного предмета в квестах и т.д.

Железо


RFID работает в нескольких частотных диапазонах, в свою очередь RFID модули и метки можно поделить на низкочастотные “LF” (125 кгц) и высокочастотные “HF” (13,56 MHz), существуют так же и ультравысокочастотные “UHF”, но они нас не интересуют.

Наиболее распространенные RFID Arduino-модули основаны на микросхеме MFRC522, работающей с HF метками 13,56 МГц. Поиск модулей и библиотек производится по этому же имени.

Существует два типа модулей MFRC522, с которыми вы скорее всего столкнетесь:

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

Библиотеки


Для работы с модулем MFRC522 понадобится библиотека https://github.com/miguelbalboa/rfid. Библиотека тяжелая, индусская и имеет немало проблем, но достойных альтернатив просто нет – несколько других “облегченных” библиотек значительно уступают в функциональности и удобстве использования.

Подключение


Модуль MFRC522 подключается по аппаратному интерфейсу SPI, выбранная библиотека предоставляет следующую таблицу подключения к Arduino:

Сигнал Модуль MFRC522  UNO/NANO Leonardo Pro micro Mega
Reset RST D9 RST/ISCP-5 RST D5
Chip select SDA (SS) D10 D10 D10 D53
MOSI MOSI D11 ISCP-4 D16 D51
MISO MISO D12 ISCP-1 D14 D50
SCK SCK D13 ISCP-3 D15 D52
  • Контакты модуля RST и SDA (SS) указываются в скетче – можно использовать любые.
  • У Leonardo подключение производится к 6-ти контактному ICSP разъему программатора.

Пример подключения модуля к Arduino Nano:

О RFID метках


Прежде всего самая сложная для понимания, но и самая важная часть – работа с RFID метками. В комплекте с модулем идут пара меток MIFARE Classic 1K, как понятно из названия – на 1 килобайт (на самом деле меньше, но об этом позже).

Чтобы изучить организацию памяти такой метки, можно воспользоваться примером из библиотеки, открыв Примеры > MFRC522 > DumpInfo. Однако для вашего удобства я подготовил вот такую карту:

Обратите внимание – память организована в виде 16-ти секторов, по 4 блока каждый. Итого – 64 блока по 16 Байт, как раз набегает 1 Килобайт. Деление по секторам носит скорее условный характер, так как адресация в памяти будет производиться по блокам.

Все сектора кроме нулевого имеют одинаковое строение – 3 блока данных + 1 блок безопасности, так называемый sector trailer. Каждый из этих блоков может быть прочитан и перезаписан (при соблюдении условий), исключение составляет нулевой блок (сектор 0).

Нулевой блок хранит в себе уникальный ID “UID”, тип метки и прочую информацию, записанную заводом-изготовителем. Нулевой сектор не может быть перезаписан, если речь идет о “классических” метках, к которым относятся комплектные с модулем. Таким образом UID позволяет отличить две с виду идентичные метки. UID как правило состоит из 4х байт, свободно считываемых из метки. Важно: китайский рынок может предложить вам “перезаписываемые” метки, UID в которых можно менять, путем перезаписи нулевого блока. Если в вашей системе используется только UID – учтите возможность очень простого копирования UID в метки-болванки (в том числе злоумышленниками).

Блоком безопасности является каждый 4й блок, каждый блок безопасности отвечает за свой сектор (предыдущие 3 блока данных) – он хранит 2 ключа доступа по 6 байт (ключи A и B), а также специальные “Access bits” (Биты доступа), грубо говоря настройки доступа. Ключи A и B могут быть использованы для аутентификации и последующего доступа к блокам данных в пределах сектора. То есть да, для того чтобы получить доступ к любому из блоков внутри сектора необходимо “разблокировать” этот сектор, при помощи одного из ключей.

Поэтому будьте уверены, если производитель позаботился о смене секретных ключей в своих RFID метках – скопировать или как-нибудь изменить содержимое штатными средствами вы уже не сможете, а ведь так хотелось? Идем дальше.

Биты доступа позволяют настроить условия доступа и возможности работы каждого блока в отдельности (каждого блока данных + блока безопасности). Наилучшим инструментом в работе с метками MIFARE Classic 1K является вот этот онлайн-калькулятор http://calc.gmss.ru/Mifare1k/. Если хотите разобраться чуть глубже – обязательно полистайте и опробуйте.

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

blank

Однако есть возможность настроить блоки на некоторые интересные сценарии, например защитить от записи (конфигурация 1-0-1 или 0-1-0). Или же сделать так, что прочитать блок можно при помощи как ключа A, так и ключа B, а вот для записи обязательно понадобится ключ B (конфигурация 1-0-0), в таком случае можно ограничить права некоторого оборудования и сделать систему безопаснее. И да, конфигурация 1-1-1 превращает блок в кирпич (обратимо).

В примерах ниже мы будем использовать конфигурацию блоков данных по умолчанию (0-0-0) и следующие принципы:

  • Создаем ключ B, значение которого знаем только мы, длина ключа – 6 Байт.
  • Ключ A будет полностью аналогичен ключу B, однако он не будет использоваться.
  • Биты доступа для блоков безопасности будем использовать в конфигурации 0-1-1

blank

Таким образом для всех операций с меткой применяется только ключ B, который невозможно считать из метки (впрочем, как и ключ A), даже если сектор предварительно разблокирован. Если хотите намертво зашить ключи A и B в блок безопасности – подойдет конфигурация 1-0-1, поменять будет уже невозможно. Ну а последняя 1-1-1 конфигурация блока безопасности заблокирует еще и настройки доступа к блокам данных!

Некоторые варианты необратимы, но не переживайте – у вас есть целых 16 попыток! По одной на каждый сектор.

В итоге 3 байта настроек доступа приняли следующие значения: 0x7F 0x07 0x88, байт USER может быть любой.

blank

Важно: изначально ключи A и B от всех секторов метки содержат значение 0xFFFFFFFFFFFF, так что если хотите защитить данные в ваших метках, не забывайте сменить оба ключа! Кстати, нулевой блок и соответственно UID свободно читаются из метки, даже если нулевой сектор был заблокирован секретными ключами.

Ну и последнее, что касается меток – реальный объем пользовательской памяти. Если не создавать костыли, а использовать только блоки для хранения данных – (по 3 блока в 15-ти секторах, и 2 блока в нулевом секторе) получаем 47 доступных блоков по 16 байт или 752 байта, что тоже неплохо.

Начало работы

Подключили модуль, распаковали свежие метки – начинаем работать!

Прежде всего создаем необходимые объекты, инициализируем интерфейс SPI и MFRC522, не забываем про ключ доступа. Изначально все ключи состоят из FF-ок, так что “наполняем” ключ.

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

#define RST_PIN         9        // Пин rfid модуля RST
#define SS_PIN          10       // Пин rfid модуля SS

MFRC522 rfid(SS_PIN, RST_PIN);   // Объект rfid модуля
MFRC522::MIFARE_Key key;         // Объект ключа
MFRC522::StatusCode status;      // Объект статуса

void setup() {
  Serial.begin(9600);            // Инициализация Serial
  SPI.begin();                   // Инициализация SPI
  rfid.PCD_Init();               // Инициализация модуля

  for (byte i = 0; i < 6; i++) { // Наполняем ключ
    key.keyByte[i] = 0xFF;       // Ключ по умолчанию 0xFFFFFFFFFFFF
  }
}

void loop() {}

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

if (!rfid.PICC_IsNewCardPresent()) return;  // Если новая метка не поднесена - вернуться в начало loop
if (!rfid.PICC_ReadCardSerial()) return;    // Если метка не читается - вернуться в начало loop

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

В итоге наш скетч имеет следующую структуру:

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

#define RST_PIN         9        // Пин rfid модуля RST
#define SS_PIN          10       // Пин rfid модуля SS

MFRC522 rfid(SS_PIN, RST_PIN);   // Объект rfid модуля
MFRC522::MIFARE_Key key;         // Объект ключа
MFRC522::StatusCode status;      // Объект статуса

void setup() {
  Serial.begin(9600);            // Инициализация Serial
  SPI.begin();                   // Инициализация SPI
  rfid.PCD_Init();               // Инициализация модуля

  for (byte i = 0; i < 6; i++) { // Наполняем ключ
    key.keyByte[i] = 0xFF;       // Ключ по умолчанию 0xFFFFFFFFFFFF
  }
}

void loop() {
  // Занимаемся чем угодно
  if (!rfid.PICC_IsNewCardPresent()) return;  // Если новая метка не поднесена - вернуться в начало loop
  if (!rfid.PICC_ReadCardSerial()) return;    // Если метка не читается - вернуться в начало loop
  // Работаем с RFID
}

Теперь давайте идти по порядку, осваивая основные методы для работы с RFID.

Обратите внимание: примеры ниже содержат специальные блоки кода, повышающие стабильность работы, о них подробно сказано в конце статьи.

Чтение UID


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

Так или иначе пример чтения UID и вывод в порт приведен ниже:

Чтение UID

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

#define RST_PIN         9        // Пин rfid модуля RST
#define SS_PIN          10       // Пин rfid модуля SS

MFRC522 rfid(SS_PIN, RST_PIN);   // Объект rfid модуля
MFRC522::MIFARE_Key key;         // Объект ключа
MFRC522::StatusCode status;      // Объект статуса

void setup() {
  Serial.begin(9600);              // Инициализация Serial
  SPI.begin();                     // Инициализация SPI
  rfid.PCD_Init();                 // Инициализация модуля
  rfid.PCD_SetAntennaGain(rfid.RxGain_max);  // Установка усиления антенны
  rfid.PCD_AntennaOff();           // Перезагружаем антенну
  rfid.PCD_AntennaOn();            // Включаем антенну

  for (byte i = 0; i < 6; i++) {   // Наполняем ключ
    key.keyByte[i] = 0xFF;         // Ключ по умолчанию 0xFFFFFFFFFFFF
  }
}

void loop() {
  // Занимаемся чем угодно
  
  static uint32_t rebootTimer = millis(); // Важный костыль против зависания модуля!
  if (millis() - rebootTimer >= 1000) {   // Таймер с периодом 1000 мс
    rebootTimer = millis();               // Обновляем таймер
    digitalWrite(RST_PIN, HIGH);          // Сбрасываем модуль
    delayMicroseconds(2);                 // Ждем 2 мкс
    digitalWrite(RST_PIN, LOW);           // Отпускаем сброс
    rfid.PCD_Init();                      // Инициализируем заного
  }

  if (!rfid.PICC_IsNewCardPresent()) return;  // Если новая метка не поднесена - вернуться в начало loop
  if (!rfid.PICC_ReadCardSerial()) return;    // Если метка не читается - вернуться в начало loop

  Serial.print("UID: ");
  for (uint8_t i = 0; i < 4; i++) {           // Цикл на 4 итерации
    Serial.print("0x");                       // В формате HEX
    Serial.print(rfid.uid.uidByte[i], HEX);   // Выводим UID по байтам
    Serial.print(", ");
  }
  Serial.println("");
}

После успешного чтения метки, ее UID появляется в массиве rfid.uid.uidByte[] который можно прочитать и использовать.

Чтение блока


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

В примерах ниже будем работать с блоком под номером 6 (сектор 1), за первый сектор и соответственно блоки 4, 5 и 6 отвечает блок безопасности под номером 7. То есть запомнили – блок данных 6, блок безопасности 7.

Аутентификацию сектора производит метод PCD_Authenticate(), по умолчанию все манипуляции с меткой возможны при помощи ключа A, а сам процесс аутентификации выглядит следующим образом:

status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 7, &key, &(rfid.uid));
if (status != MFRC522::STATUS_OK) {   // Если не окэй
  Serial.println("Auth error");       // Выводим ошибку
  return;
}

Обратите внимание на …KEY_A в первом аргументе и цифру 7 вторым аргументом – это и есть номер блока безопасности. Отслеживать статус не обязательно, но крайне желательно.

После успешной аутентификации можно свободно манипулировать разблокированным сектором, в нашем случае будем читать содержимое методом MIFARE_Read(). Читать нужно в байтовый массив, размером 18 (!) байт, чтение происходит из блока 6 (первый аргумент) и выглядит так:

uint8_t dataBlock[18];                          // Буфер для чтения
uint8_t size = sizeof(dataBlock);               // Размер буфера
status = rfid.MIFARE_Read(6, dataBlock, &size); // Читаем 6 блок в буфер
if (status != MFRC522::STATUS_OK) {             // Если не окэй
  Serial.println("Read error");                 // Выводим ошибку
  return;
}

Важно: Не смотря на то, что блоки имеют размер 16 байт, буферный массив создается на 18 байт, а количество байт на чтение передается именно в виде указателя на переменную. В противном случае чтение закончится ошибкой, примите это как факт.

В случае успешного чтения, выведем содержимое блока 6 в монитор порта, все как обычно:

Serial.print("Data:");                          // Выводим 16 байт в формате HEX
for (uint8_t i = 0; i < 16; i++) {
  Serial.print("0x");
  Serial.print(dataBlock[i], HEX);
  Serial.print(", ");
}
Serial.println("");

А завершать работу с меткой нужно при помощи еще двух замысловатых методов:

rfid.PICC_HaltA();                              // Завершаем работу с меткой
rfid.PCD_StopCrypto1();

В итоге полный скетч для чтения блока 6 из новой метки со стандартными ключами выглядит так:

Чтение блока

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

#define RST_PIN         9        // Пин rfid модуля RST
#define SS_PIN          10       // Пин rfid модуля SS

MFRC522 rfid(SS_PIN, RST_PIN);   // Объект rfid модуля
MFRC522::MIFARE_Key key;         // Объект ключа
MFRC522::StatusCode status;      // Объект статуса

void setup() {
  Serial.begin(9600);              // Инициализация Serial
  SPI.begin();                     // Инициализация SPI
  rfid.PCD_Init();                 // Инициализация модуля
  rfid.PCD_SetAntennaGain(rfid.RxGain_max);  // Установка усиления антенны
  rfid.PCD_AntennaOff();           // Перезагружаем антенну
  rfid.PCD_AntennaOn();            // Включаем антенну
  for (byte i = 0; i < 6; i++) {   // Наполняем ключ
    key.keyByte[i] = 0xFF;         // Ключ по умолчанию 0xFFFFFFFFFFFF
  }
}

void loop() {
  // Занимаемся чем угодно

  static uint32_t rebootTimer = millis(); // Важный костыль против зависания модуля!
  if (millis() - rebootTimer >= 1000) {   // Таймер с периодом 1000 мс
    rebootTimer = millis();               // Обновляем таймер
    digitalWrite(RST_PIN, HIGH);          // Сбрасываем модуль
    delayMicroseconds(2);                 // Ждем 2 мкс
    digitalWrite(RST_PIN, LOW);           // Отпускаем сброс
    rfid.PCD_Init();                      // Инициализируем заного
  }

  if (!rfid.PICC_IsNewCardPresent()) return;  // Если новая метка не поднесена - вернуться в начало loop
  if (!rfid.PICC_ReadCardSerial()) return;    // Если метка не читается - вернуться в начало loop

  /* Аутентификация сектора, указываем блок безопасности #7 и ключ A */
  status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 7, &key, &(rfid.uid));
  if (status != MFRC522::STATUS_OK) {     // Если не окэй
    Serial.println("Auth error");         // Выводим ошибку
    return;
  }

  /* Чтение блока, указываем блок данных #6 */
  uint8_t dataBlock[18];                          // Буфер для чтения
  uint8_t size = sizeof(dataBlock);               // Размер буфера
  status = rfid.MIFARE_Read(6, dataBlock, &size); // Читаем 6 блок в буфер
  if (status != MFRC522::STATUS_OK) {             // Если не окэй
    Serial.println("Read error");                 // Выводим ошибку
    return;
  }

  /* Выводим содержимое блока в Serial */
  Serial.print("Data:");                          // Выводим 16 байт в формате HEX
  for (uint8_t i = 0; i < 16; i++) {
    Serial.print("0x");
    Serial.print(dataBlock[i], HEX);
    Serial.print(", ");
  }
  Serial.println("");

  rfid.PICC_HaltA();                              // Завершаем работу с меткой
  rfid.PCD_StopCrypto1();
}

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

Запись блока


При записи все 16 байт записываются в блок единовременно, для чего должен быть заранее подготовлен байтовый массив на 16 Байт, например такой:

uint8_t dataToWrite[16] = {
  0x00, 0x00, 0x00, 0x00,
  0xAA, 0xBB, 0xCC, 0xDD,
  0xAA, 0xBB, 0xCC, 0xDD,
  0x00, 0x00, 0x00, 0x00
};

Как в случае и с чтением, сектор в котором находится нужный блок нужно предварительно аутентифицировать, об этом уже сказано выше, так что переходим непосредственно к записи. Запись производится методом MIFARE_Write(), в отличии от MIFARE_Read() тут все несколько проще – указываем записываемый блок, массив и число 16 (количество байт на запись):

status = rfid.MIFARE_Write(6, dataToWrite, 16); // Пишем массив в блок 6
if (status != MFRC522::STATUS_OK) {             // Если не окэй
  Serial.println("Write error");                // Выводим ошибку
  return;
}

Итоговый скетч записи массива в блок 6 выглядит так:

Запись блока

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

#define RST_PIN         9        // Пин rfid модуля RST
#define SS_PIN          10       // Пин rfid модуля SS

MFRC522 rfid(SS_PIN, RST_PIN);   // Объект rfid модуля
MFRC522::MIFARE_Key key;         // Объект ключа
MFRC522::StatusCode status;      // Объект статуса

void setup() {
  Serial.begin(9600);              // Инициализация Serial
  SPI.begin();                     // Инициализация SPI
  rfid.PCD_Init();                 // Инициализация модуля
  rfid.PCD_SetAntennaGain(rfid.RxGain_max);  // Установка усиления антенны
  rfid.PCD_AntennaOff();           // Перезагружаем антенну
  rfid.PCD_AntennaOn();            // Включаем антенну
  for (byte i = 0; i < 6; i++) {   // Наполняем ключ
    key.keyByte[i] = 0xFF;         // Ключ по умолчанию 0xFFFFFFFFFFFF
  }
}

void loop() {
  // Занимаемся чем угодно

  static uint32_t rebootTimer = millis(); // Важный костыль против зависания модуля!
  if (millis() - rebootTimer >= 1000) {   // Таймер с периодом 1000 мс
    rebootTimer = millis();               // Обновляем таймер
    digitalWrite(RST_PIN, HIGH);          // Сбрасываем модуль
    delayMicroseconds(2);                 // Ждем 2 мкс
    digitalWrite(RST_PIN, LOW);           // Отпускаем сброс
    rfid.PCD_Init();                      // Инициализируем заного
  }

  if (!rfid.PICC_IsNewCardPresent()) return;  // Если новая метка не поднесена - вернуться в начало loop
  if (!rfid.PICC_ReadCardSerial()) return;    // Если метка не читается - вернуться в начало loop

  /* Аутентификация сектора, указываем блок безопасности #7 и ключ A */
  status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 7, &key, &(rfid.uid));
  if (status != MFRC522::STATUS_OK) {     // Если не окэй
    Serial.println("Auth error");         // Выводим ошибку
    return;
  }

  /* Запись блока, указываем блок данных #6 */
  uint8_t dataToWrite[16] = {                    // Массив на запись в блок
    0x00, 0x00, 0x00, 0x00,
    0xAA, 0xBB, 0xCC, 0xDD,
    0xAA, 0xBB, 0xCC, 0xDD,
    0x00, 0x00, 0x00, 0x00
  };

  status = rfid.MIFARE_Write(6, dataToWrite, 16); // Пишем массив в блок 6
  if (status != MFRC522::STATUS_OK) {             // Если не окэй
    Serial.println("Write error");                // Выводим ошибку
    return;
  }

  Serial.println("Write OK");                     // Завершаем работу с меткой
  rfid.PICC_HaltA();                              
  rfid.PCD_StopCrypto1();
}

После поднесения метки в мониторе порта должно отобразиться сообщение “Write OK“.

К слову ничто не мешает нам объеденить запись и чтение в одном скетче:

Запись + чтение блока

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

#define RST_PIN         9        // Пин rfid модуля RST
#define SS_PIN          10       // Пин rfid модуля SS

MFRC522 rfid(SS_PIN, RST_PIN);   // Объект rfid модуля
MFRC522::MIFARE_Key key;         // Объект ключа
MFRC522::StatusCode status;      // Объект статуса

void setup() {
  Serial.begin(9600);              // Инициализация Serial
  SPI.begin();                     // Инициализация SPI
  rfid.PCD_Init();                 // Инициализация модуля
  rfid.PCD_SetAntennaGain(rfid.RxGain_max);  // Установка усиления антенны
  rfid.PCD_AntennaOff();           // Перезагружаем антенну
  rfid.PCD_AntennaOn();            // Включаем антенну
  for (byte i = 0; i < 6; i++) {   // Наполняем ключ
    key.keyByte[i] = 0xFF;         // Ключ по умолчанию 0xFFFFFFFFFFFF
  }
}

void loop() {
  // Занимаемся чем угодно

  static uint32_t rebootTimer = millis(); // Важный костыль против зависания модуля!
  if (millis() - rebootTimer >= 1000) {   // Таймер с периодом 1000 мс
    rebootTimer = millis();               // Обновляем таймер
    digitalWrite(RST_PIN, HIGH);          // Сбрасываем модуль
    delayMicroseconds(2);                 // Ждем 2 мкс
    digitalWrite(RST_PIN, LOW);           // Отпускаем сброс
    rfid.PCD_Init();                      // Инициализируем заного
  }

  if (!rfid.PICC_IsNewCardPresent()) return;  // Если новая метка не поднесена - вернуться в начало loop
  if (!rfid.PICC_ReadCardSerial()) return;    // Если метка не читается - вернуться в начало loop

  /* Аутентификация сектора, указываем блок безопасности #7 и ключ A */
  status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 7, &key, &(rfid.uid));
  if (status != MFRC522::STATUS_OK) {     // Если не окэй
    Serial.println("Auth error");         // Выводим ошибку
    return;
  }

  /* Запись блока, указываем блок данных #6 */
  uint8_t dataToWrite[16] = {                    // Массив на запись в блок
    0x00, 0x00, 0x00, 0x00,
    0xAA, 0xBB, 0xCC, 0xDD,
    0xAA, 0xBB, 0xCC, 0xDD,
    0x00, 0x00, 0x00, 0x00
  };

  status = rfid.MIFARE_Write(6, dataToWrite, 16); // Пишем массив в блок 6
  if (status != MFRC522::STATUS_OK) {             // Если не окэй
    Serial.println("Write error");                // Выводим ошибку
    return;
  }

  /* Чтение блока, указываем блок данных #6 */
  uint8_t dataBlock[18];                          // Буфер для чтения
  uint8_t size = sizeof(dataBlock);               // Размер буфера
  status = rfid.MIFARE_Read(6, dataBlock, &size); // Читаем 6 блок в буфер
  if (status != MFRC522::STATUS_OK) {             // Если не окэй
    Serial.println("Read error");                 // Выводим ошибку
    return;
  }

  Serial.print("Data:");                          // Выводим 16 байт в формате HEX
  for (uint8_t i = 0; i < 16; i++) {
    Serial.print("0x");
    Serial.print(dataBlock[i], HEX);
    Serial.print(", ");
  }
  Serial.println("");

  rfid.PICC_HaltA();                              // Завершаем работу с меткой
  rfid.PCD_StopCrypto1();
}

Смена ключей безопасности и настройка блоков


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

Для того, чтобы изменить ключи доступа к сектору, необходимо перезаписать блок безопасности, ответственный за данный сектор. В наших примерах фигурировал блок данных под номером и соответственно блок безопасности под номером 7. Задача по смене ключей безопасности сводится к следующим шагам:

  1. Создать байтовый массив на 16 ячеек, включающий:
    • Ключ A
    • Байты настроек доступа
    • Ключ B
  2. Провести аутентификацию выбранного сектора, пока что используя стандартный ключ 0xFFFFFFFFFFFF
  3. Произвести запись подготовленного массива в блок безопасности, в точности так же, как в случае с обычным блоком.

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

  1.  Для создания образа блока безопасности (содержимого массива) придется вернуться вверх по тексту и вспомнить, что при выбранных нами настройках сектора, байты доступа получили значения 0x7F 0x07 0x88. Осталось придумать ключи доступа, имеющие длину 6 байт. Я буду использовать ключ 0xABABABABABAB, данный ключ не является безопасным и подходит исключительно для демонстрации! Так же напоминаю, что ключи A и B будут идентичны, однако использоваться будет только ключ B.

blank

  1. 1  После того, как мы определились с настройками сектора (“Access Bits“) и ключами безопасности – создаем нужный байтовый массив:
    uint8_t secBlockDump[16] = {                   // Дамп блока безопасности
      0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,          // < Ключ A
      0x7F, 0x07,  0x88,                           // < Access Bits
      0xFF,                                        // < User байт (любой)
      0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB           // < Ключ B
    };

    Думаю по структуре массива все понятно и вполне наглядно.

  2. Производим аутентификацию сектора, как и раньше. Напомнинаю, что для этого используется блок безопасности 7:
    status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 7, &key, &(rfid.uid));
    if (status != MFRC522::STATUS_OK) {     // Если не окэй
      Serial.println("Auth error");         // Выводим ошибку
      return;
    }
  3.  После чего записываем дамп, в тот же блок безопасности – под номером 7:
    status = rfid.MIFARE_Write(7, secBlockDump, 16); // Пишем массив в блок 6
    if (status != MFRC522::STATUS_OK) {              // Если не окэй
      Serial.println("Write error");                 // Выводим ошибку
      return;
    }

Итоговый скетч записи новых ключей и настроек доступа:

Запись блока безопасности

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

#define RST_PIN         9        // Пин rfid модуля RST
#define SS_PIN          10       // Пин rfid модуля SS

MFRC522 rfid(SS_PIN, RST_PIN);   // Объект rfid модуля
MFRC522::MIFARE_Key key;         // Объект ключа
MFRC522::StatusCode status;      // Объект статуса

void setup() {
  Serial.begin(9600);              // Инициализация Serial
  SPI.begin();                     // Инициализация SPI
  rfid.PCD_Init();                 // Инициализация модуля
  rfid.PCD_SetAntennaGain(rfid.RxGain_max);  // Установка усиления антенны
  rfid.PCD_AntennaOff();           // Перезагружаем антенну
  rfid.PCD_AntennaOn();            // Включаем антенну
  for (byte i = 0; i < 6; i++) {   // Наполняем ключ
    key.keyByte[i] = 0xFF;         // Ключ по умолчанию 0xFFFFFFFFFFFF
  }
}

void loop() {
  // Занимаемся чем угодно

  static uint32_t rebootTimer = millis(); // Важный костыль против зависания модуля!
  if (millis() - rebootTimer >= 1000) {   // Таймер с периодом 1000 мс
    rebootTimer = millis();               // Обновляем таймер
    digitalWrite(RST_PIN, HIGH);          // Сбрасываем модуль
    delayMicroseconds(2);                 // Ждем 2 мкс
    digitalWrite(RST_PIN, LOW);           // Отпускаем сброс
    rfid.PCD_Init();                      // Инициализируем заного
  }

  if (!rfid.PICC_IsNewCardPresent()) return;  // Если новая метка не поднесена - вернуться в начало loop
  if (!rfid.PICC_ReadCardSerial()) return;    // Если метка не читается - вернуться в начало loop

  /* Аутентификация сектора, указываем блок безопасности #7 и ключ A */
  status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 7, &key, &(rfid.uid));
  if (status != MFRC522::STATUS_OK) {     // Если не окэй
    Serial.println("Auth error");         // Выводим ошибку
    return;
  }

  /* Запись блока, указываем блок безопасности #7 */
  uint8_t secBlockDump[16] = {                   // Дамп блока безопасности
    0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,          // < Ключ A
    0x7F, 0x07,  0x88,                           // < Access Bits
    0xFF,                                        // < User байт (любой)
    0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB           // < Ключ B
  };

  status = rfid.MIFARE_Write(7, secBlockDump, 16); // Пишем массив в блок 7
  if (status != MFRC522::STATUS_OK) {              // Если не окэй
    Serial.println("Write error");                 // Выводим ошибку
    return;
  }

  Serial.println("Write OK");                     // Завершаем работу с меткой
  rfid.PICC_HaltA();                              
  rfid.PCD_StopCrypto1();
}

Важно: при повторном поднесении метки мы неизбежно получим ошибку аутентификации, это связано с тем, что стандартный ключ больше не подходит к перенастроенному сектору!

Не забываем сменить “наполнение” ключа в void setup(){}:

for (byte i = 0; i < 6; i++) {   // Наполняем ключ
  key.keyByte[i] = 0xAB;         // Пишем свой ключ
}

Так же напоминаю, что отныне для сектора 1 (блоков 4…7) мы используем только ключ B, соответственно и команда аутентификации отныне выглядит чуть иначе:

status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, 7, &key, &(rfid.uid));
if (status != MFRC522::STATUS_OK) {     // Если не окэй
  Serial.println("Auth error");         // Выводим ошибку
  return;
}

Работа с защищенным сектором


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

Инкремент при поднесении

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

#define RST_PIN         9        // Пин rfid модуля RST
#define SS_PIN          10       // Пин rfid модуля SS

MFRC522 rfid(SS_PIN, RST_PIN);   // Объект rfid модуля
MFRC522::MIFARE_Key key;         // Объект ключа
MFRC522::StatusCode status;      // Объект статуса

void setup() {
  Serial.begin(9600);              // Инициализация Serial
  SPI.begin();                     // Инициализация SPI
  rfid.PCD_Init();                 // Инициализация модуля
  rfid.PCD_SetAntennaGain(rfid.RxGain_max);  // Установка усиления антенны
  rfid.PCD_AntennaOff();           // Перезагружаем антенну
  rfid.PCD_AntennaOn();            // Включаем антенну

  for (byte i = 0; i < 6; i++) {   // Наполняем ключ B
    key.keyByte[i] = 0xAB;         // Пишем свой ключ
  }
}

void loop() {
  // Занимаемся чем угодно

  static uint32_t rebootTimer = millis(); // Важный костыль против зависания модуля!
  if (millis() - rebootTimer >= 1000) {   // Таймер с периодом 1000 мс
    rebootTimer = millis();               // Обновляем таймер
    digitalWrite(RST_PIN, HIGH);          // Сбрасываем модуль
    delayMicroseconds(2);                 // Ждем 2 мкс
    digitalWrite(RST_PIN, LOW);           // Отпускаем сброс
    rfid.PCD_Init();                      // Инициализируем заного
  }

  if (!rfid.PICC_IsNewCardPresent()) return;  // Если новая метка не поднесена - вернуться в начало loop
  if (!rfid.PICC_ReadCardSerial()) return;    // Если метка не читается - вернуться в начало loop

  /* Аутентификация сектора, указываем блок безопасности #7 и ключ B */
  status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, 7, &key, &(rfid.uid));
  if (status != MFRC522::STATUS_OK) {     // Если не окэй
    Serial.println("Auth error");         // Выводим ошибку
    return;
  }

  /* Чтение блока, указываем блок данных #6 */
  uint8_t dataBlock[18];                           // Буфер
  uint8_t size = sizeof(dataBlock);                // Размер буфера

  status = rfid.MIFARE_Read(6, dataBlock, &size);  // Читаем блок 6
  if (status != MFRC522::STATUS_OK) {              // Если не окэй
    Serial.println("Read error");                  // Выводим ошибку
    return;
  }

  /* Выводим количество поднесений метки */
  Serial.print("Count: ");                         // Выводим количество
  Serial.println(dataBlock[0]);                    // Хранится в нулевом байте массива
  dataBlock[0]++;                                  // Инкремент

  /* Запись блока, указываем блок данных #6 */
  status = rfid.MIFARE_Write(6, dataBlock, 16);    // Пишем массив в блок 6
  if (status != MFRC522::STATUS_OK) {              // Если не окэй
    Serial.println("Write error");                 // Выводим ошибку
    return;
  }

  rfid.PICC_HaltA();                               // Завершаем работу с меткой
  rfid.PCD_StopCrypto1();
}

Количество поднесений ограничено 255, потому что используется лишь 1 Байт, но для примера это и не важно.

Инкремент и декремент


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

Для реализации данного функционала библиотека имеет методы MIFARE_Increment() и MIFARE_Decrement(), однако независимо от установленных байтов доступа данный функционал не показал работоспособность на десятке меток и нескольких модулях. Возможно всему виной поддельные чипы MFRC522, установленные в модули. В любом случае перечисленные методы возвращают ошибку при поднесении метки, а потому и пример для работы не привожу.

Особенности и костыли


Несмотря на то, что данные модули выпускаются много лет, в течение которых дорабатывалась и библиотека, пара модуль + библиотека имеют критическую проблему зависания (!). Через случайный промежуток времени система просто перестает считывать поднесенные метки – помогает только перезагрузка микроконтроллера. Этот критический баг может сыграть злую шутку, при использовании данного комплекта в электронных замках. Что является причиной? На данный момент достоверного ответа нет… однако есть кое-какие решения!

  • Периодическая перезагрузка и повторная инициализация. В главный цикл программы добавляется таймер на millis() с периодом 500…3000 мс, внутри которого производится принудительный сброс и инициализация модуля. Данный код располагается в начале главного цикла программы. Данный костыль является наиболее эффективным в борьбе с зависанием модуля.
    static uint32_t rebootTimer = millis(); // Важный костыль против зависания модуля!
    if (millis() - rebootTimer >= 1000) {   // Таймер с периодом 1000 мс
      rebootTimer = millis();               // Обновляем таймер
      digitalWrite(RST_PIN, HIGH);          // Сбрасываем модуль
      delayMicroseconds(2);                 // Ждем 2 мкс
      digitalWrite(RST_PIN, LOW);           // Отпускаем сброс
      rfid.PCD_Init();                      // Инициализируем заного
    }
  • Принудительная установка усиления и перезагрузка антенны. Полезность сомнительная, однако по некоторым заявлениям библиотека не всегда корректно настраивает антенну после инициализации модуля. Решается добавлением соответствующих строк помимо инициализации модуля в функции void setup(){}:
    rfid.PCD_SetAntennaGain(rfid.RxGain_max);  // Установка усиления антенны
    rfid.PCD_AntennaOff();                     // Перезагружаем антенну
    rfid.PCD_AntennaOn();                      // Включаем антенну
  • Повторное чтение или второй шанс. В примерах выше мы использовали следующую конструкцию для отслеживания поднесенной метки:
if (!rfid.PICC_IsNewCardPresent()) return;  // Если новая метка не поднесена - вернуться в начало loop
if (!rfid.PICC_ReadCardSerial()) return;    // Если метка не читается - вернуться в начало loop

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

if (!rfid.PICC_IsNewCardPresent()) return;  // Если новая метка не поднесена - вернуться в начало loop
if (!rfid.PICC_ReadCardSerial()) {          // Если метка не читается - пробуем еще раз
  if (!rfid.PICC_ReadCardSerial()) return;  // Все равно не читается - вернуться в начало loop
}

Наиболее эффективным и важным является первый костыль, располагайте его перед работой с модулем, желательно в начале цикла void loop(){}, но ни в коем случае не в прерывании таймера. Однако несмотря на то, что приведенные выше хитрости действительно работают и многократно повышают стабильность работы RFID модуля – в случае с электронными замками и прочими системами контроля доступа рекомендуется иметь резервные способы входа, помимо RFID.

Понравилась статья? Поделить с друзьями:
  • Метеоспазмил инструкция по применению цена в ростове на дону
  • Электрический духовой шкаф darina 1v5 bde111 709 b инструкция
  • Диронет для кошек таблетки инструкция отзывы
  • Лордес сироп инструкция по применению для детей
  • Как зарегистрировать самозанятость через госуслуги в 2022 году пошаговая инструкция