Русский
Русский
English
Статистика
Реклама

Домофон

Сим-сим откройся как я научил дверь своего подъезда узнавать меня в лицо

07.06.2021 18:08:54 | Автор: admin

Пятничный рабочий день на удалёнке уже подходил к концу, как в дверь постучали, чтобы сообщить об установке нового домофона. Узнав, что новый домофон имеет мобильное приложение, позволяющее отвечать на звонки не находясь дома, я заинтересовался и сразу же загрузил его на свой телефон. Залогинившись, я обнаружил интересную особенность этого приложения даже без активного вызова в мою квартиру я мог смотреть в камеру домофона и открывать дверь в произвольный момент времени. "Да это же онлайн АРI к двери подъезда!" щёлкнуло в голове. Судьба предстоящих выходных была предрешена.

Видеодемонстрация в конце статьи.

Кадр из фильма Пятый ЭлементКадр из фильма Пятый Элемент

Дисклеймер

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

Получение API

Чтобы автоматизировать управление дверью, необходимо понять, куда и в каком формате отправляет запросы само приложение. Реверс-инжиниринг дело неоднозначное, поэтому я попытался обойтись без него и вместо этого просто перехватить свой собственный трафик. Для этой задачи я взял НТТР Тооlkit комплекс программ, который позволяет наладить прослушивание http(s) запросов собственного Android устройства.

Первая попытка оказывается провальной после установки на телефон Android-части инструментария и сгенерированного Certificate authority оказалось, что мобильное приложение домофона не доверяет пользовательским СА. Согласно документации, начиная с Android 7 манифест приложения должен явно изъявлять такое желание.

Так как мой телефон не поддерживает root доступ для модификации списка системных СА, я воспользовался официальным эмулятором Android, идущим в комплекте с Android Studio. После запуска эмулятора и перехвата с помощью ADB меня встретило радостное сообщение о том, что трафик от всех приложений без Certificate pinning будет успешно расшифрован.

Успешное соединение с HTTP ToolkitУспешное соединение с HTTP Toolkit

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

Запрос открытия двериЗапрос открытия двери

Всего интересными показалось три запроса:

  1. Запрос на открытие двери подъезда: POST по адресу /rest/v1/places/{place_id}/accesscontrols/{control_id}/actions с JSON-телом {"name": "accessControlOpen"}

  2. Получение снимка (превью) с камеры: GET по адресу /rest/v1/places/{place_id}/accesscontrols/{control_id}/videosnapshots

  3. Получение ссылки на видеопоток с аудио: GET по адресу /rest/v1/forpost/cameras/{camera_id}/video?LightStream=0

HTTP заголовки всех трёх запросов содержат ключ Authorization судя по всему, именно по нему происходит авторизация при выполнении запросов. Сделав пару запросов руками через Advanced REST Client, чтобы убедиться, что заголовка Authorization достаточно и в самостоятельной работе с API не осталось подводных камней, я понял, что можно откладывать эмулятор и переходить к написанию кода.

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

HEADERS = {"Authorization": "Bearer ###"}ACTION_URL = "https://###.ru/rest/v1/places/###/accesscontrols/###/"VIDEO_URL = "https://###.ru/rest/v1/forpost/cameras/###/video?LightStream=0"def get_image():    result = requests.get(f'{ACTION_URL}/videosnapshots', headers=HEADERS)    if result.status_code != 200:        logging.error(f"Failed to get an image with status code {result.status_code}")        return None    logging.warning(f"Image received successfully in {result.elapsed.total_seconds()}sec")    return result.contentdef open_door():    result = requests.post(        f'{ACTION_URL}/actions', headers=HEADERS, json={"name": "accessControlOpen"})    if result.status_code != 200:        logging.error(f"Failed to open the door with status code {result.status_code}")        return False    logging.warning(f"Door opened successfully in {result.elapsed.total_seconds()}sec")    return Truedef get_videostream_link():    result = requests.get(VIDEO_URL, headers=HEADERS)    if result.status_code != 200:        logging.error(f"Failed to get stream link with status code {result.status_code}")        return False    logging.warning(f"Stream link received successfully in {result.elapsed.total_seconds()}sec")    return result.json()['data']['URL']

Поиск знакомых лиц в кадре

