Рейтинг@Mail.ru
Ключ типа iButton
Ключ типа iButton

Как сделать копию ключа для домофона в домашних условиях

2 комментарии Arduino
Print Friendly, PDF & Email

С помощью Ардуино можно сделать дома копию ключа для домофона за 15 минут, если, к примеру, мастерская закрыта, а ключ нужен срочно.

Инструкция по чтению и записи ключа iButton (1-wire) с помощью Arduino

Нам понадобится:

1Схема подключения ключа к Arduinoпо однопроводному интерфейсу

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

Ключи для домофона, которые мы будем подключать к Arduino (их иногда называют iButton или Touch Memory), считываются и записываются по однопроводному интерфейсу 1-wire. Поэтому схема подключения очень проста. Нам нужны лишь пара проводов и подтягивающий резистор номиналом 2,2 кОм. Схема соединений показана на рисунке.

Схема подключения ключа iButton к Arduino по интерфейсу 1-wire
Схема подключения ключа iButton к Arduino по интерфейсу 1-wire

Собранная схема может выглядеть примерно так:

Схема для чтения ключа Dallas на Arduino
Схема для чтения ключа Dallas на Arduino

2Считывание идентификатора ключа iButton с помощью Arduino

Для работы с интерфейсом 1-wire существуют готовые библиотеки для Ардуино. Можно воспользоваться, например, этой. Скачиваем архив и распаковываем в папку /libraries/, расположенную в каталоге Arduino IDE. Теперь мы можем очень просто работать с данным протоколом.

Загрузим в Ардуино стандартным способом этот скетч:

Скетч чтения ключа iButton с помощью Arduino (разворачивается)
#include <OneWire.h>
OneWire iButton(10); // создаём объект 1-wire на 10 выводе

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

void loop(void) {
  delay(1000); // задержка 1 сек
  byte addr[8]; // массив для хранения данных ключа
  
  if ( !iButton.search(addr) ) { // если ключ не приложен
      Serial.println("No key connected..."); // сообщаем об этом
      return; // и прерываем программу
  }
  
  Serial.print("Key : ");
  for(int i=0; i<8; i++) {
    Serial.print(addr[i], HEX); // выводим побайтно данные ключа
    Serial.print(" ");
  }
  Serial.println();
  iButton.reset(); // сброс ключа
}

Данный скетч показывает номер ключа для домофона, который подключён к схеме. Это то, что нам и нужно сейчас: мы должны узнать номер ключа, копию которого хотим сделать. Подключим Ардуино к компьютеру. Запустим монитор последовательного порта: Инструменты Монитор последовательного порта (или сочетание клавиш Ctrl+Shift+M).

Теперь подключим ключ к схеме. Монитор порта покажет номер ключа. Запомним этот номер.

Запоминаем номер ключа iButton, выводимый в монитор последовательного порта
Запоминаем номер ключа iButton, выводимый в монитор последовательного порта

А вот какой обмен происходит на однопроводной линии при чтении идентификатора ключа (подробнее – далее):

Диаграмма взаимодействия ключа Dallas с Arduino по однопроводному интерфейсу (1-wire)
Диаграмма взаимодействия ключа Dallas с Arduino по однопроводному интерфейсу (1-wire)

На рисунке, конечно, не видны все детали реализации. Поэтому в конце статьи я прикладываю временную диаграмму в формате *.logicdata , снятую с помощью логического анализатора и программы Saleae Logic Analyzer и открываемую ей же. Программа бесплатная и скачивается с официального сайта Saleae. Чтобы открыть файл *.logicdata нужно запустить программу, нажать сочетание Ctrl+O или в меню Options (расположено вверху справа) выбрать пункт Open capture / setup.

3Запись идентификатора ключа Dallasс помощью Arduino

Теперь напишем скетч для записи данных в память ключа iButton.

Скетч записи ключа iButton с помощью Arduino (разворачивается)
#include <OneWire.h>  // подключаем библиотеку
const int pin = 10;   // объявляем номер пина
OneWire iButton(pin); // объявляем объект OneWire на 10-ом пине

// номер ключа, который мы хотим записать в iButton:
byte key_to_write[] = { 0x01, 0xF6, 0x75, 0xD7, 0x0F, 0x00, 0x00, 0x9A };

void setup(void) { 
  Serial.begin(9600); 
  pinMode(pin, OUTPUT);  
}

