Чтение и запись RFID меток. Модуль RC522 для Arduino

06.07.2019

Схема эмулятора RFID транспондера стандарта EM-Marine (EM4100).
Бесконтактные карты стандарта Em-Marine являются на сегодняшний день наиболее популярным средством идентификации в нашей стране и используются для идентификации пользователей в системах контроля и управления доступом (СКУД).
Второй, не менее популярной, областью применения карт Em-Marine является их использование в системах логического доступа при авторизации пользователей по ID номеру карты в операционной системе компьютера и рабочих приложениях и тп.

Карты и брелки Em-Marine.
Соответственно подобные системы идентификации очень распространены и могут представлять интерес для реализации собственных систем идентификации и автоматизации. Поскольку протокол обмена и аппаратная часть подобных низкочастотных систем является более простой для самостоятельной реализации собственных устройств большинство радиолюбительских конструкций тематики RFID посвящена низкочастотным системам.

Рабочая частота карт Em-Marine составляет 125 КГц. Для их чтения используются специализированные считыватели бесконтактных карт (считыватели RFID). Взаимодействие идентификатора с таким считывателем осуществляется дистанционно.
Вариантов внешнего исполнения данных идентификаторов существует огромное количество: пропуска Em-Marine изготавливаются в виде тонких и толстых карт, браслетов для аквапарков, различных брелоков, радио-меток для интеграции в RFID-изделия.
Для стандарта транспондеров EM4100 карта содержит 64 бита данных, при этом карты, как правило, не перезаписываемые. Для удобства регистрации карт код, записанный в карте, продублирован печатью на одной из сторон карты. Кодировка передаваемых транспондером данных - манчестер кодирование. При этом периоды сигнала передаваемого транспондером являются кратными частоте 125Кгц - частота сигнала считывателя транспондеров. Сами транспондеры реализованы без внешнего питания (пассивный тэг), питание осуществляется за счет контура LC (катушка и конденсатор) при попадании тэга в зону действия поля считывателя карт. Тактирование транспондера также осуществляется сигналом считывателя - 125Кгц. Поэтому параметры результирующего сигнала в манчестер кодировке являются кратными сигналу 125Кгц.

Схема взаимодействия транспондера и считывателя RFID.
Для более полного понимания рассмотрим структуру пакета RFID транспондера формата EMMarine EM4100. Приведено описание (на английском, взято из анноутов) формата пакета транспондера.
“…….EM4100 compatible RFID transponders carry 64 bits of Read Only memory. This means that information can be read from the Tag but no data can be changed, or new data written to the card once the card has been programmed with the initial data. The format of the data is as shown here.
1 1 1 1 1 1 1 1 1 9 bit header bits, all 1"s
8 bit version number D00 D01 D02 D03 P0
or customer ID.
D04 D05 D06 D07 P1
D08 D09 D10 D11 P2 Each group of 4 bits
D12 D13 D14 D15 P3 is followed by an Even 32 Data Bits
D16 D17 D18 D19 P4 parity bit
D20 D21 D22 D23 P5
D24 D25 D26 D27 P6
D28 D29 D30 D31 P7
D32 D33 D34 D35 P8
D36 D37 D38 D39 P9
4 column Parity bits PC0 PC1 PC2 PC3 S0 1 stop bit (0)
The first 9 bits are logic 1“.
Соответственно мы имеем 9 стартовых бит пакета (всегда логическая 1), 11 групп по 4 бит данных с 1 битом четности по строке, 4 бита четности по столбцам в конце пакета, завершающий бит (всегда 0).
Для примера возьмем транспондер с данными номера 06001259E3.
1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 1 0 1 0 1 0 0 1 0 1 1 1 0 1 0 0 1 1 0 0 1 0 00
0 6 0 0 1 2 5 9 E 3

Байт с данными 0x06 считается номером версии. На картах EM-Marine, которые попадались мне, выбиты десятичные значения соответствующие последним 3 байтам пакета. В любом случае нам нужно будет для реализации воспроизводить все 64 бита пакета согласно данному описанию.
Теперь обратимся внимательно к описанию модуляции данных транспондера. Данные взяты из анноута AN680. На рисунке сделал отметки красным относительно интересующих нас диаграмм.

Теперь подробнее опишем нужные нам диаграммы. Сигнал CLK тактирования - это и есть сигнал считывателя RFID, о чем уже было сказано ранее. Данные в кодировке NRZ следует подготовить транспондеру в соответствии с записанными данными пакета (64 бит). Видно, что реализация кодирования NRZ по пакету транспондера элементарная и требует минимальных затрат ресурсов. Собственно разбираем пакет на битовый поток и меняем логическое значения сигнала по 0 и 1 в данных и все. Для получения результирующего сигнала делаем программно XOR текущего состояния сигнала в формате NRZ и CLK тактирующего сигнала считывателя. В итоге получаем манчестер кодирование результирующего сигнала. Подробнее про манчестер кодирование описывать не буду - данные можно найти в отдельных анноутах. Для более подробного описания методов модуляции можно ознакомиться с данными из “Modulation Methods H.R. Walker Data Systems 05/01/04(reviewed 4/18/10)”, мною изучались именно данные примеры. Главное, что с минимальными затратами ресурсов мы можем таким образом реализовать транспондер формата EM-Marine. Для примера можно взять контроллер AVR серии tiny45 (можно и на tiny13 сделать). На контроллере tiny45 тестировалось поскольку именно такой оказался в наличии для экспериментов.
Теперь представим функциональную схему транспондера на основе модели в Proteus для контроллера tiny45.