Прежде чем начать этот раздел, нужно рассказать пару слов об уже имеющихся на тот момент у меня в распоряжении серверных мощностях это недорогая виртуальная машина с доступом ко всего одному потоку Intel(R) Xeon(R) CPU E5-2650L v3 @ 1.80GHz, 1GB оперативной памяти и 0 GPU. Тратиться на более дорогую конфигурацию не хотелось, а значит, нужно было попробовать выжать максимум из имеющихся ресурсов.

На данном этапе проектом заинтересовалась моя жена, хорошо разбирающаяся в машинном обучении и в итоге взявшая на себя эту задачу. Для этой работы был выбран OpenVINO Toolkit инструментарий от Intel, в том числе заточенный как раз на запуск моделей машинного обучения на CPU.

Непродолжительный поиск существующих решений привёл на страницу Interactive Face Recognition Demo официального демо, показывающего ровно необходимый функционал сравнения видимых в кадре лиц с базой заранее сохранённых. Единственная проблема состояла в том, что данный пример по каким-то причинам исчез после релиза 2020.3, а удобная установка пакета через pip у проекта появилась только с 2021.1. Было решено установить последнюю версию OpenVINO и адаптировать код под неё.

К счастью, гит помнит всё и получить из репозитория проекта код демо и нужные модели не составило труда. После удаления всей работы с командной строкой (взяв для всего значения по умолчанию), визуализацией и видеопотоком вебкамеры, был получен класс, способный распознавать лица в конкретном кадре:

class ImageProcessor:    def __init__(self):        self.frame_processor = FrameProcessor()    def process(self, image):        detections = self.frame_processor.process(image)        labels = []        for roi, landmarks, identity in zip(*detections):            label = self.frame_processor.face_identifier.get_identity_label(                identity.id)            labels.append(label)        return labels

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

100 runs on an image with known face:Total time: 7.356sTime per frame: 0.007sFPS: 135.944100 runs on an image without faces:Total time: 2.985sTime per frame: 0.003sFPS: 334.962

Очевидно, что показанная производительность с запасом покрывает нужды детектирования лиц в реальном времени.

1 FPS: Работа со снимками с камеры

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

class ImageProcessor:# <...>    def process_single_image(self, image):        nparr = np.fromstring(image, np.uint8)        img_np = cv2.imdecode(nparr, cv2.IMREAD_COLOR)        labels = self.process(img_np)        return labelsdef snapshot_based_intercom_id():    processor = ImageProcessor()    last_open_door_time = time.time()    while True:        start_time = time.time()        image = get_image()        result = processor.process_single_image(image)        logging.info(f'{result} in {time.time() - start_time}s')        # Successfull detections are "face{N}"        if any(['face' in res for res in result]):            if start_time - last_open_door_time > 5:                open_door()                with open(f'images/{start_time}_OK.jfif', 'wb') as f:                    f.write(image)                last_open_door_time = start_time

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

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

Заработало! Полностью довольный первым запуском, я вернулся в квартиру. Единственное, что портило впечатление от системы распознавания время реакции на появление лица в кадре, т.к. время отклика API оставляло желать лучшего. Низкая частота поступления данных, 0.7с на получение картинки и 0.6с на открытие двери, давали видимый невооружённым взглядом лаг.

До 30 FPS: Обработка видеопотока

Получить кадры из видео оказалось на удивление просто:

vcap = cv2.VideoCapture(link)success, frame = vcap.read()

Замеры показали, что камера домофона способна выдавать стабильные 30 FPS. Проблемой оказалось поддержание меньшей частоты кадров: метод read() возвращает последний необработанный кадр из внутренней очереди кадров. Если эта очередь наполняется видеопотоком быстрее, чем вычитывается, то обрабатываемые кадры начинают отставать от реального времени, что приводит к нежелательной задержке. Дополнительно, с практической точки зрения, распознавать лица 30 раз в секунду пустая трата вычислительных ресурсов, так как обычно люди подходят к двери подъезда с небольшой скоростью.

Первым потенциальным решением было установить размер внутренней очереди: vcap.set(CV_CAP_PROP_BUFFERSIZE, 0);. Согласно найденной информации, такой трюк должен был хорошо работать с любой конфигурацией системы для версий OpenCV выше 3.4, но по какой-то причине, так и не оказал никакого влияния в моём случае. Единственной рабочей альтернативой стал подход, описанный в этом ответе со StackOverflow завести отдельный поток, читающий кадры из камеры на максимально возможной скорости и сохраняющий последний в поле класса для дальнейшего доступа (впоследствии оказалось, что именно этот цикл ответственен за большую часть потребления процессора).