void loop(void) {
  delay(1000); // задержка на 1 сек  
  iButton.reset(); // сброс устройства 1-wire
  delay(50); 
  iButton.write(0x33); // отправляем команду "чтение"

  byte data[8]; // массив для хранения данных ключа
  iButton.read_bytes(data, 8); // считываем данные приложенного ключа, 8х8=64 бита
  
  if ( OneWire::crc8(data, 7) != data[7] ) { // проверяем контрольную сумму приложенного ключа
      Serial.println("CRC error!"); // если CRC не верна, сообщаем об этом
      return; // и прерываем программу
  }

  if (data[0] & data[1] & data[2] & data[3] & data[4] & data[5] & data[6] & data[7] == 0xFF) {
    return; // если ключ не приложен к считывателю, прерываем программу и ждём, пока будет приложен
  }

  Serial.print("Start programming..."); // начало процесса записи данных в ключ
  
  for (int i = 0; i < 8; i++) {
    // формирование 4-х байт для записи в ключ - см. рис.4 из datasheet для подробностей
    iButton.reset(); // сброс ключа 
    data[0] = 0x3C; // отправляем команду "копировать из буфера в ПЗУ"
    data[1] = i; // указываем байт для записи
    data[2] = 0;
    data[3] = key_to_write[i]; 
    iButton.write_bytes(data, 4); // записываем i-ый байт в ключ
  
    uint8_t b = iButton.read(); // считываем байт из ключа
    
    if (OneWire::crc8(data, 4) != b) { // при ошибке контрольной суммы
        Serial.println("Error while programming!"); // сообщаем об этом
        return; // и отменяем запись ключа
    }
    send_programming_impulse(); // если всё хорошо, посылаем импульс для записи i-го байта в ключ
  }
  Serial.println("Success!"); // сообщение об успешной записи данных в ключ  
}

// Инициализация записи данных в ключ-таблетку iButton:
void send_programming_impulse() { 
  digitalWrite(pin, HIGH); 
  delay(60);
  digitalWrite(pin, LOW); 
  delay(5);
  digitalWrite(pin, HIGH); 
  delay(50); 
}

Не забудьте задать номер своего оригинального ключа в массиве key_to_write, который мы узнали ранее.

Загрузим этот скетч в Arduino. Откроем монитор последовательного порта (Ctrl+Shift+M). Подключим к схеме ключ, который будет клоном оригинального ключа. О результате программирования монитор последовательного порта выведет соответствующее сообщение.

Если данный скетч не сработал, попробуйте заменить код после Serial.print("Start programming...") и до конца функции loop() на следующий:

Дополнительный скетч записи ключа iButton с помощью Arduino (разворачивается)
delay (200);
iButton.skip();
iButton.reset();
iButton.write(0x33); // чтение текущего номера ключа
Serial.print("ID before write:");
for (byte i=0; i<8; i++){
  Serial.print(' ');
  Serial.print(iButton.read(), HEX);
}    
Serial.print("\n");

iButton.skip();
iButton.reset();
iButton.write(0xD1); // команда разрешения записи
digitalWrite(pin, LOW); 
pinMode(pin, OUTPUT); 
delayMicroseconds(60);
pinMode(pin, INPUT); 
digitalWrite(pin, HIGH); 
delay(10);

// выведем ключ, который собираемся записать:
Serial.print("Writing iButton ID: ");
for (byte i=0; i<8; i++) {
  Serial.print(key_to_write[i], HEX);
  Serial.print(" ");
}
Serial.print("\n");
                 
iButton.skip();
iButton.reset();
iButton.write(0xD5); // команда записи
for (byte i=0; i<8; i++) {
  writeByte(key_to_write[i]);
  Serial.print("*");
}
Serial.print("\n");

iButton.reset();
iButton.write(0xD1); // команда выхода из режима записи
digitalWrite(pin, LOW); 
pinMode(pin, OUTPUT); 
delayMicroseconds(10);
pinMode(pin, INPUT); 
digitalWrite(pin, HIGH); 
delay(10);

Serial.println("Success!"); 
delay(10000);

Здесь функция writeByte() будет следующей:

int writeByte(byte data) {
  int data_bit;
  for(data_bit=0; data_bit<8; data_bit++) {
    if (data & 1) {
      digitalWrite(pin, LOW); 
      pinMode(pin, OUTPUT);
      delayMicroseconds(60);
      pinMode(pin, INPUT); 
      digitalWrite(pin, HIGH);
      delay(10);
    } else {
      digitalWrite(pin, LOW); 
      pinMode(pin, OUTPUT);
      pinMode(pin, INPUT); 
      digitalWrite(pin, HIGH);
      delay(10);
    }
    data = data >> 1;
  }
  return 0;
}

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

Ключи для домофона бывают разных типов. Данный код подойдёт не для всех ключей, а только для RW1990 или RW1990.2. Программирование ключей других типов может привести к выходу ключей из строя!