Функциональная схема транспондера в Proteus.

Вот так выглядит сигнал генерируемый транспондером. Красным отмечено начало пакета.
По схеме видно, что ножка контроллера T0 (PORTB.2) используется для подачи тактового сигнала для 8 битного таймера TIMER0. В программе реализовано прерывание по совпадению на таймере TIMER0 (TIM0_COMPA). Тактирование установлено от внешнего сигнала для данного таймера. Для нас тактовым сигналом является 125Кгц от считывателя карт. На схеме удалено все, что касается питания контроллера и цепей тактирования от считывателя. В реальной схеме сам контроллер тактируется от кварца 4 Мгц установленного между 2 и 3 ножкой контроллера. Также можно добавить блокировочные конденсаторы для кварца по 22 pF на данных ножках контроллера.
Настройки симуляции Proteus для контроллера указанны следующим образом:

При программировании контроллера tiny45 фьюзы (биты конфигурации) устанавливаем таким же образом, как указано на рисунке.2. Для тактирования контроллера используется кварц 4 Мгц.
Относительно реализации внешней схемы обвязки контроллера рассмотрим данный вопрос подробнее. Для примеров были взяты материалы RFID Handbook (E2E_chapter03-rfid-handbook) где описываются фундаментальные принципы построения RFID систем. Сам документ прилагается к статье. Рассмотрим пример схемы пассивного транспондера (часть схемы на странице 46). Для понимания я сделал пометки на схеме красным цветом.
Видно, что мы имеем приемный контур на L1C1, который служит для питания схемы транспондера и тактирования. Все что касается счетчика-делителя IC1(4024), логических элементов IC3 (7400) можем смело выкидывать - нам это не потребуется. Делитель для таймера реализован настройками таймера без внешних делителей - счетчиков, логическая часть также реализована программно. Однако данный пример позволяет более полно понять работу пассивной схемы транспондера. Максимальное расстояние считывания для транспондера данного формата составляет 200см. В реальности большинство схем работает на расстояниях 2-10см. Параметры контура емкости и индуктивности LC подбираются максимально точно на резонансную частоту 125Кгц. Для примера использовался контур с емкостью 1nF и катушкой 60 витков на оправке диаметром 50мм проволокой ПЭВ 0.2. Рассчитать нужный контур можно в специальной программе (можно рассчитать контур для прямоугольно катушки, печатной и тп.). Главное подобрать точные номиналы под частоту 125 Кгц иначе расстояние считывания и чувствительность схемы значительно ухудшатся. При плохо настроенных контурах будет работать только при поднесении катушки транспондера вплотную к считывателю. Устройство работает по принципу Full Duplex (FDX) - генерация данных транспондера непрерывно при наличии питания схемы. Тактирование схемы от считывателя и передача данных осуществляется непрерывно. Некоторые схемы транспондеров используют схему работы HDX (Half Duplex) - считыватель излучает в импульсном режиме, транспондер передает данные в промежутках данных импульсов зарядки от считывателя. Это относится, например к TIRIS транспондерам от Texas Instruments.

Схема пассивного транспондера на основе схемы из RFID Handbook.


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



Как вам эта статья?

Метка EM4100 хранит 64 бита данных, значит, конструкция должна содержать 64-битный сдвиговый регистр, составленный из восьми 8-битных регистров 74HC165. Регистр перезагружается после каждых 64 сдвигов, чтобы сбросить данные и начать сначала. Данные на входах регистра следующие:
  • Паттерн синхронизации: девять единиц
  • Идентификатор производителя/версии: 2 блока по 5 бит, из которых 4 бита - данные, а пятый - четность
  • Уникальный идентификатор: 8 блоков по 5 бит, из которых 4 бита - данные, а пятый - четность
  • Контрольная сумма: 4 бита четности, подсчитанные по столбцам
  • Стоп-бит: «0»

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

В качестве стандартной отмазки напомню, что автор (И переводчик! - Прим. перев. ) не несет никакой ответствености за последствия использования информации из данной статьи. Читатель должен сам отвечать за все свои действия.

Корпус

Иногда очень везет. Красивый корпус не помешал бы именно сейчас, когда прототип закончен, а печатная плата заказана. И именно в это время Флеминг закончил собирать и запустил станок лазерной резки OSAA PhotonSaw . После года работы над проектом лазер готов вырезать свои первые детали. Флемминг и Рун делают последние юстировки и ставят на место алюминиевую крышку лазерного шкафа. Вы можете себе представить, как все мы были рады видеть, что эта штука работает.

