Приручение nRF пульта для ПК с потерянным приемником

cd20abd18cb71165a980a3042eed76f8.jpg

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

В далекой-далекой галактике…

Для начала, расскажу зачем мне вообще понадобился такой пульт. Будет много и длинно, кому это неинтересно, может сразу перейти к следующей главе. Дело, в том, что я все еще отношусь к той устаревшей модели людей, которые привыкли видеть компьютер центром всего мультимедиа в доме (как это было в середине 2000х) и хоть, теперь для каждой задачи уже давно есть свой гаджет, мне все равно так привычнее и удобнее. Поэтому фильмы я смотрю исключительно через ПК. У меня довольно древний 42' ЖК телевизор подключенный по HDMI, который я не вижу смысла менять на что-то новее. Если мне нужен большой экран — для этого у меня есть проектор и экран с диагональю в 2 метра (так же по HDMI к ПК). Но я уже давно на нем ничего не смотрел, он для чего-то такого эффектного, красивого, вроде Аватара, но что-то за последние пару лет я ничего такого интересного для себя не находил. А простые сериальчики можно глазеть и на экране поменьше, к чему эти огромные головы крупным планом? И вот, уже, наверное, лет 10 назад, попался мне первый пульт для ПК, шел он в комплекте к одной из материнок от ASUSа.

fe88acaaaf0c34336947ebfeb24f0e02.jpg

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

98568c8784476cceaf0f84136b3e3c77.jpg

Его я использовал вместе с программой EventGhost для Windows, которая позволяет назначать на разные кнопки разные действия и поддерживает некоторые пульты, включая этот. Но вот был один недостаток — для того, чтобы этот пульт нормально работал внутри EventGhost и не делал больше ничего лишнего, для него нужно было поставить специальный драйвер, а драйвер этот был не подписан. Как известно Майкрософт это любит не очень и хоть и оставляет такую возможность, но приходится немного поплясать с бубном, перезагружаясь в тестовый режим, где этот драйвер надо ставить. При этом, во время крупных обновлений 10ки он зачастую слетал и приходилось проделывать это снова. Этот пульт служил мне еще несколько лет, но в какой-то момент, после очередного слета драйвера, мне это надоело и я решил попробовать что-то еще. Тогда я заказал с Али вот такое чудо:

1e6655af3f92ecb7384687d3670fc05a.jpg

Он уже работает на 2.4Ghz и не требует прицеливания, что конечно большой плюс, а вот минусом было то, что для него не было уже какого-то специального драйвера, который позволил бы мне назначить на каждую его кнопку именно те комбинации клавиш, которые мне нужны. Т.е. я конечно мог назначить на кнопку «домой» какое-то действие в EventGhost и оно выполнялось, но помимо этого кнопка «домой» срабатывала еще и как кнопка «домой» и открывала мне браузер с домашней страницей. Я даже нашел какую-то программу, которая позволяла мне заблокировать прямое назначение некоторых кнопок, на которые у меня были назначены макросы в EventGhost и это улучшило картину. Но я все еще не мог назначить действие на кнопки с цифрами, иначе они выполнялись, когда я нажимал эти цифры на обычной клавиатуре, и заблокировать я их не мог по той же причине. Позже я нашел древнюю программу HidMacros которая могла вешать действия на клавиши с какого-то конкретного устройства, и она даже работала под 10кой, но че-то программ стало слишком много для такой простой задачи и я снова стал бояться, что с очередным обновлением что-то из этого отвалится. К этому времени я как раз увлекся Arduino и понял, что могу сделать, чтобы любой ИК пульт работал у меня, как пульт для ПК. Я снова вернулся к предыдущему пульту, но теперь он передавал сигнал в ИК-приемник Arduino, а тот в свою очередь отдавал команду в EventGhost через ком порт. Единственное, что после 2.4Ghz пульта, использовать снова ИК, которым надо хоть не много, но прицеливаться было шагом назад, но я смирился. И все шло себе тихо-мирно, но вдруг, внезапно, мне достались эти красавцы:

5c28ad8658c1c1fd7c918fa02fa67042.jpg

