Иммобилайзер или радио-ключ
- Войдите на сайт для отправки комментариев
Чт, 28/08/2014 - 21:12
Захотелось как-то сделать устройство как в современных автомобилях(пока в определенной зоне нет радио ключа, багажник не откроешь или автомобиль не заведешь).
После просмотра кучи статей и постов, было собрано устройство по принципу роботы:
1. отправляем команду
2. идем спать для экономии аккумулятора
3. просыпаемся через некоторое время и идем к пункту №1
Далее устройство!
модуль зарядного + arduino Pro mini + NRF24L01i + кнопки, лампочки, аккумулятор.
Это стабилизатор напряжения ams1117-3.3 с транзистором для подачи и отключения радио модуля.
Конечно радиомодуль можно и с ардуинки слать в сон, но этот вариант дает меньше енергопотребления.
Ардуинка просто выключает с 8-мой ногы высокий уровень (куда потключена База транзистора) и радиомодуль полностю обесточен.
было добавлено еще две кнопки для дополнительного функционала (открыть двери в гараж и т.д.)
не знаю как можно оптимизировать код!
Скетч:
//Последний две кнопкой + показатель аккумулятора!!! #include <avr/sleep.h> #include <avr/power.h> #include <avr/wdt.h> #include <SPI.h> #include "RF24.h" int msg[1]; RF24 radio(9,10); const long InternalReferenceVoltage = 1115; //настройка показателей батареи const uint64_t pipes[2] = {0xF0F0F0F000LL, 0xF0F0F0F0FFLL}; extern volatile unsigned long timer0_millis; int buttonPin1 = 4; int buttonPin2 = 2; int buttonPin3 = 3; volatile int f_wdt=1; int r1=0; int i=0; int x=0; int led8 = 8; int led7 = 7; void(* resetFunc) (void) = 0; // reset arduino ISR(WDT_vect) { if(f_wdt == 0) { f_wdt=1; } else { Serial.println("WDT Overrun!!!"); } } void enterSleep(void) { attachInterrupt(1, wakeup,RISING); attachInterrupt(0, wakeup,RISING); set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_enable(); sleep_mode(); sleep_disable(); power_all_enable(); } /*************************************************** * Name: setup * * Returns: Nothing. * * Parameters: None. * * Description: Setup for the serial comms and the * Watch dog timeout. * ***************************************************/ void setup(void) { pinMode(led8, OUTPUT); pinMode(led7, OUTPUT); pinMode(buttonPin1, INPUT); pinMode(buttonPin2, INPUT); pinMode(buttonPin3, INPUT); digitalWrite(led8, HIGH); // включаем радио модуль delay(10); Serial.begin(115200); Serial.println("Initialising..."); Serial.println(msg[0]); delay(10); //Allow for serial print to complete. interrupts(); radio.begin(); radio.setDataRate(RF24_250KBPS); // Скорость передачи radio.setChannel(100); // Номер канала от 0 до 127 radio.setRetries(15,15); // Кол-во попыток и время между попытками radio.openWritingPipe(pipes[1]); // Открываем канал передачи radio.openReadingPipe(1, pipes[0]); // Открываем один из 6-ти каналов приема radio.startListening(); // Начинаем слушать эфир /*** Setup the WDT ***/ MCUSR &= ~(1<<WDRF); WDTCSR |= (1<<WDCE) | (1<<WDE); WDTCSR = 1<<WDP0 | 1<<WDP3; /* 8.0 seconds */ WDTCSR |= _BV(WDIE); Serial.println("Initialisation complete."); delay(10); } /*************************************************** * Name: enterSleep * * Returns: Nothing. * * Parameters: None. * * Description: Main application loop. * ***************************************************/ void wakeup() { Serial.println("Wake end reset"); detachInterrupt(0); resetFunc(); } void loop(void) { int vb = getBandgap (); // записываем в переменную vb значение замеренного напряжения с батарии Serial.print("charge battery "); Serial.print(vb); Serial.println(" "); if(f_wdt == 1){ delay(10); if (digitalRead(buttonPin1) == HIGH){ r1=0; while (r1<2){ //количество пропуска основного цыкла. msg[0] = 1; radio.stopListening(); radio.write(msg, 1); radio.startListening(); delay(10); Serial.println(msg[0]); r1++; delay(10); } if (digitalRead(buttonPin2) == HIGH){ msg[0] = 2; radio.stopListening(); radio.write(msg, 1); radio.startListening(); Serial.println(msg[0]); delay(10); digitalWrite(led7, HIGH); delay(100); digitalWrite(led7, LOW); delay(100); digitalWrite(led7, HIGH); delay(100); digitalWrite(led7, LOW); delay(100); digitalWrite(led7, HIGH); delay(100); digitalWrite(led7, LOW); } if (digitalRead(buttonPin3) == HIGH){ msg[0] = 3; radio.stopListening(); radio.write(msg, 1); radio.startListening(); Serial.println(msg[0]); delay(10); digitalWrite(led7, HIGH); delay(1000); digitalWrite(led7, LOW); } digitalWrite(led8, LOW); // выключаем радио модуль Serial.println(msg[0]); delay(10); /* Don't forget to clear the flag. */ f_wdt = 0; Serial.println("PWR_DOWN");//идем спать delay(10); /* Re-enter sleep mode. */ //enterSleep(); i=0; while (i<4){//количество пропуска основного цыкла. f_wdt = 0; enterSleep(); delay(10); i++; delay(10); } Serial.println("PWR_UP");//Просыпаемся delay(10); Serial.println("resetting"); delay(10); resetFunc(); // перезагружаемся } else { Serial.println("Do nothing."); delay(10);/* Do nothing. */ } } } int getBandgap () { // REFS0 : Selects AVcc external reference // MUX3 MUX2 MUX1 : Selects 1.1V (VBG) ADMUX = _BV (REFS0) | _BV (MUX3) | _BV (MUX2) | _BV (MUX1); ADCSRA |= _BV( ADSC ); // start conversion while (ADCSRA & _BV (ADSC)) { } // wait for conversion to complete int results = (((InternalReferenceVoltage * 1024) / ADC) + 5) / 10; return results; } // end of getBandgap
P/S.
большое спасибо Dmitry OSIPOV
Вам интересен сам красивый код? У Вас не влазит на ардуину? Вы хотите замутить коммерческий проект?
В чем прикол оптимизации ради оптимизации?
хочу чтобы было грамотно, возможно чтото добавить
кое что изменил:
А можно мне чуток объяснить, этот блочок которые на фото, теперь нужно носить с собой?
а внутри авто получается лежит ардуино которая принимает и выполняет действия?
Этот блочок нужно носить с собой, а приемник делается по такой же схеме, только вместо кнопок и индикаторов подключается исполняющее устройство, так же требуется провести некоторые изменения в скетче Arduino.
Сейчас пытаюсь повторить Ваш проект, и вношу чуть измениний под себя и паралейно мысль не покидает...
а как же устроено в автомобилях с завода? видь там не блок и ключ с чипом,
удобней ключ, менее занимает...
Дело в том, что ключ машины имеет маленькие габариты из за сложности конструкции, идея такая же.
В некоторых используется тот же чип передачи как у меня, и батарейки используются мощнее..., может замечали, что батарейка в брелок идет на 12v 27A.
Ну это только в некоторых случаях.
нет, в руках не держал( незнаю...
а еще вопрос, есть ли защита от транслятора? (при момощи которого производят взлом)
Всё зависит от кода.
Нет такой защиты, которую нельзя взломать.
В абсолютных значениях такое утверждение верно. Однако, если защита не взламывается в разумные сроки или на ее взлом расходуются ресурсы превышающие стоимость взламываемого объекта, то такая защита может считаться условно-надежной.
Хорошей практикой было бы шифровать трафик, циркулирующий между разными частями системы безопасности, в условиях открытой среды. На сильные шифры у ардуины ресурсов не хватит, но и не сильно сложные алгоритмы порой вполне способны усложнить жизнь потенциальному злоумышленнику. Например, ксорить трафик последовательностью знаков после запятой небезызвестного числа Пи, начиная с некоего "отступа", выбираемого в сеансе установки связи.
Согласен, все зависит от алгоритма и кода.
Я собрал данное устройство как примитивный передатчик, который потребляет мало енергии и который в дальнейшем будет дорабатываться под конкретные нужды.
Ребят вы лучше знаете, помогите пожалуйста, можно ли вообще реализовать такое условие:
1. Если ключ в двух метрах от машины.
2. Если ключ в трех метрах от машины.
С какими параметрами игратся? я так понимаю, что можно как то указывать силу передачика и тем самым методом тыка установить мощьность на два метра.
Вот вам мануал, курите!
http://www.nordicsemi.com/eng/Products/2.4GHz-RF/nRF24L01P
> На сильные шифры у ардуины ресурсов не хватит
Ну не надо так категорично. Aes и sha1 я успешно использую.
На миниПро?
Ага
Сорри, я toc'a про шифровку спросил :)
Ардуиновские библиотеки для использования криптостойких алгоритмов есть и они вполне себе употребимы, с небольшими оговорками, но плата за это -- серьезный расход ресурсов МК. Высоких скоростей и производительности от ардуины на этой задаче ждать не приходится.
На миниПро?
на нано, мега2560 и на голой atmega328p-pu
Ардуиновские библиотеки для использования криптостойких алгоритмов есть и они вполне себе употребимы, с небольшими оговорками, но плата за это -- серьезный расход ресурсов МК. Высоких скоростей и производительности от ардуины на этой задаче ждать не приходится.
Сам задумался сделать подобный ключ в мастерскую, иногда подходишь к двери груженый .. нужно бросить все напол нарыть в кармане или в сумке связку ключей, хорошо если светло и видишь замочную скважину, а то бывает соседи выключат свет в коридоре и нужно еще до выключателя идти ....
Подумал о таком алгоритме.
Подхожу к двери , за дверью передатчик пускает посылки раз в секунду, как только в зоне появляется ключ, ключ откликается и шлет код, за дверью приемник ловит код и открывает дверь. Захожу раскладываю груз...... потом достаю ключ кидаю в ящик или на полку. На полке ИК (инфрокрасный передатчик/приемник и на ключе тоже передатчик/приемник, ИК передатчик настроен на расстояние максимум в один метр, т.е за дверь и за стены ИК ни как не попадает) в общем увидели они друг друга и "дверь" послала random() новый ключ на ключ, и записала его в EEPROM себе и на ключ.
Ну а если пропадает свет или все виснет, то уже придется доставать настоящий ключ.
Илья73, не хорошо засорять эфир постоянными посылками и небезопасно. По-моему, лучше так:
1. замок в основном молчит, только слушает эфир.
2. собираясь в мастерскую, включаете ключ
...
Около трехста пятидесяти байт в секунду может и хватить для каких-то приложений, но, в общем, скорость не ахти.
Замок шлет сиды. Эфир такое надругательство вполне вынесет ввиду крайне низкой мощности передатчика. Безопасность тут тоже никак пострадать не может, т.к. сиды это просто случайные цифры.
А приемник постоянно слушае, и жрет акумулятор...
По правильному стоит проверить что энергетически выгодней, что бы брелок постоянно эфир слушал и откликался на замок (т.е. приемник всегда что то потребляет, а МК спит) или брелок раз в 1-5 сек отправляет запрос в эфир, а остальное время спит (и МК и радио)...
Нет, не хватит моего ума на такое решение. Стал задумываться глубже, стали появляться вопросы все больше и больше. В течении дня бывает несколько раз выйти / зайти нужно, бывает подойду к двери а тут сосед на встречу зовет зайти в гости ....
В общем пока буду делать на кнопке открыть/закрыть. А ключ буду перезаписывать по ИК перед уходом из мастерской .
Чет я не пойму, зачем каждый раз ключ переписывать? Или ключом вы называете одноразовый пин для отпирания замка?
Да, каждый раз записывать новое число на ключ, хотя можно и не записывать, но появляются риски.
Это совершенное неудобно и первый, кто откажется от использования такого ключа, это его владелец. :)
Ну владелец наверно буду только я :) Согласен что не удобно, но стараюсь это неудобство минимизировать. Положить ключ напротив передатчика и что бы перезапись прошла автоматически по ИК. Пока в голову ни чего проще не приходит, а если приходит то на реализацию мозгов не хватает.
Пока на макетке собрал ИК передатчик/приемник погонял числа друг на друга, работает на расстоянии 5 см, если включить свет (лампу) на конструкцией то расстояние падает до 2 см, считаю вполне приемлемым.
a5021 , если есть альтернативные предложения то с радостью выслушаю. Только могу коечего не понять, так как сам любитель.
Реализаций шфрования "для бедных" может быть великое множество, и, как вариант, можно рассмотреть использование встроенного генератора псевдослучайных чисел для этих целей. Стандартная ардуиновская random() при каждом вызове возвращает случайное число, и вызвав ее десять раз подряд мы получим десять разных значений, которые хоть и будут выглядеть полностью случайными, но это только на первый взгляд. Если теперь нажать на ардуине кнопку резета, то перестартовавший скетч сгенерит эти десять чисел в точно такой же последовательности, как и в первый раз. Чтобы изменить вид этой последовательности следует задать начальное значение генератора случайных чисел посредством функции randomSeed(). При разных сидах (т.е. начальных значениях генератора) последовательности значений возвращаемых функцией random() будут тоже разными. Но ценность этой псевдо-случайности для нас в том, что задав одинаковый сид на приемнике и передатчике мы получим идентичные ключи шифрования (причем любой длины вплоть до бесконечной) на обоих устройствах. Рассмотрим схематично простейший алгоритм передачи шифрованной строки между передатчиком и приемником:
ардуино-замок: сочиняет случайный сид, который предполагается использовать обоими сторонами для инициализации randomSeed(), выдает его в эфир и ждет ответа;
seed = random(analogRead(0));
transmit(seed);
ардуино-ключ: принимает сид, инициализирует им генератор, потом последовательностью с random() ксорит сообщение и шлет замку.
seed = receive();
randomSeed(seed);
transmit(msg ^ random());
ардуино-замок: принимает шифрованное сообщение и тем сидом, который передавался в последний раз, опять инициализирует генератор случайных чисел и последовательностью с random() расксоривает сообщение до исходного вида и проверяет то ли это кодовое сообщение, по которому надо открывать дверь.
randomSeed(seed);
msg = receive();
msg = msg ^ random();
Тут еще есть смысл обратиться к "Security by obscurity", тобишь, начать темнить и гнать в эфир всякую пургу обеими сторонами вперемешку с реальными данными. Сделать все передачи в эфир переменной длины, где часть несет осмысленные данные, а остальное сплошной рандом.
Одновременно в эту пургу хорошо бы заложить повторяющийся в каждом сообщении совершенно левый кусок осмысленной информации , который легко ломается с использованием того же частотного метода (типа "ENTER ACCESS CODE:"). Тогда в замке можно будет слежение учинить, когда пионэры им начнут дверь ломать. :)
Вобщем, я тут себе экспромт позволил и выстругал почти что топором из замшелого пня некий скетч, где, как смог, попытался проиллюстрировать основную идею.
Вот такое оно в консоль отладки выдает. Посмотрел, вроде получается, как и замысливалось.
a5021, Спасибо!
Для меня, конечно это все темный лес. Прочитал несколько раз, с третьего раза начал доходить до меня алгоритм работы.
Потом загрузил скетч, скетч сразу не пошел в мониторе кубики разные посыпались, сменил скорость на 9200 все пошло как надо. Хорошо что в скетче сделали подробное описание, отчасти понятно (тут понимаю, тут не понимаю) Но все равно есть вопросы, сформулировать их толком пока не могу.
Сейчас нужно будет разобраться и написать отдельно скетч для замка и ключа, хотя бы под ту модель которая у меня на макетке (ИК), и погонять их вместе, зажигать диод. Пока в пути модули nrf24l01. По ходу буду задавать вопросы, если не против. Сейчас стоит задача разделить ваш скетч для замка/ключа.
К сожалению не хватает меня как это запсать для замка и для ключа :(
Согласен, написано довольно сумбурно. Если это вам действительно нужно, могу попозже раскидать все на два скетча, отдельно для замка и ключа.
Да конечно буду, даже если с вашим кодом не судьба, буду придумывать что-нибудь. Вчера даже замок для этого дела прикупил.
Но чур тогда рассказать, что из этого в конце концов получится. :)
Ага :)
Тогда вот пока код замка
Так прогнать код я на реальном устройстве не могу, за 100% отсутствие ошибок не ручаюсь. Однако, гарантирую, что компиляция проходит и алгоритм в целом верен. Очень хорошо у меня получаются ошибки выхода за диапазон, когда в цикле перебираю символы, т.ч. этот момент вам придется проверить особо. Я могу либо один символ недосчитать, либо посчитать лишний. Так как ленив сверх меры, то не имею привычки подсчитывать точное число в момент написания программы, а вылавливаю выходы за границы уже на отладке. Дурная привычка, так сказать.
Код ключа напишу позже.
Я вот про ключ подумал. Наверно всетаки по кнопке пусть запускается, а все остальное время пусть спит для экономии батарейки так сказать.
Насчет батарейки на ключе я тоже подумал, что атмегу сложно назвать энергоэффективным МК и жрать оно ее будет от пуза. Даже активное использование режимов малого потребления тут не особо скажется. Но, в принципе, общую концепцию это не меняет. Единственно, что придется выключать ключ совсем, если использование его в ближайшее время не планируется. Для замка в этом смысле ничего не меняется, т.к. он по идее должен быть на стационарном питании.
А может вообще не делать ей сон. Кнопку нажал - зашунтировал ей питание атмеги (полевой транзистор) - отмега в самом начале старта скетча выдала HIGH на ногу на полевик - полевик зашунтировал кнопку - кнопку можно отпустить, выполнилась программа и в конце дала LOW на полевика - все отрубилось. Вопрос сколько времени нужно держать кнопку пока не старует HIGH.
Для коммутации питания луче использовать P-канальные приборы, соответственно, уровни наоборот будут. Стартует атмега быстро (если только там в загрузчике чего ни перемудрили). Да и полевик не закроет канал сразу после снятия воздействующего на затвор уровня, если конечно не притянут жестоко низкоомным резистором. В принципе, мне такая ваша идея видится здравой.
Можно не инвертировать выход, добавиь транзистор.
Тогда уж и базу ему через 10к на точку, где знак VCC нарисован. Будет защелкиваться и фиг с ним, сколько атмега стартовать будет.
Хотелось бы один вопрос разъяснить.
Есть библиотека AESlib. На сколько я понял её в данном случае напрямую использовать нельзя, потому как нам нужно знать не (секретный код) который откроет замок, а достаточно узнать ту зашифрованную кракозябру которая летит в эфир к замку, отловить её и потом воспроизвести для взлома замка. Или я не прав?
Именно так. Если ключ все время будет слать замку одну и ту же последовательность, будь она хоть стотыщьмильенов раз зашифрованная, то однажды перехватив ее, потом можно просто скармливать замку и открывать дверь, не переживая, что там она на самом деле содержит. То есть, ни тип шифрования, ни криптостойкость здесь уже ни на что не влияют. В предлагаемом мной варианте, содержимое радиообмена каждый раз разное и открыть дверь возможно только правильно ответив на запрос замка. Даже для такой простой схемы аутентификации, число разных вариантов ответа равняется 65536-и. Если предположить, что дверь будет открываться десять раз в сутки, а передаваемые коды старательно перехватываться злоумышленником, то последний получит все варианты пар "запрос замка - ответ ключа" не ранее, чем через 18 лет. Если же менять ключ шифрования раз в несколько лет, то злоумышленник лишится возможности узнать все варианты правильных сочетаний в принципе.
Спасибо, теперь понятно.
a5021, все-таки не теряю надежду на код для ключа :) Сам я такое не осилю :(
Да напишу и ключ, только время найду. Давайте я вкратце опишу алгоритм ключа, чтобы вам было понятнее потом код рассматривать.
Итак, ключ слушает эфир. Как только он узреет осмысленную передачу, он сложит все принятое в буфер и дальше предпримет следующие шаги по пунктам:
1) Вычленит из длинной последовательности принятых байт два значения: значение сида для инициализации генератора псевдо-случайных чисел и значение числа отброшенных результатов, возвращаемых функцией random() перед началом шифрования.
2) Проинициализирует генератор псеводо-случайных чисел полученным значением.
3) Тупо в цикле вызовет функцию random() точное число раз, в соответствии с величиной полученной от замка, не сохраняя полученные значения. Смысл этой операции в том, чтобы двигать генератор случайных чисел по последовательности генерируемых им значений до какой-то конкретной точки.
4) Шифрование кодовой фразы происходит с использованием ключа шифрования, котороый одинаков у замка и ключа. Шифрование это операция исключающего "ИЛИ" между байтом кодовой фразы и байтом ключа шифрования, которую на жаргоне называют ксор или ксора (от английского названия операции -- XOR). Сущность операции исключающего или заключается в том, что происходит побитовое сравнение двух двоичных чисел и если значения битов в сравниваемом разряде совпадают, то в этот же разряд результата заносится ноль, если биты различаются, то заносится единица. Для примера, можно посмотреть, что получится, если применить операцию исключающего или между символами "A" и "B" (латинские). Двоичные коды обеих симоволов и результат ксоры таковы:
"A" - 100 0001 (65 в десятичном представлении)
"B" - 100 0010 (66 в десятичном представлении)
Результат - 0000011 (3 в десятичном представлении)
Операция XOR обратимая. Т.е. имея результат и одно из исходных значений, можно всегда восстановить и второе исходное значение, если проксорить имеющиеся данные еще раз. Наш замок, имея "в руках" полученный из эфира шифр и сохраненный ключ шифрования, без всяких проблем восстановит передаваемую информацию без каких либо искажений. Представим, что пароль длиной n лежит у нас в массиве a[], ключ шифрования в массиве b[], а результат мы хотим поместить в массив c[]. В этом случае код шифрования (а равно и код проводящий обратную процедуру) будет выглядеть так:
Этот нехитрый алгоритм, тем не менее, имеет некоторую устойчивость ко взлому, если злоумышленник не владеет ключом. Однако, если не предпринять дополнительных мер, то закодированная последовательность будет иметь всегда один и тот же вид и передавая ее в эфир мы просто вкладываем в руки злоумышленнику отмычку от нашего замка. Чтобы этого не произошло, в данном алгоритме стоит выбирать символы ключа шифрования не последовательно, а псевдо-случайным образом. Именно для этого и потребовались шаги 2) и 3). Если принять длину ключа шифрования за x, то шифрование станет выглядеть так:
Результат выполнения функция random(0, х) задает индекс ключа шифрования. Так как генератор случайных чисел у нас на самом деле является генератором псевдо-случайных чисел, то замок будет в состоянии выбирать те же самые индексы при расшифровке просто проинициализировав генератор случайных чисел тем же самым способом, что и ключ. Порядок следования индексов, в этом случае, сам по себе является еще одним элементом алгоритма шифрования. В этом смысле, даже если злоумышленник каким-то образом украдет исходный код вместе с ключом шифрования (подразумевается, что код не содержит исходного пароля), то не зная последовательности использования индексов ключа шифрования не сможет ни расшифровать сообщение из эфира, ни подделать его.
5) Добавит к полученным после шифрования данным случайное число мусорных байт. Замок знает длину пароля и его это не собьет с толку, а вот всем остальным ее знать не положено.
Теперь ключу только остается передать получившуюся строку в эфир, чтобы считать свою работу выполненной.
вариант http://arduino.ru/forum/programmirovanie/rfid-klyuchi-lan-set#comment-35754
Как-то мне не показалось, что предложенный вами вариант проще.