Получилась модификация ImageProcessor для обработки видеопотока с частотой 3 кадра в секунду:

class CameraBufferCleanerThread(threading.Thread):    def __init__(self, camera, name='camera-buffer-cleaner-thread'):        self.camera = camera        self.last_frame = None        self.finished = False        super(CameraBufferCleanerThread, self).__init__(name=name)        self.start()    def run(self):        while not self.finished:            ret, self.last_frame = self.camera.read()    def __enter__(self): return self    def __exit__(self, type, value, traceback):        self.finished = True        self.join()class ImageProcessor:# <...>    def process_stream(self, link):        vcap = cv2.VideoCapture(link)        interval = 0.3 # ~3 FPS        with CameraBufferCleanerThread(vcap) as cam_cleaner:            while True:                frame = cam_cleaner.last_frame                if frame is not None:                    yield (self.process(frame), frame)                else:                    yield (None, None)                time.sleep(interval)

И соответствующая модификация snapshot_based_intercom_id:

def stream_based_intercom_id():    processor = ImageProcessor()    link = get_videostream_link()    # To notify about delays    last_time = time.time()    last_open_door_time = time.time()    for result, np_image in processor.process_stream(link):        current_time = time.time()        delta_time = current_time - last_time        if delta_time < 1:            logging.info(f'{result} in {delta_time}')        else:            logging.warning(f'{result} in {delta_time}')        last_time = current_time        if result is None:            continue        if any(['face' in res for res in result]):            if current_time - last_open_door_time > 5:                logging.warning(                  f'Hey, I know you - {result[0]}! Opening the door...')                last_open_door_time = current_time                open_door()                cv2.imwrite(f'images/{current_time}_OK.jpg', np_image)

Тест в реальных условиях показал ощутимое падение времени отклика при хорошем освещении подъездная дверь оказывалась открытой ещё до того, как я успевал подойти к ней вплотную.

Момент успешного распознавания, версия с обработкой видеопотокаМомент успешного распознавания, версия с обработкой видеопотока

Управление с помощью Telegram бота

Сама система доказала свою работоспособность и теперь хотелось создать для неё удобный интерфейс для включения/выключения. Телеграм бот показался отличным вариантом решения для этой задачи.

С помощью пакета python-telegram-bot была написана простая оболочка, принимающая в себя callback включения/выключения системы и список доверенных никнеймов.

class TelegramInterface:    def __init__(self, login_whitelist, state_callback):        self.state_callback = state_callback        self.login_whitelist = login_whitelist        self.updater = Updater(            token = "###", use_context = True)        self.run()    def run(self):        dispatcher = self.updater.dispatcher        dispatcher.add_handler(CommandHandler("start", self.start))        dispatcher.add_handler(CommandHandler("run", self.run_intercom))        dispatcher.add_handler(CommandHandler("stop", self.stop_intercom))        self.updater.start_polling()    def run_intercom(self, update: Update, context: CallbackContext):        user = update.message.from_user        update.message.reply_text(            self.state_callback(True) if user.username in self.login_whitelist else 'not allowed',            reply_to_message_id=update.message.message_id)    def stop_intercom(self, update: Update, context: CallbackContext):        user = update.message.from_user        update.message.reply_text(            self.state_callback(False) if user.username in self.login_whitelist else 'not allowed',            reply_to_message_id=update.message.message_id)    def start(self, update: Update, context: CallbackContext) -> None:        update.message.reply_text('Hi!')                class TelegramBotThreadWrapper(threading.Thread):    def __init__(self, state_callback, name='telegram-bot-wrapper'):        self.whitelist = ["###", "###"]        self.state_callback = state_callback        super(TelegramBotThreadWrapper, self).__init__(name=name)        self.start()    def run(self):        self.bot = TelegramInterface(self.whitelist, self.state_callback)

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