С работающим станком мы получили возможность протестировать наш проект в реальной жизни. Корпус для нашей RFID-метки сделали из 2-миллиметрового огрстекла. Этот корпус - первый объект, сделанный на PhotonSaw, да!

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

Тестовая катушка была намотана, без всяких расчетов, проводом 0,4 мм в 66 витков. И, очевидно, нам опять повезло, потому что катушка получилась точно такой как надо, индуктивностью 645 мкГн, с подключенной меткой давая резонансную частоту 125,2 кГц. Тест на дверном считывателе показал, что прототип работает просто прекрасно с этой катушкой.

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

Станок PhotonSaw еще не совсем в нормальном состоянии: гравировка на верхней крышке значительно съехала. Необходимо его окончательно отладить перед изготовлением финальной версии корпуса. Изогнутые контуры также подверглись ошибке расчета в программном обеспечении, так как луч не вернулся в начальное положение после прохода замкнутой траектории. Но во всяком случае, кривые выглядят действительно гладкими.

Сборка печатной платы

Прибыла заказанная плата:

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

Через разделительную емкость (47 пФ имеют сопротивление примерно 27 кОм на частоте 125 кГц) и защитные диоды ток поступает на шины питания. Энергии, поступающей с катушки, хватает на поддержание напряжения питания около 1 В. Ток может достигать 250-500 мкА. Удивительно, но микросхемы 74HC, похоже, работают при таком питании. К сожалению, при таком напряжении происходят довольно странные вещи. Микросхемы 74HC имеют внутреннюю схему сброса, и нужно убедиться, что она срабатывает. Обратите внимание, что отключение защитных диодов не помогает. На входах микросхем есть внутренние защитные диоды, которые в этом случае открываются и выполняют ту же работу.

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

Симптомы наблюдались следующие: метка работает некоторое время, при этом посылая корректные данные. Если катушку убрать от считывателя, а затем вернуть обратно, можете делать ставки, выключится ли при этом метка. Иногда срабатывает, иногда - нет. Отключение ФАПЧ ухудшает ситуацию. Низкое энергопотребление приводит к тому, что ридер время от времени будет принимать данные от выключенной метки. Вот что значит «энергоэффективная система».

Существует два решения: 1) уменьшить конденсатор в цепи восстановления тактового сигнала до 15 пФ, и 2) включить между питанием и землей резистор 22-100 кОм для сброса лишней энергии. Второй метод приводит к росту утечек во время работы и на самом деле не требуется при уменьшении емкости конденсатора. Тем не менее, он предусмотрен как опция, и это все равно лучше, чем неопределенное состояние микросхем.

Модуляция током или напряжением

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

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

Было принято решение перевести модулятор из режима модуляции напряжением в режим модуляции током. Для первого режима резистор находился в цепи стока, а теперь он включен между истоком и землей. На этом резисторе будет падать напряжение затвор-исток, пока не останется значение чуть выше порога открывания транзистора (0,9-1,1 В), которое переведет транзистор в линейный режим. Теперь ток через транзистор будет стабильным, независимо от напряжения на стоке.

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

Законченная версия 1

Можно заметить внесенные изменения на печатной плате. У меня не было 15 пФ SMD-конденсатора, пришлось впаять обычный, с ногами. Модулятор оброс дополнительными резисторами на истоках транзисторов. В целом приемлемо для первой версии.

(картинки кликабельны)





Видео-демонстрация

Заключение

Вы можете подумать, что этот проект, собранный на логике 7400, можно отнести к ретро-схемотехнике, но это не совсем так. Во-первых, современное семейство 74HC не такое уж и старое. Во-вторых, низкопотребляющие схемы всегда актуальны. В-третьих, микросхемы одиночных логических элементов (такие, как использованный триггер Шмитта) часто используются в современных разработках. Часто забывают, что развитие технологий не прекращается и для старых семейств микросхем. Они просто стали менее заметны на фоне общего разнообразия.

Аналоговая часть оказалась сложнее в разработке, чем цифровая. Частично из-за отсутствия спецификаций, но в основном, за счет множества компромиссов, необходимых для соответствия параметрам, и непредвиденных побочных эффектов. Цифровые конструкции имеют относительно мало вариантов, в то время как аналоговые обычно требуют баланса между различными (и часто противоположными) критериями.

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

Теперь посмотрим, как этот проект покажет себя на конкурсе 7400 2012 года. Подача заявок на конкурс заканчивается 31 ноября. Пожелаем автору удачи! - Прим. перев.

Теги:

  • RFID
  • 7400 Contest
  • overengineering
  • логические схемы
  • грабли повсюду