Посмотрев на них и на их набор кнопок, я понял, что хочу чтобы хоть один из этих ребят работал на меня. Я вставил батарейки и радостно начал нажимать кнопки, но на ИК приемник ничего не приходило. И вот тут я понял — они не ИК, они такие же, как и VONTAR на картинке выше, работают на 2.4Ghz и без приемника с ними делать нечего, а приемников от них то у меня и нет. Можно было, конечно, заказать такой же пульт с приемником на Али, но я уже предвидел какие проблемы меня ждут: я снова не смогу нормально назначать действия на кнопки, они будут эмулировать те клавиши которые в них зашиты и только их. Снова надо как-то хитро блокировать прямое назначение этих клавиш, снова все не просто Plug и не просто Play, опять какие-то трудности. А я не люблю, когда трудности. Я люблю когда ахренительно-невероятно-пипец, какие трудности. Поэтому я заказал для Ардуино модуль nRF24L01+ и раз уж нам подарили с барского плеча столько выходных на майские, а погода как-то не все дни обещает быть хорошей, я решил потратить часть этого времени, чтобы хакнуть протокол пультов!

Этап принюхивания

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

bc699bc5f38b0528993db75b0bc61a8c.jpgЭто не моя картинка, но подключал я по этой схеме на те же пины с поправкой на их другое расположение в наноЭто не моя картинка, но подключал я по этой схеме на те же пины с поправкой на их другое расположение в нано

Ну вот теперь я точно был готов к перехвату пакетов, но вот пакеты оказались не готовы быть перехваченными. Это было первое разочарование. Вообще модуль nRF24L01+ я брал наугад, типа тут 2.4Ghz, там 2.4Ghz, ну… давайте… работайте вместе как нибудь! Ну пожалуйста! Но, как выяснилось, протоколов этих очень много, те же модули WiFi работают на этих частотах, но пакеты от пульта получать в большинстве своем не могут. И если пульт шлет свои пакеты не по тому протоколу, по которому работает nRF24L01+, то тоже ничего не выйдет. Я понадеялся на удачу и для начала стал искать канал, на котором должны идти данные. Для этого я скачал какой-то нагугленный скетч в интернете, который смотрит на каких каналах есть сигналы. Как оказалось сигналы есть много на каких, но нажатия кнопок на пульте никак на них не влияет. Сигналов больше не становиться ни на одной из частот, а они хаотично появляются на разных частотах, не зависимо от того, нажимаю я что-то на пульте или нет. У nRF24L01+ есть 3 скорости 250Kbit, 1Mbit и 2Mbit. Ни на одной из них и ни на одном из каналов, я пульта не нашел, сколько бы не давил не его кнопки. «Ну что ж», сказал я себе, значит не судьба, и лег спать. Но на утро интерес снова проснулся, и я решил разобрать один пульт и посмотреть, что там все-таки за чип. К моей радости пульт, хоть и собран без единого винтика, но разобрался очень легко:

487c5d1a7f582f2bcbd1f172a06e8377.jpg

И что же я вижу на плате?

fa0abc33ca460b052deeee4365843f4a.jpg

Именно! Там чип nRF24LE1G, внутри которого сидит nRF24L01. Значит все верно, и протокол тот. Но почему же, я не могу понять на какой частоте он работает? Пульт точно рабочий (когда я нажимаю кнопку, на нем моргает лампочка. А раз лампочка моргает — значит и пульт работает. И не говорите, мне что это не обязательно так!). Я потратил на это еще пару часов, но никаких результатов не добился и тогда я понял:

если уже больше ничего не помогает — прочти наконец этот чертов код, который ты скопировал из интернета и бездумно вставил в свой проект!

И я стал смотреть код и понял в чем причина. Модуль не умеет одновременно случать все частоты, поэтому код написан так, чтобы модуль постоянно переключался с одной частоты на другую слушая каждую по 128 мкс. Но частот много, и получается, что те сигналы, которые генерит пульт, не успевают попадать под сканирование или попадают, но в очень малом объеме, не отличимом от общего шума. В результате я переписал скетч, чтобы частоты переключались вручную ('w' в COM порт — следующая, 's' — предыдущая), и о чудо! Уже на первом же канале, я увидел изменения при нажатии кнопок пульта. На втором канале, сигналов при нажатии стало еще больше, на 3 м еще, а к 7 му все прекратилось. Значит золотая середина — это канал номер 3, по нему и идут данные. Сканер, который получился в итоге выложил сюда:

https://github.com/CodeName33/NRFRemote/blob/main/NRF24Scanner.ino

Итак, пол дела сделано (я так думал), пульт работает по нужному протоколу и частота найдена, осталось только получать данные и использовать их! Как все просто в ожидании. Теперь реальность. Все не так! Как оказалось, nRF24L01 это не вай-фай и в нем нельзя сказать «получай все». Он требует задания скорости, канала и адреса, и без этого ничего получать не будет. Канал я уже знаю, осталось две переменные, скорость и адрес, и если первое можно подобрать (их всего три), то на подбор второго (адрес от 3х до 5 байт) уйдут всего лишь, миллиарды лет. Как хорошо, что они у меня, есть, погнали! (нет). Путем гугления выяснилось, что модуль можно немного обмануть. Адрес может быть от 3х до 5 байт, но если передать ему, что длина адреса 2 байта, а само значение 0×55 или 0xAA, то можно таким образом получать все! В одном из случаев данные будут сдвинуты на 1 бит (в зависимости от того, как они передаются передатчиком). Ну что ж, начнем с адреса 0×55 и скорости в 2Mbit и к моему удивлению, это сработало с первого раза, я тут же стал получать кучу пакетов, а зажав кнопку на пульте, я заметил, что среди этих пакетов стали появляться пакеты с однотипным началом:

69eda8a66637bbad451257a0b0c97678.jpg

Естественно, я тут же поставил фильтр по этим 4 первым байтам и о чудо! Пакеты начали появляться, только при нажатии кнопок с пульта:

2a586096d27097e8c40ec37b4f40ab9a.png

Успех был так близко, осталось только запомнить какие данные идут от каких кнопок и все решено, но вот тут возникла проблема. При нажатии на одну и ту же кнопку, каждый раз приходили разные данные, точнее если отсеять мусор в конце, я заметил 2 варианта и по началу я решил, что добавлю их оба, но позже, уже начав добавлять коды всех кнопок, я понял, что вариантов уже не 2, а 4. И добавлять на каждую кнопку все 4 варианта кодов было не круто. Да и вообще, не круто распознавать кнопку по данным сырого пакета, надо этот пакет хотя бы расшифровать. Возможно я в нем найду неизменные данные, по которым гораздо проще смогу определять, какая кнопка был нажата. И если формат самой секции данных в пакете знает только производитель пульта (HP), то формат пакета nRF24L01 секретом не является, вот он:

7e700958883b3d307ea71e2997546bed.png

По этой схеме я и стал пытаться разобрать данные:

eedc9eb5135357d7347f3fc98249924a.jpg

И все бы тут хорошо и адрес постоянный и доп адрес (5й байт адреса) меняется в зависимости от группы нажатой кнопки, но вот размер данных, я честно пытался взять его из чисел 13, 12, 159 и 130 извлекая 6 бит. Но выходил полный бред, 12 и 13 давали число 3, что явно слишком мало, а 159 — это 39, что явно нарушает все правила протокола, допускающего не более 32 байт данных. Притом, что данные внутри пакета дальше шли даже вроде и не плохо. Чертов Packet Control Header занимает именно 9 бит, и это значит, что все байты дальше надо сдвигать на 1 бит, что я и делал. И вроде все похоже. Но не совсем. Все равно на одну и ту же кнопку, я получал разные данные, странные данные, хотя часть из них выглядела очень правдоподобно. Но времени было уже около 3х ночи, и еще через 3 часа первые петухи начнут петь о том, какой я лошара, и мне нечего будет им ответить. Совсем нечего. На этом я плюнул, и лег спать. Утро вечера мудренее, я уже не раз в этом убеждался и решил, поверить этой пословице снова.

Этап вязки