def stream_based_intercom_id_with_telegram():    processor = ImageProcessor()    loop_state_lock = threading.Lock()    loop_should_run = False    loop_should_change_state_cv = threading.Condition(loop_state_lock)    is_loop_finished = True    loop_changed_state_cv = threading.Condition(loop_state_lock)    def stream_processing_loop():        nonlocal loop_should_run        nonlocal loop_should_change_state_cv        nonlocal is_loop_finished        nonlocal loop_changed_state_cv        while True:            with loop_should_change_state_cv:                loop_should_change_state_cv.wait_for(lambda: loop_should_run)                is_loop_finished = False                loop_changed_state_cv.notify_all()                logging.warning(f'Loop is started')            link = get_videostream_link()            last_time = time.time()            last_open_door_time = time.time()            for result, np_image in processor.process_stream(link):                with loop_should_change_state_cv:                    if not loop_should_run:                        is_loop_finished = True                        loop_changed_state_cv.notify_all()                        logging.warning(f'Loop is stopped')                        break                current_time = time.time()                delta_time = current_time - last_time                if delta_time < 1:                    logging.info(f'{result} in {delta_time}')                else:                    logging.warning(f'{result} in {delta_time}')                last_time = current_time                if result is None:                    continue                if any(['face' in res for res in result]):                    if current_time - last_open_door_time > 5:                        logging.warning(f'Hey, I know you - {result[0]}! Opening the door...')                        last_open_door_time = current_time                        open_door()                        cv2.imwrite(f'images/{current_time}_OK.jpg', np_image)    def state_callback(is_running):        nonlocal loop_should_run        nonlocal loop_should_change_state_cv        nonlocal is_loop_finished        nonlocal loop_changed_state_cv        with loop_should_change_state_cv:            if is_running == loop_should_run:                return "Intercom service state is not changed"            loop_should_run = is_running            if loop_should_run:                loop_should_change_state_cv.notify_all()                loop_changed_state_cv.wait_for(lambda: not is_loop_finished)                return "Intercom service is up"            else:                loop_changed_state_cv.wait_for(lambda: is_loop_finished)                return "Intercom service is down"    telegram_bot = TelegramBotThreadWrapper(state_callback)    logging.warning("Bot is ready")    stream_processing_loop()

Результат

Видео:

Послесловие

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

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

Подробнее..

Нахлобучиваем домофонные ключи iButton с помощью Flipper Zero

09.06.2021 22:15:01 | Автор: admin


Flipper Zero проект карманного мультитула для хакеров в формфакторе тамагочи, который мы разрабатываем. Предыдущие посты [1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14]

iButton это общее название для формата электронного ключа в форм-факторе металлической таблетки. Еще его называют Dallas Touch Memory. Часто его ошибочно называют магнитным ключом, но это неправильно, ничего магнитного в нем нет. Внутри iButton полноценный микрочип, работающий по цифровому протоколу.
В статье разберемся что такое формат ключей iButton от физического устройства до протоколов, а также трюки, которые можно с ним делать при помощи Flipper Zero.

Что такое iButton


Название iButton это продукт фирмы Dallas Semiconductor, в 1991 году выпустившей на рынок ключ под торговой маркой Touch Memory, потом замененной на iButton.


Схематическое устройство ключа iButton: в центре корпуса контакт плюс, потом пластиковая изоляция, и внешняя часть корпуса это минус

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

Внутреннее устройство iButton: внутри металлической оболочки находится микрочип

Считыватель


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

Касание контактов iButton ключа с домофонным считывателем

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

Как устроен iButton во Flipper Zero


Разработка контактной площадки iButton во Flipper Zero это огромная боль. Дело в том, что не существует готовых контактных площадок для одновременного чтения и эмуляции. Если бы мы просто взяли существующие модули, из флиппера бы торчала огромная металлическая кнопка, и рядом такая же огромная площадка для считывания. Это бы многократно увеличило размер устройства.

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


Прототипы конструкций контактной площадки iButton во Flipper Zero, которые мы печатали на 3D-принтере в процессе разработки

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

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

Финальная конструкция получилась компромиссной: 100% ключей считывается успешно, примерно 80% считывателей успешно работают с эмуляцией. В оставшихся 20% считывателей приходится корячиться, чтобы контакт достал до нужных стенок. Для этих редких случаев можно использовать внешние контакты GPIO, на которые выведены контакты ibutton: подключиться макетными проводами и ткнуть их в считыватель.

Режим считывателя


В режиме считывателя флиппер ожидает поднесения ключа, при этом готов прожевать сразу три типа ключей: Dallas, Cyfral, Metakom. Флиппер сам определит тип ключа при чтении. Название протокола ключа отобразится на экране над ID номером.

Чтение ключа ibutton формата Dallas. Прочитанный ключ сохраняется на SD-карту.