Добавить метки Метка EM4100 хранит 64 бита данных, значит, конструкция должна содержать 64-битный сдвиговый регистр, составленный из восьми 8-битных регистров 74HC165. Регистр перезагружается после каждых 64 сдвигов, чтобы сбросить данные и начать сначала. Данные на входах регистра следующие:
  • Паттерн синхронизации: девять единиц
  • Идентификатор производителя/версии: 2 блока по 5 бит, из которых 4 бита - данные, а пятый - четность
  • Уникальный идентификатор: 8 блоков по 5 бит, из которых 4 бита - данные, а пятый - четность
  • Контрольная сумма: 4 бита четности, подсчитанные по столбцам
  • Стоп-бит: «0»

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

В качестве стандартной отмазки напомню, что автор (И переводчик! - Прим. перев. ) не несет никакой ответствености за последствия использования информации из данной статьи. Читатель должен сам отвечать за все свои действия.

Корпус

Иногда очень везет. Красивый корпус не помешал бы именно сейчас, когда прототип закончен, а печатная плата заказана. И именно в это время Флеминг закончил собирать и запустил станок лазерной резки OSAA PhotonSaw . После года работы над проектом лазер готов вырезать свои первые детали. Флемминг и Рун делают последние юстировки и ставят на место алюминиевую крышку лазерного шкафа. Вы можете себе представить, как все мы были рады видеть, что эта штука работает.

С работающим станком мы получили возможность протестировать наш проект в реальной жизни. Корпус для нашей RFID-метки сделали из 2-миллиметрового огрстекла. Этот корпус - первый объект, сделанный на PhotonSaw, да!

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

Тестовая катушка была намотана, без всяких расчетов, проводом 0,4 мм в 66 витков. И, очевидно, нам опять повезло, потому что катушка получилась точно такой как надо, индуктивностью 645 мкГн, с подключенной меткой давая резонансную частоту 125,2 кГц. Тест на дверном считывателе показал, что прототип работает просто прекрасно с этой катушкой.

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

Станок PhotonSaw еще не совсем в нормальном состоянии: гравировка на верхней крышке значительно съехала. Необходимо его окончательно отладить перед изготовлением финальной версии корпуса. Изогнутые контуры также подверглись ошибке расчета в программном обеспечении, так как луч не вернулся в начальное положение после прохода замкнутой траектории. Но во всяком случае, кривые выглядят действительно гладкими.

Сборка печатной платы

Прибыла заказанная плата:

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

Через разделительную емкость (47 пФ имеют сопротивление примерно 27 кОм на частоте 125 кГц) и защитные диоды ток поступает на шины питания. Энергии, поступающей с катушки, хватает на поддержание напряжения питания около 1 В. Ток может достигать 250-500 мкА. Удивительно, но микросхемы 74HC, похоже, работают при таком питании. К сожалению, при таком напряжении происходят довольно странные вещи. Микросхемы 74HC имеют внутреннюю схему сброса, и нужно убедиться, что она срабатывает. Обратите внимание, что отключение защитных диодов не помогает. На входах микросхем есть внутренние защитные диоды, которые в этом случае открываются и выполняют ту же работу.

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

Симптомы наблюдались следующие: метка работает некоторое время, при этом посылая корректные данные. Если катушку убрать от считывателя, а затем вернуть обратно, можете делать ставки, выключится ли при этом метка. Иногда срабатывает, иногда - нет. Отключение ФАПЧ ухудшает ситуацию. Низкое энергопотребление приводит к тому, что ридер время от времени будет принимать данные от выключенной метки. Вот что значит «энергоэффективная система».

Существует два решения: 1) уменьшить конденсатор в цепи восстановления тактового сигнала до 15 пФ, и 2) включить между питанием и землей резистор 22-100 кОм для сброса лишней энергии. Второй метод приводит к росту утечек во время работы и на самом деле не требуется при уменьшении емкости конденсатора. Тем не менее, он предусмотрен как опция, и это все равно лучше, чем неопределенное состояние микросхем.

Модуляция током или напряжением

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

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

Было принято решение перевести модулятор из режима модуляции напряжением в режим модуляции током. Для первого режима резистор находился в цепи стока, а теперь он включен между истоком и землей. На этом резисторе будет падать напряжение затвор-исток, пока не останется значение чуть выше порога открывания транзистора (0,9-1,1 В), которое переведет транзистор в линейный режим. Теперь ток через транзистор будет стабильным, независимо от напряжения на стоке.

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

Законченная версия 1

Можно заметить внесенные изменения на печатной плате. У меня не было 15 пФ SMD-конденсатора, пришлось впаять обычный, с ногами. Модулятор оброс дополнительными резисторами на истоках транзисторов. В целом приемлемо для первой версии.

(картинки кликабельны)





Видео-демонстрация

Заключение

Вы можете подумать, что этот проект, собранный на логике 7400, можно отнести к ретро-схемотехнике, но это не совсем так. Во-первых, современное семейство 74HC не такое уж и старое. Во-вторых, низкопотребляющие схемы всегда актуальны. В-третьих, микросхемы одиночных логических элементов (такие, как использованный триггер Шмитта) часто используются в современных разработках. Часто забывают, что развитие технологий не прекращается и для старых семейств микросхем. Они просто стали менее заметны на фоне общего разнообразия.