При желании можно переписать программу для ключа другого типа. Для этого воспользуйтесь техническим описанием Вашего типа ключа (datasheet) и изменить скетч в соответствии с описанием. Скачать datasheet для ключей iButton можно в приложении к статье.

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

4Описание однопроводного интерфейса 1-Wire

Давайте чуть глубже познакомимся с интерфейсом One-wire. По организации он похож на интерфейс I2C: в нём также должно присутствовать ведущее устройство (master), которое инициирует обмен, а также одно или несколько ведомых устройств (slave). Все устройства подключены к одной общей шине. Устройства iButton – всегда ведомые. В качестве мастера чаще всего выступает микроконтроллер или ПК. Скорость передачи данных составляет 16,3 кбит/сек. Шина в состоянии ожидания находится в логической "1" (HIGH). В данном протоколе предусмотрены всего 5 типов сигналов:

  • импульс сброса (master)
  • импульс присутствия (slave)
  • запись бита "0" (master)
  • запись бита "1" (master)
  • чтение бита (master)
За исключением импульса присутствия все остальные генерирует мастер. Обмен всегда происходит по схеме: 1) Инициализация 2) Команды работы с ПЗУ 3) Команды работы с ППЗУ 4) Передача данных.

1) Инициализация

Инициализация заключается в том, что ведущий выставляет условие сброса RESET (на время от 480 мкс или более опускает линию в "0", а затем отпускает её, и за счёт подтягивающего резистора линия поднимается в состояние "1"), а ведомый не позднее чем через 60 мкс после этого должен подтвердить присутствие, также опустив линию в "0" на 60…240 мкс и затем освободив её:

Инициализация: сигнал сброса и подтверждения протокола 1-wire
Инициализация: сигнал сброса и подтверждения протокола 1-wire

2) Команды работы с ПЗУ

Если после импульса инициализации не пришёл сигнал подтверждения, мастер повторяет опрос шины. Если сигнал подтверждения пришёл, то мастер понимает, что на шине есть устройство, которое готово к обмену, и посылает ему одну из четырёх 8-битных команд работы с ПЗУ:

НазваниеКомандаНазначение
Чтение (Read ROM)0x33Мастер считывает 64 первых битов iButton, в которых содержатся: 8 бит кода семейства (*), 48 бит серийного номера и 8 бит контрольной суммы.
Совпадение (Match ROM)0x55Обращение к определённому устройству с известным 64-битным номером.
Поиск (Search ROM)0xF0Позволяет определить все 64-битные номера ведомых устройств, подключённых к шине.
Пропуск (Skip ROM)0xCCПозволяет сэкономить время обмена данными с ключом благодаря тому, что мастер пропускает проверку серийного номера. Не рекомендуется к использованию в ситуации, когда на линии присутствуют несколько ведомых.

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

Коды семейств устройств типа iButton (разворачивается)
Код семействаУстройства iButtonОписание
0x01DS1990A, DS1990R, DS2401, DS2411Уникальный серийный номер-ключ
0x02DS1991Мультиключ, 1152-битная защищённая EEPROM
0x04DS1994, DS24044 кб NV RAM + часы, таймер и будильник
0x05DS2405Одиночный адресуемый ключ
0x06DS19934 кб NV RAM
0x08DS19921 кб NV RAM
0x09DS1982, DS25021 кб PROM
0x0ADS199516 кб NV RAM
0x0BDS1985, DS250516 кб EEPROM
0x0CDS199664 кб NV RAM
0x0FDS1986, DS250664 кб EEPROM
0x10DS1920, DS1820, DS18S20, DS18B20Датчик температуры
0x12DS2406, DS24071 кб EEPROM + двухканальный адресуемый ключ
0x14DS1971, DS2430A256 бит EEPROM и 64 бита PROM
0x1ADS1963L4 кб NV RAM + счётчик циклов записи
0x1CDS28E04-1004 кб EEPROM + двухканальный адресуемый ключ
0x1DDS24234 кб NV RAM + внешний счётчик
0x1FDS2409Двухканальный адресуемый ключ с возможностью коммутации на возвратную шину
0x20DS2450Четырёхканальный АЦП
0x21DS1921G, DS1921H, DS1921ZТермохронный датчик с функцией сбора данных
0x23DS1973, DS24334 кб EEPROM
0x24DS1904, DS2415Часы реального времени
0x26DS2438Датчик температуры, АЦП
0x27DS2417Часы реального времени с прерыванием
0x29DS2408Двунаправленный 8-разрядный порт ввода/вывода
0x2CDS2890Одноканальный цифровой потенциометр
0x2DDS1972, DS24311 кб EEPROM
0x30DS2760Датчик температуры, датчик тока, АЦП
0x37DS197732 кб защищённой паролем EEPROM
0x3ADS2413Двухканальный адресуемый коммутатор
0x41DS1922L, DS1922T, DS1923, DS2422Термохронные и гигрохронные датчики высокого разрешения с функцией сбора данных
0x42DS28EA00Цифровой термометр с программируемым разрешением, возможностью работать в режиме подключения к последовательному каналу и программируемыми портами ввода/вывода
0x43DS28EC2020 кб EEPROM