Для считывания ключа необходимо зайти в меню iButton > Read и приложить читаемый к контактной площадке. Считанный ключ можно сразу эмулировать, записать на болванку, либо сохранить на SD-карту. Хоть контактная площадка находится на задней стороне от экрана, можно быстро наловчиться читать ключи не разворачивая флиппер, просто на ощупь.

В режиме чтения iButton используются два правых контакта Flipper Zero

Режим эмуляции iButton


В режиме эмуляции ключа, Флиппер сам выступает ключом и программно эмулирует ключ iButton из памяти. ID ключа для эмуляции во Flipper Zero можно добавить двумя способами:

  • Считать существующий ключ сохранить ID ключа на SD-карту и выбирать нужный ключ в любой момент
  • Вручную ввести ID ключа даже если в руках нет нужного ключа, но его ID известен, его можно ввести вручную. Так, например, можно сфотографировать ID ключа и отправить его другу с флиппером, без необходимости передавать физический ключ


Для запуска эмуляции ключа нужно зайти в меню iButton > Saved, выбрать нужный ключ и запустить Emulate. На экране появится надпись с ID ключа, который эмулируется. После этого можно подносить Флиппер к считывателю. Важно помнить, что в этом режиме используются другие пины на контактной площадке Флиппера.

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

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

iButton через внешний GPIO


Контакт iButton на нижней крышке также выведен на гребенку GPIO. Это можно пользовать для подключения к нестандартным считывателям, ключам, любым устройствам работающим по протоколу 1-Wire вроде датчиков. Мы используем эти контакты для анализа сигналов через осциллограф. При этом, этот контакт не совсем честный GPIO, потому что имеет подтяжу к 5V.


Контакты iButton соединены с гребенкой GPIO. Порт iButton работает в режиме open-drain и подтянут к напряжению 5 В через резистор 1 кОм.

Протокол 1-Wire



В протоколе 1-Wire всегда есть главное устройство Master и ведомые Slave

Ключи Dallas обмениваются данными по протоколу 1-wire. Всего один контакт на передачу данных(!!) в обе стороны, от мастера к слейву и наоборот. Протокол 1-wire работает по модели Master-Slave. В этой топологии устройство Master всегда инициирует общение, а Slave следует его указаниям.

При контакте ключа (Slave) с домофоном (Master) чип внутри ключа включается, получив питание от домофона и происходит инициализация ключа, после чего домофон запрашивает ID ключа. Далее мы разберем подробно этот процесс.

Флиппер умеет работать в режимах Master и Slave. В режиме чтения ключа Флиппер выступает в роли считывателя, то есть работает как Master. А в режиме эмуляции ключа, флиппер прикидывается ключом, то есть работает в режиме Slave.


При чтении ключа Флиппер выступает мастером, а при эмуляции с домофоном работает как slave

Формат данных в ключе Dallas



Домофон считывает из iButton 8 байт (64 бита) информации, чтобы решить, открывать дверь или нет.

Структура данных этих 8 байт следующая:

  • 1 байт код семейства (Family Code), для iButton он всегда равен 0x01
  • 6 байт серийный номер ключа
  • 1 байт контрольная сумма СRC




Код семейства у ключей Dallas всегда 0x01. Если у вас этот код отличается, то скорее всего, это не ключ от домофона.

Серийный номер в некоторых случаях выгравирован на ключе, но может:

  • Не содержать все 8 байт
  • Иметь последовательность символов задом-наперед
  • Иметь начало в непонятном месте


На оригинальном ключе iButton выгравирован ID, но его формат записи немного отличается от представления Флиппере: сперва идет family code, потом инвертированный серийный номер, потом контрольная сумма

На картинке выше показан неочевидный пример гравировки ID на оригинальном ключе iButton. В нем читать байты нужно справа налево, контрольная сумма написана слева, а family code справа.

Ошибки чтения



При некорректном чтении ключа Flipper Zero сообщает об ошибках. Возможные ошибки:
  • Некорректная контрольная сумма ошибка в CRC
  • Неправильный код семейства когда family code отключается от 0x01, Флиппер ругается что это не ключ iButton.


Возможные ошибки при чтении ключей Dallas: неправильный байт CRC CRC ERROR; байт Family-code не равен 0x01 THIS IS NOT A KEY.

Ввод ID вручную


Если ID ключа известен, его можно ввести во Флиппер вручную. Это удобно когда самого физического ключа нет, например можно передать нужны байты просто в чате или скинув другу фото. На видео показан пример создания нового ключа Cyfral из 2 байт. Новый сгенерированный ключ сохраняется на SD-карту.