Аналоговая часть оказалась сложнее в разработке, чем цифровая. Частично из-за отсутствия спецификаций, но в основном, за счет множества компромиссов, необходимых для соответствия параметрам, и непредвиденных побочных эффектов. Цифровые конструкции имеют относительно мало вариантов, в то время как аналоговые обычно требуют баланса между различными (и часто противоположными) критериями.

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

Теперь посмотрим, как этот проект покажет себя на конкурсе 7400 2012 года. Подача заявок на конкурс заканчивается 31 ноября. Пожелаем автору удачи! - Прим. перев.

Теги: Добавить метки

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

Применение

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

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

История

В 1945 году Леон Термен изобрел прослушивающее устройство для Советского Союза, которое повторно передавало радиоволны с добавленной аудиоинформацией. Звуковые колебания при вибрации влияли на диафрагму, которая слегка меняла форму резонатора, модулировавшего отраженную радиочастоту. Несмотря на то что это устройство было скрытым прибором для прослушивания, а не идентификационным тегом, оно считается предшественником USB RFID-считывателя, поскольку активировалось аудиоволнами из внешнего источника. Транспондеры по-прежнему используются большинством работающих самолетов. А раньше подобная технология, такая как считыватель RFID-меток, регулярно использовалась союзниками и Германией во Второй мировой войне для идентификации самолетов.

Устройство Марио Кардулло, запатентованное 23 января 1973 года, было первым истинным предшественником современной RFID, поскольку это был пассивный радиоприемник с памятью. Первоначальное устройство было пассивным, с питанием от опросного сигнала. Оно было продемонстрировано в 1971 году администрации Нью-Йорка и другим потенциальным пользователям и состояло из транспондера с 16-разрядной памятью для использования в качестве платного устройства. Основной патент Cardullo охватывает использование радиочастот, звука и света в качестве среды передачи.

Область использования

Первоначальный бизнес-план, представленный инвесторам в 1969 году, демонстрировал следующие сферы применения считывателя RFID:

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

Ранняя демонстрация отраженной мощности (модулированного обратного рассеяния) RFID-меток, как пассивных, так и полупассивных, была выполнена Стивеном Деппом, Альфредом Коелле и Робертом Фрайманом в Национальной лаборатории Лос-Аламоса в 1973 году. Портативная система работала на частоте 915 МГц и использовала 12-битные теги. Этот метод применяется большинством современных UHFID и микроволновых RFID-считывателей. В современной жизни такие устройства очень востребованы.

Спецификация

Система радиочастотной идентификации использует метки, прикрепленные к идентифицируемым объектам. При изготовлении RFID-считывателя своими руками следует учитывать, что двусторонние радиопередатчики-приемники, называемые запросчиками или считывателями, посылают сигнал тегу и считывают его ответ. Метки RFID могут быть пассивными, активными или пассивными. Активный тег имеет встроенный аккумулятор и периодически передает его ID-сигнал. Пассивный аккумулятор (BAP) имеет небольшую батарею на борту и активируется при наличии считывателя RFID. Пассивная бирка дешевле и меньше, потому что у нее нет батареи. Вместо этого тег использует радиоволну, переданную считывателем. Однако для работы пассивного тега он должен быть освещен уровнем мощности примерно в тысячу раз сильнее, чем для передачи сигнала. Это влияет на интерференцию и облучение.

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

Результат: когда кладем ноутбук на «зарядку», то автоматически включается розетка с его зарядным устройством. А в остальное время розетка, понятно, выключена. Не сказать, что особо полезно, но - развлечение.

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

Для понимания: этот считыватель - не законченное устройство, а периферия для контроллера или компьютера. По этой причине для использования потребуется некоторое количество усилий. Готовых рецептов «из коробки» как бы нет, зато все остальное зависит от фантазии и способностей.

Что обычно делают? Чаще всего - замки с открытием по карточке, учет рабочего времени, СКД для домашних животных (допуск к корму, например).

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

Скорее всего, вы неоднократно встречались с такими карточками. Чаще всего - в виде офисного пропуска. Другой яркий пример - бесконтактные билеты для прохода в метро.

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

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

Гребенок в комплекте было две, но одну (угловую) я, простите уже запаял. Поэтому вида платы без гребенок нет

Этот комплект включает в себя считыватель, гребенку для простого макетирования и даже монтажа на «материнскую» плату и два идентификатора: карточку MIFARE 1K и аналогичный по возможностям брелок. То есть, вполне достаточно для экспериментов.

.

.

.

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

С батарейкой ААА

Т.е. можно не мучить себя компромиссом между удобством и универсальностью (гребенкой) и экономией места в расчете на встраивание (прямой пайкой проводов).