Мое утро настало позже, чем я ожидал, но все таки подкинуло еще пару идей, я снова начал изучать эти цифры, пытаться крутить биты туда-сюда, понять что я упустил, но все подтверждало то, что у меня все было правильно, а картина все равно не сходилась. Я потратил еще пару часов и результат был нулевой. Черт, что же не так? Я же вроде не дурак? У меня даже справка есть! Мне её выдавали, когда я права менял. С тех пор не должно было ничего сильно измениться. Я еще раз понажимал две соседние кнопки на пульте и кажется начал кое-что понимать. Одна кнопка давала что-то типа последовательности 247 0 где-то в середине данных, другая 246 128 на тех же позициях. 247 и 246 это наверное коды кнопок, они рядом, логично, что соседние кнопки имеют отличие на кода единицу. Но вот следующий байт это 0 в одном случае и 128 в другом. Но ведь 0 это 00000000 по битам, а 128 это 10000000. Такое ощущения что эта единица заехала сюда зря, она из предыдущего байта. Но я точно все правильно смещаю, на 1 бит, как и должно быть, без смещения было еще хуже, было вообще 01000000, что равно 64… Стоп! Все если и правда все смещено еще на 1 бит! Ведь именно этим и отличаются адреса 0×55 и 0xAA которые используются для «хакерского» приема всех пакетов.

Флешбэк:

Адрес может быть от 3х до 5 байт, но если передать ему, что длина адреса 2 байта, а само значение 0×55 или 0xAA, то можно таким образом получать все! В одном из случаев данные будут сдвинуты на 1 бит (в зависимости от того, как они передаются передатчиком)

Я определенно дурак, я ведь уже пробовал менять 0×55 на 0xAA до этого, но в этом случае у меня вообще переставали приходить мои пакеты… А они так и должны были сделать! Все сдвинулось на 1 бит, и адрес тоже, а я продолжаю фильтровать пакеты по первым байтам 65, 223, 152, 111, а они тоже меняются! Я отменил фильтр и снова нашел адрес пульта в море сигналов, он и правда стал другим:

4a9b0ea54107a7f8dfe26ec720c4c715.jpg

И наконец-таки, поле с размером данных стало адекватным, теперь пакеты можно расшифровать:

2504d75d0ad308ae92fd74ae68e940b9.jpg

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

d87d89b5ac4c47f4af87d1792f3f85c9.png

Я научился понимать, какие коды соответствуют нажатию клавиши, а какие тому, что все клавиши в группе отпущены. Это важно, для того, чтобы можно было делать авто-повтор нажатия для некоторых клавиш при их зажатии на пульте (перемотка, например или громкость), а для некоторых не делать (Play/Pause, например). В EventGhost все настраивается очень просто, для начала добавляется плагин ком-порта:

d377f78d51b6331fd4de211dde7844e6.png

Дальше все что приходит из этого ком-порта можно видеть в логе программы:

d15c3d1e126da45991aa4ba21fc50072.png

И назначать макросы на эти события:

09ab281f8c440e990f85a7fbbcb01342.png

Итоговый результат скетча здесь:

https://github.com/CodeName33/NRFRemote/blob/main/NRF24Remote.ino

CRC, я все же решил не проверять, т.к. при включении его проверки (вариант с 1 байтом CRC) почему-то отсеивается довольно приличная часть пакетов, которая на самом деле имеет вполне себе валидные данные внутри. Может быть что-то еще можно подкрутить, но пока сойдет и так. Еще у пульта выявился еще один неприятный момент: Часть его кнопок работает довольно странно (крестовина и цифры). После нажатия одной из этих кнопок, пульт довольно долго шлет еще какие-то команды и ту команду, которую я посчитал за «кнопка больше не нажата», он может присылать с задержкой до 1 сек. Это очень много и можно поставить крест на быстрой навигации стрелочками (она стала медленная). Вероятно это как-то связано с особенностями его работы, может быть приемник ему, что-то должен отвечать, а может я что-то неверно понял, но на данный момент мне не удалось это победить. Возможно, еще поковыряю.

В будущем хочу отказаться от EventGhost и перевести проект на Arduino Pro Micro, которая умеет прикидываться USB клавиатурой и сразу отправлять нужные нажатия клавиш, работая без посредников. В этом случае пульт сможет работать не только в разных ОС, без доп. программ, но и с телевизорами и Андроид устройствами. Но пока все так, как есть. Спасибо за внимание!

© Habrahabr.ru