Создание нового ключа вручную с помощью ручного ввода айдишника

При создании ключа нужно выбрать его тип: Dallas, Cyfral или Metakom. От этого будет зависеть длина ID и протокол, используемый при эмуляции. После ввода ID Флиппер предложит ввести имя нового ключа, либо использовать сгенерированное.

Запись ключей 1-Wire Dallas


Существуют ключи Dallas, которые можно записать и которые нельзя. Популярные перезаписываемые iButton болванки: RW1990, TM2004, TM01C. Процесс записи имеет свои нюансы, разберем их.

Запись болванки может требовать повышенного напряжения например, для записи менее популярной RW2000 требуется напряжение 8 В (правда это Cyfral, но смысл понятен).

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

Существуют болванки, которые могут подходить ко всем типам ключей(Dallas/Cyfral/Metakom).

Запишем ключ домофона на болванку.



Чтобы записать ключ нужно из раздела iButton зайти в Saved и выбрать ключ. После чего перейти в раздел Write и прислонить перезаписываемый ключ к контактной площадке.

Русские народные ключи Cyfral, Metakom




Протоколы Metakom и Cyfral отечественные русские изобретения. Не популярны в мире.

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

Cyfral и Metakom не принимают никакие команды. При подаче питания на ключ, он сразу начинает бесконечно посылать ID за счет изменения сопротивления. Таким образом, логические уровни определяются по сопротивлению ключа. По документации ключей условно принимается, что информационные слова кода выдаются начиная с младшего бита.
В интернете есть документация на данные ключи;)

Cyfral




Логические уровни в Cyfral, так же как и в Dallas имеют временные ограничения: если сопротивление остается низким около 50 мкс это логический 0, если 100 мкс это логическая 1.

Формат передаваемых данных специфичен.
Cyfral циклично отправляет 9 нибблов (1 ниббл = 4 бита): 1 стартовый и 8 ID. Нибл может иметь всего 4 значения для ID и одно значение для стартового слова. Все остальные записи некошерные.



В итоге ID записывается в 2 байта (Всего 8 нибблов ID. 4 ниббла = 16 состояний = 1 байт информации).

Бывает, что домофоны Cyfral занимаются проверкой ключа, подавая некорректные данные.
У Cyfral нет никаких контрольных сумм. Хочешь убедиться прочитай ключ еще раз, а хочешь пять. Так как сигнал аналоговый, то чтение сигнала требует наличия АЦП или компаратора. Проще всего использовать компаратор, выход которого является низким или высоким уровнем напряжения.
С эмулированием ключа на практике проблем не возникает. Замыкания на землю (отсутствие сопротивления) вполне достаточно, чтобы ключ выдать логический 0.

Metakom




Ключ Metakom посылает 4 байта, где каждый байт заканчивается битом четности.
Metakom имеет 3 примитива передачи:
Синхронизирующий бит;
Бит 0;
Бит 1;

Структура посылки выглядит так:
Синхронизирующий бит;
4 байта информации;
+ 7 бит данных;
+ 1 бит четности.

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

Смотрим на 1-Wire через осциллограф




Линия передачи устроена по принципу монтажного И и может иметь одно из двух состояний: логический 0 и логическая 1.
Устройства (ключи и домофон) имеют внутренние транзисторы, которые в нужное время подтягивают линию к нулю. Вся линия передачи переходит в состояние логического 0, если любое из устройств перевело ее в нуль, т.е. если домофон перетянул линию в нуль ключ об этом узнает, и наоборот.

С помощью перетяжек напряжения и удержания уровней 1-wire имеет 4 примитива для работы на шине:
  • импульс сброса (RESET);
  • импульс присутствия (PRESENCE);
  • отправка бита 0;
  • отправка бита 1 и она же по совместительству чтение бита.



Чтение ключа Dallas на Flipper. Состоит из команд Search ROM и Read ROM. Каждая команда имеет Reset sequence.

Чтение ключа на Flipper Zero устроено так: поиском проверяется наличие ключа, а затем происходит чтение ID. Это сделано, чтобы избежать случайных совпадений с другими ключами Cyfral/Metakom, тайминги которых могут случайно совпасть с требуемыми.

На осциллограмме виден длинный сигнал из 2 команд, где каждая состоит из:
  • Инициализации команды:
    • Импульс сброса;
    • Импульс присутствия;

  • Передачи команды для Slave;
  • Ответа Slave на принятую команду.