Среди прочего на плате также есть красный светодиод, который, увы, совершенно неинформативен. Суть в том, что он горит даже при оторванном питании - очевидно, достаточно уровней на SPI-интерфейсе. Да и на карточки он никак не реагирует.

Одним словом - горит, когда считыватель хоть как-то подключен к Arduino, что не гарантирует его работоспособности.

Что касается дальности срабатывания, то субъективно порог находится на расстоянии 2 см от поверхности платы. В пределах 2 - 2.5 см - область неуверенного срабатывания.

Характеристики (от продавца):

Напряжение: 3.3В

Потребляемый ток в активном состоянии:13-26 мА

Потребляемый ток в состоянии ожидания: 10-13 мА

Ток в режиме сна: менее 80 мкА

Пиковое потребление: менее 30 мА

Рабочая частота: 13.56 МГц

Поддержвиаемые типы карт: MIFARE S50, MIFARE S70, MIFARE UltraLight, MIFARE Pro, MIFARE DESfire

Интерфейс: SPI

Размеры: 40х60 мм

Из приведенного выше видно, что отличился я дважды. Во-первых, не посмотрел на напряжение питания. А 3.3В, между прочим, означает, что проще всего использовать эту плату с Arduino Uno, Mega и прочими (или аналогичными платами), оснащенными регуляторами напряжения и выходом 3.3В. Благо максимальный потребляемый ток не выходит за пределы возможностей платформы.

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

Во-вторых, интерфейс SPI в требует 5 (!) проводов для подключения. Т.е. в сумме к считывателю подходит шлейф из 7 проводников, а это довольно расточительно. Для экономии имело смысл ориентироваться на I2C, но где я и где этот смысл?!

Подключение
Я бы хотел разделить подключение на софт и хард, но все так тесно переплелось - и вылилось в не очень интересную проблему.

Одним словом, все знают для RC522. Но почему-то никто не задается вопросом, почему в ридми, приложенном к библиотеке и в табличке в одном из ее примеров совершенно разная «распиновка» подключения считывателя.

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

Наконец, третья причина - два пина (SS и RST) допускают произвольное подключение и конфигурируются в коде. При этом по умолчанию в коде примера забита конфигурация для Arduino Uno, а если у вас Mega и вы подключали по агитке из ридми или таблички в начале примера, то, разумеется, промахнетесь.

Но фишка в том, что третья причина довольно очевидна, и ее я более-менее обошел, сразу задав свои пины, потому что у меня под руками была плата Arduino Mega. Поэтому же, кстати, меня обошла и вторая беда.

А вот первая - подключение пинов SPI помучила меня вдоволь. Я же и предположить не мог, что человек, сделавший успешную библиотеку, мог так накосячить в банальном подключении трех проводочков.

Иными словами, подключение ни по первому, ни по второму варианту, указанному в описании библиотеки, не помогло. Сначала я подумал, что у меня «мертвая плата». Поэтому, несмотря на позднее время, взял себя в руки и распаял гребенку на вторую (я запасливый и купил сразу три). Результат оказался аналогичным, несмотря на то, что я неоднократно проверял подключение по имеющимся на руках «распиновкам».

Даже не спрашивайте, почему я не пошел на Arduino.cc, чтобы посмотреть, как разведен SPI на Mega. По-моему, это случилось какое-то помутнение.

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

И обе платы заработали.

Чтобы очистить совесть - загрузил пример с «проблемной» библиотекой, и он оказался рабочим тоже.

Вот такими карточками я мучил считыватель. Слева направо: карточка из комплекта, карточка с буквой N, билет метро MIFARE UltraLight, карточка РЖД

А это - как примерно выглядит дамп содержимого и вообще распознавание карточек с библиотекой RFID и ее же примером DumpInfo

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

В завершение правильная распиновка для китайской библиотеки и Mega, к которой я подключался:

Mega RC522
3.3V VCC
GND GND
RESET RST

50 MISO
51 MOSI
52 SCK
53 NSS

И для Uno (ее не проверял):

Uno RC522
3.3V VCC
GND GND
5 RST

12 MISO
11 MOSI
13 SCK
10 NSS

Вместе с большой сестрой

.

.

В результате простых испытаний, которые я все-таки выполнил, также выяснилось, что считыватель нормально записывает (вот такая игра слов) карты MIFARE 1K из тех, что были с ним в комплекте.

Вторая особенность в том, что эта штука адекватно реагирует, если к нему одновременно поднести несколько карт. Я подносил две карты, опять же, из тех, что были в комплекте. При этом пример DumpInfo из библиотеки RFID по порядку выводит содержимое обеих карт. Т.е. сначала - содержимое одной карты, а следом за ней - второй.

И шашечки, и ехать
Итак, подключили, полюбовались на дамп содержимого памяти карточек MIFARE 1K. Что дальше? А дальше мне было немного лень возиться с розеткой, и я вспомнил, что карточки метро, вроде бы, работают по тому же протоколу.

Сходил, взял свою, приложил, полюбовался на ее содержимое. Как и обещали: MIFARE UltraLight, 64 байта памяти и не очень понятно, где там поездки, где срок действия, ну и вообще не очень понятно.