Данные передаются последовательно, бит за битом. Передачу каждого бита инициирует ведущее устройство. При записи ведущий опускает линию к нулю и удерживает её. Если время удерживания линии равно 1…15 мкс, значит записывается бит "1". Если время удерживания от 60 мкс и выше – записывается бит "0".

Чтение битов также инициируется мастером. В начале чтения каждого бита мастер устанавливает низкий уровень на шине. Если ведомое устройство хочет передать "0", оно удерживает шину в состоянии LOW на время от 60 до 120 мкс, а если хочет передать "1", то на время примерно 15 мкс. После этого ведомый отпускает линию, и за счёт подтягивающего резистора она возвращается в состояние HIGH.

Вот так, например, выглядит временная диаграмма команды поиска Search ROM (0xF0). Красным цветом на диаграмме отмечены команды записи битов. Обратите внимание на порядок следования битов при передаче по 1-Wire: старший бит справа, младший – слева.

Временная диаграмма отправки команды поиск (0xF0) ведомому iButton
Временная диаграмма отправки команды поиск (0xF0) ведомому iButton

Далее, если предшествующей командой подразумевается работа с ППЗУ (чтение и запись из перезаписываемой памяти ключа Dallas), то мастер передаёт команду работы с ППЗУ.

3) Команды работы с ППЗУ

Прежде чем рассматривать команды для работы с ППЗУ iButton, необходимо пару слов сказать о структуре памяти ключа. Память разделена на 4 равных участка: три из них предназначены для хранения трёх уникальных ключей, а четвёртый – для временного хранения данных. Этот временный буфер служит своеобразным черновиком, где данные готовятся для записи ключей.

Структура памяти ключа iButton
Структура памяти ключа iButton

Для работы с ППЗУ существуют 6 команд:

НазваниеКомандаНазначение
Записать во временный буфер (Write Scratchpad)0x96Используется для записи данных во временный буфер (scratchpad).
Прочитать из временного буфера (Read Scratchpad)0x69Используется для чтения данных из временного буфера.
Копировать из временного буфера (Copy Scratchpad)0x3CИспользуется для передачи данных, подготовленных во временном буфере, в выбранный ключ.
Записать пароль ключа (Write Password)0x5AИспользуется для записи пароля и уникального идентификатора выбранного ключа (одного из трёх).
Записать ключ (Write SubKey)0x99Используется для непосредственной записи данных в выбранный ключ (минуя временный буфер).
Прочитать ключ (Read SubKey)0x66Используется для чтения данных выбранного ключа.

4) Передача данных

Более подробно и с примерами низкоуровневая работа с протоколом 1-Wire описана в этой статье.

5Возможные ошибки при компиляции скетча

1) Если при компиляции скетча возникнет ошибка WConstants.h: No such file or directory #include "WConstants.h", то, как вариант, следует в файле OneWire.cpp заменить первый блок после комментариев на следующий:

#include <OneWire.h>
#include <Arduino.h>
extern "C" {
  #include <avr/io.h>
  #include <pins_arduino.h>
}

2) Если при компиляции появляется ошибка class OneWire has no member named read_bytes,то найдите и попробуйте использовать другую библиотеку для работы с интерфейсом OneWire.

Демонстрация копирования ключа iButton с помощью Arduino
Последнее изменениеПонедельник, 15 Июль 2024 19:05 Прочитано 119431 раз

Поблагодарить автора:

Поделиться

Print Friendly, PDF & Email

2 комментарии

  • Slava
    Slava 11.10.2018 17:43 Комментировать

    Ошибка в скетче
    byte key_to_write[] = { 0x01, 0xF6, 0x75, 0xD7, 0x0F, 0x00 0x00, 0x9A }; - запятая

    byte key_to_write[] = { 0x01, 0xF6, 0x75, 0xD7, 0x0F, 0x00, 0x00, 0x9A };

  • tutikaka
    tutikaka 26.02.2022 23:50 Комментировать

    Спасибо! Очень помогла статья