Разберем более детально каждый из пунктов.


Reset sequence инициализация команды. Состоит из Импульса Сброса и Импульса Присутствия. Импульс Сброса уровень опускает Master. Импульс Присутствия уровень опускает Slave.

Инициализация (reset sequence) состоит из двух импульсов:
  1. Импульс Сброса (Reset pulse);
  2. Импульса Присутствия (Presence pulse).

Для Импульса Сброса линию к земле подтягивает Master (домофон).
Для Импульса Присутствия линию к земле подтягивает Slave (ключ).

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


Команда чтения ID с ответом. Тайм-слот бита информации состоит из 2 участков: синхронизации и значения бита. В КОМАНДАХ за уровни напряжения на обоих участках отвечает Master. В ОТВЕТЕ на команду чтения за синхронизацию отвечает Master, за значение бита отвечает Slave.

После инициализации команды происходит обмен информацией:
  • отправка команды для Slave;
  • ответ Slave на команду.


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

Важно, что все сигналы имеют конкретные временные ограничения, которые обязательно должны быть соблюдены! Бывает, что производители домофонов упарываются, и соблюдают какие-то свои временные задержки, но на Руси дураков не бьют, и мы их трогать не станем.
Еще у iButton есть режим Overdrive, в котором временные задержки сокращены, из-за чего передача информации происходит быстрее. Есть вероятность, что в дикой природе существуют домофоны, работающие в этом режиме Но на практике они не встречались.

Стандартные команды 1-wire ключей Dallas


Для iButton характерны команды размером 1 байт (8 бит).
Зачастую домофон использует команды поиска и чтения ID (Search ROM и Read ROM).

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

Стандартные команды iButton для Regular режима следующие:



Бонус про домофонные ключи

Бонус про домофонные ключи


Почему старые домофоны это плохо?


Некоторые старинные домофоны с ключами Dallas имеют в памяти базу ID ключей, заполненную не полностью. Незаполненные поля имеют некоторое значение, иногда соответствующее всем нулям (0x00) или всем единицам (0xFF). Для проверки домофона на дремучесть создаются два ключа: один со всеми нулями, другой со всеми единицами.
Эти ключи содержат неправильный код семейства (не 0x01) и неправильный CRC (вообще не контрольная сумма)!!! Да, бывают и такие исключения.

Как почтальоны разносят по подъездам рекламу?


Никак, почтальоны разносят письма :) Но очевидно, что в почтовом отделении нет огромной связки с ключами для каждого подъезда. Часто используют уникальные ключи, они же вездеходы, подходящие на группу домофонов, например для всего района. Этот ключ может быть один на город, один на группу домов, может быть дефолтным для какой-то модели, а может и вовсе отсутствовать.

Что такое мастер ключ?


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

Играясь заблокировали домофон?


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

Какие болванки нам известны


В ходе работы с iButton мы зафиксировали некоторые известные нам болванки ключей. Вот они, на здоровье!




Наши соцсети


Все обновления по проекту первым делом публикуются в Telegeram-канале @zhovner_hub
Подробнее..

Открываем дверь при помощи голосового ассистента

18.12.2020 18:13:39 | Автор: admin


Всем привет!
Сегодня у меня статья про то, как при помощи Яндекс-колонки Алиса и Ардуино, можно открыть дверь, подключенную к домофону. Если Вам это интересно, то прошу под кат.

Часто бывает так, что кто-то звонит в домофон, а ты в этот момент чем-то занят или просто лень подходить для того, чтобы открыть дверь. Знакомая ситуация, не правда ли?
А мой домофон так вообще без поднятия трубки не открывает дверь и если в течении 30 секунд после вызова не поднять трубку, то кнопка открывания двери перестанет быть активной до того момента, пока не поступит новый вызов. Так как домофон не из дешёвых (на тот момент) меня жаба душила менять его на другой. И я решил с этим бороться.

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

Второй проект был реализован на роутере TL-MR3020 с прошивкой CyberWRT и программным СИП телефоном BareSIP. При нажатии на кнопку вызывной панели, роутер параллельно с вызовом интеркома звонил на мобильный телефон и можно было даже находясь в другом городе разговаривать с человеком, стоящим около двери твоей квартиры, но программный телефон или сам роутер, работали крайне нестабильно и пришлось от него отказаться.
Но тут к моему счастью в прошлом году на день рождения мне подарили голосового ассистента Алису и у меня появилась новая идея отрывать дверь голосом, через Яндекс колонку.
И сегодня настал тот день, когда я взялся реализовать свою задумку.