Гугление на тему использования Arduino с этими карточками не дало ничего особо полезного. Чаще всего карточки метро, как выяснилось, используют в качестве ультрадешевых (т.е. бесплатных) NFC-меток во всяких самодельных системах вроде замков, исполнения сценариев и прочего, где достаточно знать серийный номер карты для идентификации. А вот готовых библиотек и решений для вывода информации о поездках я почему-то не нашел: то ли так спать хотел, то ли их действительно нет (см. синдром «Неуловимого Джо»).

Зато я нашел замечательный и очень романтичный текст авторства Александра «Dark Simpson» Симонова под заголовком " ", опубликованный, судя по всему, в каком-то из номеров Хакера. Несмотря на любопытную природу текста, в нем есть масса полезной информации о структуре хранения данных, в том числе на интересующих меня билетах.

Значащая часть билета - номер - проверяемая визуально

Так что на следующий день я постарался переборость свое отвращение к HEX и полез разбираться. Вот такой дамп билета я получил с помощью библиотеки RFID:

Card UID: 04 6F 25 62 04 33 82
PICC type: MIFARE Ultralight or Ultralight C

Page 0 1 2 3
0 04 6F 25 C6
1 62 04 33 82
2 D7 48 F0 00
3 00 07 FF FC
4 45 DA 21 06
5 D0 E5 3D 00
6 1F E8 00 00
7 1F E8 00 00
8 1F 8F 5A 00
9 40 19 2E D2
10 19 91 59 7C
11 1F AB 91 C8
12 1F 8F 5A 00
13 40 19 2E D2
14 19 91 59 7C
15 1F AB 91 C8

Здесь Card UID - уникальный идентификатор карты (суть серийный номер), а остальное - 16 страниц памяти, каждая из которых содержит по 4 байта. Вместе - 64 байта.

Из чтения текста про билеты метро отметил наиболее полезные моменты:

1) Номер билета (который отпечатан на нем) зашит в 32 битах, начиная с 21 бита на странице 4: 10 6D 0E 53;

2) Дата выдачи билета в днях, прошедших с 01.01.1992 г. - первые два байта странице 8: 1F 8F;

3) Срок действия в днях - третий байт на странице 8: 5А.

4) Наконец, количество оставшихся поездок - второй байт на странице 9: 19.

С этим уже можно было работать.

Немного поковырялся в примере DumpInfo из библиотеки RFID, в ее же коде, чтобы понять, какие функции за что отвечают и чем могут быть полезны, в форумах Arduino, чтобы посмотреть реализацию вычисления даты (вообще, можно на любом языке смотреть, но мне было проще искать ближе к целевой платформе).

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

#include #include #define SS_PIN 53 #define RST_PIN 9 MFRC522 mfrc522(SS_PIN, RST_PIN); // объект MFRC522 unsigned long uidDec, uidDecTemp; // для отображения номера карточки в десятичном формате byte bCounter, readBit; unsigned long ticketNumber; void setup() { Serial.begin(9600); SPI.begin(); // инициализация SPI mfrc522.PCD_Init(); // инициализация MFRC522 Serial.println("Waiting for card..."); } void loop() { // Поиск новой карточки if (! mfrc522.PICC_IsNewCardPresent()) { return; } // Выбор карточки if (! mfrc522.PICC_ReadCardSerial()) { return; } uidDec = 0; // Выдача серийного номера карточки Serial.print("Card UID: "); for (byte i = 0; i < mfrc522.uid.size; i++) { // Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); // Serial.print(mfrc522.uid.uidByte[i], HEX); uidDecTemp=mfrc522.uid.uidByte[i]; uidDec=uidDec*256+uidDecTemp; } Serial.println(uidDec); Serial.println(); // Выдача типа карты byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); // запрос типа Serial.print("Card type: "); Serial.println(mfrc522.PICC_GetTypeName(piccType)); // трансляция типа в читаемый вид if (piccType != MFRC522::PICC_TYPE_MIFARE_UL) { // если не билетная карта Serial.print("Not a valid card: "); // так и говорим Serial.println(piccType); // Halt PICC mfrc522.PICC_HaltA(); // остановка чипа return; } // сюда мы приедем, если чип правильный byte status; byte byteCount; byte buffer; // длина массива (16 байт + 2 байта контрольная сумма) byte pages={4, 8}; // страницы с данными byte pageByte; // счетчик байтов страницы byteCount = sizeof(buffer); byte bCount=0; for (byte i=0; i<2; i++) { // начинаем читать страницы status = mfrc522.MIFARE_Read(pages[i], buffer, &byteCount); if (status != MFRC522::STATUS_OK) { Serial.print("Read error: "); Serial.println(mfrc522.GetStatusCodeName(status));} else { if (pages[i] == 4) { bCounter = 0; // 32-битный счетчик для номера // биты 0-3 for (bCount=0; bCount<4; bCount++) { readBit = bitRead(buffer, (bCount+4)); setBitsForGood(readBit); } // биты 4 - 27 for (pageByte=5; pageByte > 2; pageByte--) { for (bCount=0; bCount<8; bCount++) { readBit = bitRead(buffer, bCount); setBitsForGood(readBit); } } // биты 28-31 for (bCount=0; bCount<4; bCount++) { readBit = bitRead(buffer, bCount); setBitsForGood(readBit); } Serial.print("Ticket number: "); Serial.println(ticketNumber, DEC); } if (pages[i] == 8) { // читаем дату выдачи Serial.print("Issued: "); unsigned int issueDate = buffer * 256 + buffer; // количество дней с 01.01.1992 в десятичном формате, 256 - сдвиг на 8 бит printIssueDate(issueDate); Serial.print("Good for (days): "); // срок действия Serial.print(buffer, DEC); Serial.println(); Serial.print("Trip reminder: "); // количество оставшихся поездок Serial.print(buffer, DEC); Serial.println(); } } } // Halt PICC mfrc522.PICC_HaltA(); } void printIssueDate(unsigned int incoming) { boolean isLeap = true; // признак високосного года int days={0,31,59,90,120,151,181,212,243,273,304,334}; // последний по порядку день месяца для обычного года byte dayOfMonth, monthCounter; unsigned int yearCount; incoming = incoming+1; // подогнал под ответ, но возможно это как раз необходимая коррекция, потому что начало отсчета - 01.01.1992, а не 00.01.1992 for (yearCount = 1992; incoming >366; yearCount++) { // считаем год и количество дней, прошедших с выдачи билета if ((yearCount % 4 == 0 && yearCount % 100 != 0) || yearCount % 400 == 0) { incoming = incoming - 366; isLeap = true; } else { incoming = incoming - 365; isLeap = false; } } for (monthCounter = 0; incoming > days; monthCounter++) { // узнаем номер месяца } // считаем день месяца if (isLeap == true) { // если високосный год if (days>31) { // если не первый месяц, то добавляем к последнему дню месяца единицы dayOfMonth = incoming - (days+ 1); } else { dayOfMonth = incoming - (days); // если первый - ничего не добавляем, потому что сдвиг начинается с февраля } } else { dayOfMonth = incoming - (days); // если не високосный год } Serial.print(dayOfMonth); Serial.print("."); Serial.print(monthCounter); Serial.print("."); Serial.print(yearCount); Serial.println(); } void setBitsForGood(byte daBeat) { if (daBeat == 1) { bitSet(ticketNumber, bCounter); bCounter=bCounter+1; } else { bitClear(ticketNumber, bCounter); bCounter=bCounter+1; } }

Что мне дает этот скетч? Ну, во-первых, небольшую тренировку головы - приходилось, знаете, думать, когда разбирался со считывателем и кодом. Во-вторых, я всегда могу узнать, сколько осталось поездок, и когда закончится карточка. С учетом того, что у меня в смартфоне NFC нет, получается довольно удобно.

Результат работы монстра

Разумеется, в данном случае даже речь не идет о том, чтобы сделать «копию» билета. Это исключительно информационная функция, дублирующая терминал в вестибюле метро.

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

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

Логика простая: если считыватель видит карту с определенным номером, то включает розетку. Если не видит - выключает.

РОЗЕТОЧНЫЙ МОНСТР

#include #include #define SS_PIN 53 #define RST_PIN 9 MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. unsigned long uidDec, uidDecTemp; #include RCSwitch mySwitch = RCSwitch(); boolean switchOn = false; boolean cardRead = false; void setup() { Serial.begin(9600); // Initialize serial communications with the PC SPI.begin(); // Init SPI bus mfrc522.PCD_Init(); // Init MFRC522 card mySwitch.enableTransmit(8); Serial.println("Waiting for card..."); } void loop() { byte status; byte byteCount; byte buffer; // длина массива (16 байт + 2 байта контрольная сумма) byteCount = sizeof(buffer); uidDec = 0; status = mfrc522.PICC_RequestA(buffer, &byteCount); if (mfrc522.PICC_ReadCardSerial()) { for (byte i = 0; i < mfrc522.uid.size; i++) { uidDecTemp=mfrc522.uid.uidByte[i]; uidDec=uidDec*256+uidDecTemp; } if ((uidDec==2218415941) && (switchOn == false)) { mySwitch.sendTriState("00110000F000"); switchOn = true; // Serial.println("Switched On"); } mfrc522.PICC_ReadCardSerial(); } else { if (switchOn == true) { mySwitch.sendTriState("001100000000"); // Serial.println("Switched Off"); switchOn = false; } } }

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

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

Вот так работает розеточный монстр

Подводя итог: недорого, просто, довольно удобно и стимулирует фантазию.

Ps. Я уверен, что вы можете лучше, и что я ничего не понимаю в Arduino и программировании.

Ссылочки всякие
Обзор понравился +67 +173