Немного о том, как работает вызывная панель видео-домофона.


Вызывная панель соединена с монитором домофона при помощи 4-х проводов GND, +12 В, аудио и видео.
Если на вызывной панели, провод Audio замкнуть через резистор 100 Ом на GND, то раздастся сигнал вызова. А если на провод Audio подать напряжение +12 В, то сработает реле открытия электромагнитного замка.

На изображении ниже показана схема соединения вызывной панели с домофоном.



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

GND экранирующая оплетка
+12 В красный провод
Аудио синий провод
Видео центральная жила коаксиального кабеля



Схема


Из схемы видно, что для подключения устройства на Ардуино к домофону, потребуется подсоединить всего 2 провода. Для коммутации этих проводов я использовал то, что у меня было под рукой. Вместо реле можно использовать ключ на N-P-N транзисторе или оптроне PC817.

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


Используемые компоненты


Ардуино Нано
Модуль реле
ИК приемник 38 кГц
Провода Dupont

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

Исходник для Arduino


Код достаточно простой и понятный.
Для получения и сохранения кода нажатой кнопки, нужно перевести контроллер Ардуино в режим добавления кнопки пульта. Потребуется установить перемычку на выводы D2 и GND и нажать кнопку сброса на Arduino, после чего на плате контроллера засветится системный светодиод и контроллер будет ожидать нажатия кнопки на пульте.

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

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

Код для Ардуино
#include <IRremote.h>
#include <EEPROM.h>

//#define TOLERANCE 35 // процент допустимого отклонения принимаемого сигнала
#define IR_RECEIVE_PIN 11 // Вход для подключения ИК приемника
#define RELAY_PIN 12 // Выход для управления реле
#define SET_PIN 2 // Вход для перевода в режим программирования пульта

IRrecv irrecv(IR_RECEIVE_PIN);
decode_results results;

void setup()
{
irrecv.enableIRIn(); //запустить ожидание нажатия кнопки на пульте
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
pinMode(SET_PIN, INPUT_PULLUP); //D10 конфигурируем на вход, для режима запоминания кода
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
// Serial.begin(115200);

while(!digitalRead(SET_PIN)) //если пины D2 и GND замкнуты то заходим в режим запоминания ИК кода
{
set_command();
}
}

void loop()
{
if (irrecv.decode(&results)) //если был принят код с пульта
{
if(LoadEEPROM()==results.value) //если код совпадает с сохраненным (ir_command==results.value)LoadEEPROM();
{
digitalWrite(RELAY_PIN, HIGH); //включить реле
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
digitalWrite(RELAY_PIN, LOW); //Выключить реле
}
irrecv.resume(); //запустить ожидание нажатия кнопки на пульте
}
}

void set_command() //режим запоминания кода пульта ДУ
{
digitalWrite(LED_BUILTIN, HIGH);
if (irrecv.decode(&results))
{
digitalWrite(LED_BUILTIN, LOW);
SaveEEPROM(results.value); //сохраняем полученый код кнопки ПДУ в EEPROM
irrecv.resume(); //запустить ожидание нажатия с пульта
}
delay(100);
}

void SaveEEPROM(unsigned long ir_code) // записываем полученый код кнопки ПДУ в EEPROM
{
EEPROM.write(0, ir_code & 0xFF);
EEPROM.write(1, (ir_code & 0xFF00) >> 8);
EEPROM.write(2, (ir_code & 0xFF0000) >> 16);
EEPROM.write(3, (ir_code & 0xFF000000) >> 24);
}

unsigned long LoadEEPROM() // считываем код кнопки ПДУ из EEPROM
{
byte val = EEPROM.read(3);
unsigned long ir_code=val;
val = EEPROM.read(2);
ir_code= (ir_code << 8) | val;
val = EEPROM.read(1);
ir_code= (ir_code << 8) | val;
val = EEPROM.read(0);
ir_code= (ir_code << 8) | val;
return ir_code;
}

При публичном размещении, выложенного мной выше кода указывайте, пожалуйста, ссылку на первоисточник.

Подключение к Яндекс.Станции Алиса


Для подключения к умной колонке понадобится Умный пульт Яндекс.

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

Заключение


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

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

Если у Вас есть вопросы, то можете их задать в комментариях.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru