Как изготовить kvm свитча своими руками. Обзор KVM переключателя из Китая. Выходим в прямой эфир

Эта статья написана под впечатлением от другой - большое спасибо автору! В этой статье почти удалось сделать собственный IP KVM Switch, и это круто! Но объясню, почему почти. Да, там все работает как и написал автор… До момента перезагрузки в биос - там вся магия рассеивается и сколько не старайся, ничего не происходит.

Решено было исправить это досадное недоразумение и как можно дешевле и компактней. Начнем со стереотипов Raspberry Pi и Arduino, а в следующей статье будет продолжение уже на другом железе.

Итак, что нам понадобится:

1. Плата видеозахвата обязательно с поддержкой UVC драйвера, вроде этой.Вариантов
полно на алиекспрессе и других китайских магазинах.

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

2. VGA to AV Конвертер:



Обратите внимание! Нужен именно VGA to AV, а не наоборот.

3. Arduino UNO, именно UNO, так как на ней есть чип Atmega16u2, он нас интересует в первую очередь. Вот он рядом с USB портом, так же бывают ардуины с чипом Atmega8u2 подойдут и с тем и с тем.

4. Ну и конечно Raspberry Pi, у меня был версии 2 b поэтому все написанное в этой статье актуально именно для него, но в целом думаю не должно возникнуть особых сложностей и с другими моделями малины.

Заливаем дистрибутив

Что ж вводные данные даны, пожалуй приступим.Я использовал дистрибутив 2015-05-05-raspbian-wheezy, вероятно это не принципиально, дальнейшие манипуляции должны подойти для любого дистрибутива основанного на Debian.

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

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

Sudo apt-get update && sudo apt-get upgrade –y

Передача видео

Проверяем определилась ли плата:

Ls /dev/video*
Должно выдать что-то вроде: /dev/video0.

Устанавливаем Motion, трансляцию захваченного изображения будем вести именно через него:

Sudo apt-get install motion -y
Редактируем конфиг автозапуска:

Sudo nano /etc/default/motion
В строке start_motion_daemon ставим ‘yes’. Сохраняем изменения Ctrl + x, y, Enter.

Редактируем конфиг самого motion(а):

Sudo nano /etc/motion/motion.conf
Меняем значения параметров как указано далее:

Параметр определяет запуск приложения в качестве службы:

Daemon on
Эти параметры определяют разрешение передаваемого изображения, смысла ставить большее разрешение нет, т.к. захват видео ограничен стандартами PAL или SECAM, разрешение коих 720х576. Это кстати досадный недостаток, но об этом позднее.

Width 800 height 600
Частота захвата кадров:

Framerate 25
Отключаем сохранение скриншотов:

Output_normal off
Качество передачи изображения:

Webcam_quality 100
Частота передачи кадров:

Webcam_maxrate 25
Отмена ограничения на подключение с других ip

Webcam_localhost off
Сохраняем изменения Ctrl + x, y, Enter.

Перезагружаем распберри:

Sudo reboot
Ждем пару минут если все сделали правильно должен загореться светодиод на плате видео захвата.

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

Процесс пошел, ищем жертву для захвата сигнала с VGA порта, подключаем к порту “VGA IN” конвертера, а плату видеозахвата к “VIDEO OUT”. Должна получиться примерно такая картинка, не пугайтесь у меня плохой кабель поэтому изображение «двоит», пробовал с другим изображение было лучше, но разрешение не изменить. 720х576 это ограничение конвертера и платы видео захвата, которое при всем желании не преодолеть.

Что ж передавать изображение научились, осталось дело за малым - передать управление.

Передача управления

Для этого, как вы уже догадались, будем использовать ардуину. Выбор пал на Arduino UNO неспроста, там есть очень нужная для наших целей микросхема с названием Atmega16u2, только благодаря ей мне удалось заставить БИОС компьютера определить arduino как USB клавиатуру. По умолчанию в составе платы ардуино эта микросхема выполняет роль USB to Serial конвертера для заливки прошивки в микроконтроллер Atmega328p, большая прямоугольная микросхема на плате ардуино. По сути же Atmega16u2 является то же микроконтроллером, но с важным отличием, она способна напрямую работать с шиной USB. Atmega16u2, при наличии нужной прошивки, может эмулировать практически любое USB устройство. Понимаете к чему я веду? Мы прошьем это чудо инженерной мысли и заставим трудиться на благо общества.

Прошивка Atmega16u2

На просторах интернета была найдена прошивка , которая превращает Atmega16u2 в USB клавиатуру принимающую команды, определенного вида, через Serial Port.

Инструкция в данной статье написана для windows, линуксоиды же могут воспользоваться этой .

И так приступим, для прошивки потребуется утилита от производителя под названием Flip . Качаем, устанавливаем, запускаем и вот перед нами окно программы:

Сначала кнопки(галки) не активны, это нормально, подключаем ардуину к компьютеру и замыкаем – размыкаем два крайних контакта со стороны USB порта, RESET и GND.

В системе должно появиться новое устройство под названием, как ни странно, ATmega16u2 устанавливаем драйвер(в папке с программой), выбираем в программе flip вкладку «Settings» → «Communication» → «USB» → «open», кнопки должны стать активны. На всякий случай можно сделать бэкап прошивки, чтоб можно было все вернуть на место. В меню «File» нажимаем «Load HEX File», программа требовательна к путям, лучше всего положить файл прошивки в корень диска C:, выбираем нужный hex файл с прошивкой, проверяем стоят ли галки «Erase», «Program», «Verify» и нажимаем «Run». Отключаем - подключаем arduino и вуаля… Теперь мы больше не сможем загружать прошивки в ардуино через встроенный USB, зато получили отличную клавиатуру без кнопок.

Не переживайте по поводу прошивки arduino, прошивку можно будет загрузить из Arduino IDE через отдельный USB To TTL адаптер, хотя надо сказать, теперь это будет менее удобно.

Подключаем USB To TTL адаптер, например такой:

Нам понадобятся Белый, зеленый и черный контакты это RX, TX и GND соответственно, подключаем их к пинам с такими же обозначениями на ардуине, только наоборот RX к TX, а TX к RX. Красный контакт использовать не следует!

Подключаем USB To TTL к компьютеру, устанавливаем драйвера, в диспетчере устройств должен появиться новый COM Port. Открываем arduino IDE и выставляем: Плата - Arduino/Genuino Uno, Порт – наш новоиспеченный последовательный порт.

Приступаем к прошивке arduino

Добавим необходимую библиотеку в arduino IDE: Переходим по ссылке github.com/SFE-Chris/UNO-HIDKeyboard-Library нажимаем «Clone or download» → «Download ZIP». далее в arduino IDE выбираем вкладку «Скетч» → «Подключить библиотеку» → «Добавить.ZIP библиотеку» и выбираем только что скачанный zip архив.

Подготовка закончена, переходим непосредственно к прошивке. Копируем мою писанину:

Arduino - Sketch

#include HIDKeyboard keyboard; int sbor; void setup() { keyboard.begin(); } void loop() { while (Serial.available()) {//запуск цикла при появлении данных sbor += Serial.read();//считывание данных, сложение в десятичном виде if (sbor == 27){//появление символа управляющей последовательности for (int i=0; i<=4; i++){//сложение последовательности if (sbor == 165) {//для определения F1-F12 на разных терминалах могут быть разные значения sbor += sbor; } sbor += Serial.read(); delay(1); } } } if (sbor > 0) { //переход по десятичной сумме последовательности switch (sbor){ case 505: keyboard.pressSpecialKey(F1); break; case 506: keyboard.pressSpecialKey(F2); break; case 507: keyboard.pressSpecialKey(F3); break; case 508: keyboard.pressSpecialKey(F4); break; case 509: keyboard.pressSpecialKey(F5); break; case 511: keyboard.pressSpecialKey(F6); break; case 512: keyboard.pressSpecialKey(F7); break; case 513: keyboard.pressSpecialKey(F8); break; case 340: keyboard.pressSpecialKey(F9); break; case 341: keyboard.pressSpecialKey(F10); break; case 343: keyboard.pressSpecialKey(F11); break; case 344: keyboard.pressSpecialKey(F12); break; case 13: keyboard.pressSpecialKey(ENTER); break; case 22: keyboard.pressSpecialKey(ESCAPE); break; case 127: keyboard.pressSpecialKey(BACKSPACE); break; case 9: keyboard.pressSpecialKey(TAB); break; case 32: keyboard.pressSpecialKey(SPACEBAR); break; case 26: keyboard.pressSpecialKey(PAUSE); break; case 292: keyboard.pressSpecialKey(INSERT); break; case 456: keyboard.pressSpecialKey(HOME); break; case 295: keyboard.pressSpecialKey(PAGEUP); break; case 294: keyboard.pressSpecialKey(END); break; case 296: keyboard.pressSpecialKey(PAGEDOWN); break; case 182: keyboard.pressSpecialKey(RIGHTARROW); break; case 183: keyboard.pressSpecialKey(LEFTARROW); break; case 181: keyboard.pressSpecialKey(DOWNARROW); break; case 180: keyboard.pressSpecialKey(UPARROW); break; case 293: keyboard.pressSpecialKey(DELETE); break; case 320: keyboard.pressSpecialKey((CTRL | ALT), DELETE); break; //для вызова ctl+alt+del нажать alt + del case 346: keyboard.pressSpecialKey(ALT, F4); break; //для вызова alt+f4 нажать shift + F4 default: keyboard.pressKey(sbor); break; } //Serial.println(sbor);//только для отладки без подключения к usb keyboard.releaseKey(); sbor = NULL; } }


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

Avrdude: reading on-chip flash data: Reading | ################################################## | 100% 0.34s avrdude: verifying ... avrdude: 2934 bytes of flash verified avrdude done. Thank you.
Если после нескольких попыток загрузка прошивки так и не произошла, попробуйте поменять местами контакты RX и TX, а также проверьте надежно ли подключен контакт GND.

Финишная прямая

Открываем консоль на распберри и пишем:

Sudo raspi-config
Откроется меню настройки распберри, выбираем «Advanced Options» → «Serial» и выбираем «No».

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

Устанавливаем программу minicom.

Minicom - простенькая программа для работы с serial портом.

Sudo apt-get install minicom -y
Задаем права на доступ к устройству, /dev/ttyAMA0 - это тот самый сериал порт.

Sudo chown pi /dev/ttyAMA0 sudo chmod 744 /dev/ttyAMA0
Запускаем minicom:

Sudo minicom -s
Откроется меню программы, выбираем пункт «Serial port setup», откроется еще одно меню, выбираем «Serial Device» нажатием на клавишу A, прописываем /dev/ttyAMA0, нажимаем Enter, далее выбираем пункт Bps/Par/Bits под буквой E, появляется очередное меню нажимаем C и Q строчка Current: должна выглядеть вот так «9600 8N1» нажимаем Enter. Убедимся что в строчках F - Hardware Flow Control: и G - Software Flow Control: стоит No, в общем все должно быть как на скриншоте ниже, нажимаем Enter.

Сохраним эти настройки как настройки по умолчанию «Save setup as dfl» и закрываем «Exit from Minicom».

Подключение

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

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

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

Или хотя бы собрать делитель напряжения на резисторах.

Запуск

Все готово, можно начинать.

Проверяем все соединения, вкючаем raspberry pi, переходим в консоль малины, запускаем minicom. Сразу оговорюсь я подключался к малине через ssh, в качестве клиента использовал KiTTY(модифицированную версию PuTTY), это важно т.к. с другими терминалами значения передаваемых клавиш могут быть иными и соответственно нужно будет сделать поправку на ветер - поменять номер перехода switch case.

В общем передаю вам в руки как говорится «as is». Что ж на этом пожалуй закончу, самодельный IP KVM готов.

P. S.

На последок опишу что получилось в сухом остатке.

Плюсы:

- Цена
- Устройство получилось относительно недорогим
- Raspberry Pi: примерно 2700руб.
- Arduino UNO: примерно 400руб.
- VGA to AV конвертер: примерно 700руб.
- Плата видеозахвата: 500руб.
- Итого: 4300руб.

- Тонкая настройка
Можно перехватывать практически любые комбинации и назначать на них практически любые клавиши вплоть до KEYBOARDPOWER и VOLUMEUP, кстати возможные значения можно посмотреть в заголовочном файле HIDKeyboard.h, а можно и добавить свои.

Минусы:

- Торможение как видео, так и передачи нажатий
- Второй и самый большой это качество изображения , здесь просто необходим грустный смайлик, оно ужасно, даже если убавить разрешение на целевом компьютере до минимума, максимум что можно будет сделать, это настроить БИОС и выбрать пункт в загрузчике. Но разве собственно не для этого нужен KVM?.. А для всего остального существует радмин и ему подобные.

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


Здравствуйте друзья! Для подключения двух компьютеров к одному интерфейсу, я приобрёл на Aliexpress-е за 4$ вот такой, самый простой KVM VGA переключатель.

Выбор пал на VGA, так как мой монитор не имел HDMI входа, а по входу DVI не захотел работать через адаптер.



Предположение, что на печатной плате хватит места на ещё два переключателя для коммутации аудио и USB разъёмов, полностью оправдалось.

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

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


Правда, пришлось заменить переключатель мониторов, так как мне попался усечённый вариант KVM VGA, где не хватало одной группы контактов. Как потом выяснилось, нужно было выбирать вариант, где в технических данных было прописано разрешение Full HD. Но, и тут трудностей не возникло, так как необходимые дорожки были проложены на плате и осталось просто заменить П2К.


Два дополнительных переключателя распаял в пистонах, запрессованных в печатную плату.


Для дополнительных кнопок пришлось просверлить в лицевой панели два отверстия.


Ещё шесть отверстий просверлил в задней стенке для соединительных кабелей.

USB кабели тоже изготовил самостоятельно.


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


Вот что у меня получилось в результате.


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


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

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

Для тех, кто только что присоединился, краткое содержание предыдущих серий:


  • Собирали IP KVM на Arduino и Raspberry Pi, получилось дорого и с плохим качеством видео.

  • OrangePI и Atmega16u2, поучилось дешево, но качество изображения всё также отвратительно.

И вот, наконец, в этой статье будут исправлены все минусы предыдущих. Особый упор будет сделан на максимальное снижение стоимости компонентов.

По традиции рассмотрим составные части собираемого устройства:

1. Наша старая знакомая Atmega16u2:

Это единственный компонент, который перейдет из предыдущих статей.

2. Небезызвестный ESP8266, в данном случае ESP8266-12e:


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

3. И, собственно, виновник торжества LKV373A


Благодаря этому устройству стало возможным передавать видео по локальной сети, с высоким
разрешением вплоть до full-HD.

План мероприятия таков:

  1. Игра в прятки: Ищем LKV373A куда он спрятался в сети под каким ip адресом
  2. Игра в хакера: Заливаем прошивку, сбрасываем пароли и настраиваем LKV373A
  3. Заводим новых друзей: ESP8266, Arduino IDE и веселые картинки
  4. Финал торжества. Подключаем все компоненты и передаем нажатия клавиш через telnet

LKV373A, предыстория

Итак, приступим! LKV373A или HDMI Extender устройство для захвата изображения прямо с порта hdmi и трансляции в локальную сеть, эти устройства еще называют hdmi удлинителями. По задумке производителей комплект подобных устройств должен состоять из transmitter (передатчика) и receiver (приемника), обозначение TX и RX соответственно. Использоваться они должны, вероятно, для того, чтобы приносить производителю больше прибыли, только в паре. Но нашелся человек, под ником Danman вот ссылка на его блог, которому стало интересно как это работает. Открыл он Wireshark, поснифал трафик передаваемый TX устройством, что же оказалось?

Видеопоток передается без какого-либо шифрования, и при помощи VLC Player"a можно посмотреть его без лишних усилий. Но на этом он не остановился, «пощупал»: и веб интерфейс, и TTL, и телнет, и даже прошивку программатором стянул. Об этом он в подробностях рассказал в своем блоге. Там же была выложена прошивка, которая интересует нас в первую очередь: IPTV_TX_PKG_v4_0_0_0_20160427.PKG. В этой прошивке веб интерфейс с расширенными настройками, а не как у стандартной только кнопка обновить. К тому же, в этой прошивке есть телнет с множеством команд для настройки. Именно при помощи этой прошивки мы перенастроим HDMI Extender под наши задачи. Прошивку и все необходимое я выложил на github вот ссылка , она понадобится нам позднее, а пока заканчиваем с теорией. Переходим к практике.

Передатчик

Ищем LKV373A в сети

Мне попался в руки такой же extender что и Danman(у). Все что будет изложено далее подходит именно для HDMI Extender LKV373A версии V3.0!

Подключаем LKV373A к локальной сети, включаем питание. Теперь попробуем удостовериться, что устройство видно в сети ping 192.168.1.238 .

192.168.1.238 это IP-адрес по умолчанию. Если на extender"ре старая прошивка адрес не изменится, независимо от того, присутствует DHCP сервер в сети или нет. Более новые версии прошивки используют IP по умолчанию только, если устройство не смогло получить адрес от DHCP. Если на ping запрос получили ответ, продолжаем дальше. Если нет, не отчаивайтесь, попробуйте подключить extender напрямую к LAN порту компьютера и воспользоваться сниффером.

Прошиваем

HDMI Extender нашли, перейдем к прошивке. Зайдем на гитхаб и скачаем все, что там выложено. Теперь откроем веб интерфейс extender"а через браузер и увидим такую картину:


Нажимаем «Browse…», выбираем прошивку, файл с названием IPTV_TX_PKG_v4_0_0_0_20160427.PKG, и нажимаем «Upgrade». Тадам! Прошивка завершена, теперь подключимся к LKV373A по телнету, чтобы сбросить пароль.

Команда для подключения будет выглядеть примерно так Telnet 192.168.1.238 9999, где 9999 - это порт для подключения. КЭП предупреждает: адрес полученный от DHCP можно узнать при помощи сканера сети.

Подключаемся через телнет

При подключении должно появиться следующее сообщение:

============================== ========IPTV TX Server======== ============================== input>

Пишем list . В ответ получаем список команд:

============================== ========IPTV TX Server======== ============================== input>list set_group_id get_group_id set_dhcp get_dhcp set_uart_baudrate get_uart_baudrate set_static_ip get_static_ip set_mac_address get_mac_address get_lan_status get_hdcp get_video_lock get_ip_config set_session_key set_device_name get_device_name set_video_bitrate get_video_bitrate set_downscale_mode get_downscale_mode set_video_out_mode get_video_out_mode set_streaming_mode get_streaming_mode get_fw_version get_company_id factory_reset reboot list exit

Для сброса всех настроек и пароля используем команду factory_reset . Пишем команду, нажимаем enter и получаем такую картину:

Input>factory_reset Processing factory reset! System will reboot after few seconds! Connection closed by foreign host.

Веб интерфейс

Теперь мы можем настраивать устройство как нужно нам. Зайдем в веб интерфейс. Используем стандартные логин:admin пароль:123456 и вот он, «желанный» веб интерфейс с дополнительными пунктами настройки:


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

Вот, устройство в сети нашли, прошивку «накатили», пароли сбросили. Передатчик почти готов перейдем к приемнику.

Приемник

Давайте определимся, на устройство, под управлением какой операционной системы, будем передавать поток. Муки выбора думаю испытывать не будете, варианта всего два:

Windows

Для виндовс попробовал несколько плееров, но результаты получились так себе. При захвате и последующем воспроизведении видеопотока появляется задержка, которая в первую очередь зависит от плеера воспроизводящего поток. На различных плеерах задержка варьировалась от одной секунды, до пяти и даже больше. Лучше всех, на виндовс, показал себя VLC Player.

Перейдем к делу. Запустим VLC Player, сверху в выпадающем меню «Медиа» выбираем пункт «Открыть URL…» в поле сетевого адреса пишем udp://@:5004 , ставим галку в чекбоксе «Показать дополнительные параметры» и в поле «Кэширование» ставим свое значение, этот параметр определяется индивидуально. Чем меньше значение в этом поле, тем меньше задержка, но слишком малое значение может привести к «артефактам» и выпадениям кадров, все будет зависеть от инфраструктуры локальной сети. Лучшие результаты, которых смог добиться, это задержка около одной секунды. На линуксе же, результаты получились гораздо лучше около 200-300 миллисекунд.

Linux

Как показывает практика, лучшие результаты получаются при использовании связки программ socat и mplayer. На моем компьютере установлена Ubuntu, поэтому команда для установки socat будет выглядеть так:

Sudo apt-get install socat

Mplayer устанавливается аналогично:

Sudo apt-get install mplayer

Ну, и прислушаемся совету Danman"а:

Sudo iptables -t raw -A PREROUTING -p udp -m length --length 28 -j DROP

Эта команда нужна для того что бы убрать из потока так называемые «Zero Length UDP Packets» - пакеты нулевого размера, которые «засоряют» поток.

Выходим в прямой эфир!

Приемник готов, можно запускать трансляцию! Набираем в консоли принимающего компьютера ifconfig или ipconfig в зависимости от операционной системы, Запоминаем IP-адрес приемника и вернемся к веб интерфейсу передатчика. Откроем веб-интерфейс, если вдруг уже закрыли, введем логин, пароль и прямо в адресной строке браузера пишем:

Http://АйПиПередатчика/dev/info.cgi?action=streaminfo&udp=n&rtp=y&multicast=n&unicast=y&mcastaddr=АйПиПриемника&port=5004

Подставляем свои IP-адреса и нажимаем enter. Эта строка настроит HDMI Extender для трансляции захваченного видео на выбранный вами ip и выключит мультикаст.

Запускаем VLC в Windows. Или в терминале Linux пишем:

Socat UDP-RECV:5004 - | mplayer –

Абра кадабра! И вот наш, или не наш, рабочий стол в прямом эфире.


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

С передачей видео разобрались, переходим к удаленному управлению.

Передача управления

Выбор комплектующих

Т.к. стоимость HDMI Extender"а невысока, около 1800 рублей, а так же из-за комментариев что, мол, дороговато, выдвигаю лозунг: «Даешь IP KVM за 2000 рублей!». На верность этого утверждения будет сильно влиять курс рубля, но не будем о грустном, хочется верить в светлое будущее. Для достижения цели нам понадобятся очень дешевые элементы, мой выбор пал: на ESP8266 в качестве контроллера, и все того же Atmega(8/16/32)u2 в качестве исполнительного устройства.

Можно конечно рассмотреть и другие кандидатуры на роль исполнительного устройства(клавиатуры). Прошивка, эмулирующая клавиатуру, написана на библиотеке LUFA(вроде как). Эта библиотека может использоваться для всего семейства AVR с возможностью подключения к USB. Из этого следует, что в качестве эмулятора клавиатуры можно подобрать, возможно более дешевый, микроконтроллер. Это информация к размышлению, а сейчас продолжим.

Приобрести ESP8266 можно примерно за 90 рублей, Atmega(8/16/32)u2 примерно за 100 рублей, и даже дешевле, если брать небольшими партиями по 5, или больше штук. Конечно, нужны будут расходные материалы для обвязки микроконтроллеров, но стоимость их крайне мала, поэтому рассматривать их не буду.

ESP8266

Это чудо китайской промышленности не нуждается в представлении, посему скажу только, что в этом проекте я использовал версию ESP8266-12e. Использовать другие версии конечно можно, только нужно учитывать расположение портов, т.к. в данном варианте один из портов ESP8266 используется для включения питания на Atmega(8/16/32)u2, ниже будет указано на схеме.

Прошивка

Прошивка для ESP8266 написана в среде ArduinoIDE так что давайте скачаем свежую версию с сайта разработчика . Далее нужно добавить поддержку ESP8266 - самый простой способ можете найти по этой ссылке . На той же странице можно найти много полезной информации, например схема подключения к питанию и TTL. Для тех, кто не в курсе ESP8266 использует строго 3.3 вольта! Если не уверены в собственных силах, лучше использовать вариант платы адаптированный для USB, вроде NodeMCU:


Если все готово открываем ArduinoIDE и копируем мой скетч:

Скетч

#include #include #include #include #include #include #define MAX_SRV_CLIENTS 3 HIDKeyboard keyboard; const char* host = "esp8266"; const char* ssid = ""; const char* pass = ""; int rebootdev = 0; int modeswitch = 0; //номера портов, на разных реализациях ESP8266 могут быть разными #define Port1 15 #define Port2 14 #define Port3 12 #define Port4 4 #define Port5 5 //цвет кнопок String ColorB1; String ColorB2; String ColorB3; String ColorB4; String ColorB5; ESP8266WebServer server(80); WiFiClient serverClients; const char* serverIndex = "

BACK";//Update //Возврат на главную страницу void handleRedirect(){ String content = ""; server.send(200, "text/html", content); } //Страница авторизации WI-FI void handleLogin(){ String msg = ""; if (server.hasArg("SSID") && server.hasArg("PASSAP")){ if ((server.arg("SSID") != NULL) && (server.arg("PASSAP") != NULL)){ String header = "HTTP/1.1 301 OKrrnLocation: /rnCache-Control: no-cachernrn"; server.sendContent(header); String web_ssid = server.arg("SSID"); String web_pass = server.arg("PASSAP"); ssid = web_ssid.c_str();//перевод строки в массив символов в стиле C pass = web_pass.c_str(); Serial.println(); Serial.print("SSID "); Serial.println(ssid); Serial.print("Pass "); Serial.println(pass); WiFi.begin(ssid, pass); digitalWrite(LED_BUILTIN, LOW); ESP.reset(); return; } msg = "Wrong ssid/password! try again."; Serial.println("Login Failed"); } String content = "
Enter the access point name and password
";//страница ввода SSID и пароля content += "Name AP:
"; content += "Password:

"; content += "
" + msg + "
"; content += "Firmware update UPDATE"; server.send(200, "text/html", content); } void handleNotFound(){ String message = "File Not Foundnn"; message += "URI: "; message += server.uri(); message += "nMethod: "; message += (server.method() == HTTP_GET)?"GET":"POST"; message += "nArguments: "; message += server.args(); message += "n"; for (uint8_t i=0; i 0){ int StPort; if (digitalRead(UsePin) == 1) {//проверяет в каком состоянии находится порт digitalWrite(UsePin, LOW); StPort = 0; } else { digitalWrite(UsePin, HIGH); StPort = 1; } digitalWrite(LED_BUILTIN, HIGH);//индикация при получении данных Serial.print("Port "); Serial.print(UsePin); Serial.print("="); Serial.println(StPort); delay(500); digitalWrite(LED_BUILTIN, LOW); return(StPort); } return(-1); } //Ожидание подключения int clientConnect(int Seconds){ Serial.print("connection "); for (int i=0; i <= Seconds; i++){ WiFi.begin(ssid, pass); digitalWrite(LED_BUILTIN, LOW); delay(250); digitalWrite(LED_BUILTIN, HIGH); delay(250); Serial.print(" "); Serial.print("."); if (WiFi.status() == WL_CONNECTED) return(0); } return(1); Serial.println(); } void setup(void){ Serial.begin(115200); delay(1000); pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); //uint8_t i = 0; if (modeswitch == 0) WiFi.mode(WIFI_STA);//если переменная modeswitch = 0 преход в режим клиента Serial.println(); Serial.println(); if (clientConnect(30) != 0) modeswitch = 1;//старт подключения с ожиданием если не подключился переход в режим точки доступа if (modeswitch == 1){ Serial.println(""); Serial.println("WiFi switch AP mode"); //WiFi.mode(WIFI_AP_STA);Это для режима клиент + точка доступа. Закоментил потому что притормаживало WiFi.mode(WIFI_AP); WiFi.softAP("TD", "testtest"); Serial.print("AP mode ip adress "); Serial.println(WiFi.softAPIP()); digitalWrite(LED_BUILTIN, LOW); } if (modeswitch != 1){ WiFiServer server(23); Serial.println(); Serial.print("Client mod ip address: "); Serial.println(WiFi.localIP()); digitalWrite(LED_BUILTIN, LOW); } MDNS.begin(host); pinMode(Port1, OUTPUT); pinMode(Port2, OUTPUT); pinMode(Port3, OUTPUT); pinMode(Port4, OUTPUT); pinMode(Port5, OUTPUT); if (modeswitch == 1){ //Действия при переходе по ссылкам server.on("/", handleLogin);//Страница ввода логина(SSID) и пароля //Обновление прошивки server.on("/upload", HTTP_GET, (){ server.sendHeader("Connection", "close"); server.send(200, "text/html", serverIndex); }); server.on("/update", HTTP_POST, (){ server.sendHeader("Connection", "close"); int uperror = Update.hasError(); Serial.printf("UPERR %unRebooting...n",Update.hasError()); if (uperror == 0) server.send(200, "text/html", "Firmware update successfully BACK"); else server.send(200, "text/html", "Update error BACK"); ESP.restart(); },(){ HTTPUpload& upload = server.upload(); if(upload.status == UPLOAD_FILE_START){ Serial.setDebugOutput(true); WiFiUDP::stopAll(); Serial.printf("Update: %sn", upload.filename.c_str()); if (upload.filename == NULL) { Serial.printf("ERROR: zero file size"); server.send(200, "text/html", " zero file size BACK"); return(-1); } uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; if(!Update.begin(maxSketchSpace)){//start with max available size Update.printError(Serial); } } else if(upload.status == UPLOAD_FILE_WRITE){ if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){ Update.printError(Serial); } } else if(upload.status == UPLOAD_FILE_END){ if(Update.end(true)){ //true to set the size to the current progress Serial.printf("Update Success: %unRebooting...n", upload.totalSize); } else { Update.printError(Serial); } Serial.setDebugOutput(false); } yield(); }); //Управление портами server.on("/PoRt1", { controlPin(Port1); handleRedirect();//переход на главную страницу }); server.on("/PoRt2", { controlPin(Port2); handleRedirect(); }); server.on("/PoRt3", { controlPin(Port3); handleRedirect(); }); server.on("/PoRt4", { controlPin(Port4); handleRedirect(); }); server.on("/PoRt5", { controlPin(Port5); handleRedirect(); }); server.on("/reboot", { rebootdev = 1;//перезагрузка при переходе на главную страницу handleRedirect(); }); server.onNotFound(handleNotFound);//если нет такой страницы //server.begin(); MDNS.addService("http", "tcp", 80); Serial.println(); Serial.println("HTTP server started"); } server.begin(); } void loop(void){ uint8_t i; if (modeswitch == 1) server.handleClient(); delay(100); if (modeswitch != 1){ if (WiFi.status() != WL_CONNECTED) clientConnect(30); else { digitalWrite(5, HIGH);//включение питания ATMEGA16U2 digitalWrite(LED_BUILTIN, LOW); } } WiFiServer server(23); server.setNoDelay(true); server.begin(); keyboard.begin(); while(WiFi.status() == WL_CONNECTED) { if (server.hasClient()){ for(i = 0; i < MAX_SRV_CLIENTS; i++){ //find free/disconnected spot if (!serverClients[i] || !serverClients[i].connected()){ if(serverClients[i]) serverClients[i].stop(); serverClients[i] = server.available(); Serial.println("New client: "); Serial.print(i); continue; } } //no free/disconnected spot so reject WiFiClient serverClient = server.available(); serverClient.stop(); } //check clients for data for(i = 0; i < MAX_SRV_CLIENTS; i++){ if (serverClients[i] && serverClients[i].connected()){ if(serverClients[i].available()){ //get data from the telnet client and push it to the UART String bufkey; while(serverClients[i].available()) bufkey += (serverClients[i].read());//сбор данных в строковую переменную if (bufkey != 0) { bufkey = bufkey.substring(0, 8);//обрезка строки int key = bufkey.toInt();//перевод в целочисленные switch (key){ case 277980: keyboard.pressSpecialKey(F1); break; case 277981: keyboard.pressSpecialKey(F2); break; case 277982: keyboard.pressSpecialKey(F3); break; case 277983: keyboard.pressSpecialKey(F4); break; case 27914953: keyboard.pressSpecialKey(F5); break; case 27914955: keyboard.pressSpecialKey(F6); break; case 27914956: keyboard.pressSpecialKey(F7); break; case 27914957: keyboard.pressSpecialKey(F8); break; case 27915048: keyboard.pressSpecialKey(F9); break; case 27915049: keyboard.pressSpecialKey(F10); break; case 27915051: keyboard.pressSpecialKey(F11); break; case 27915052: keyboard.pressSpecialKey(F12); break; case 1310: keyboard.pressSpecialKey(ENTER); break; case 130: keyboard.pressSpecialKey(ENTER); break; case 27: keyboard.pressSpecialKey(ESCAPE); break; case 8: keyboard.pressSpecialKey(BACKSPACE); break; case 9: keyboard.pressSpecialKey(TAB); break; case 32: keyboard.pressSpecialKey(SPACEBAR); break; case 27915012: keyboard.pressSpecialKey(INSERT); break; case 27914912: keyboard.pressSpecialKey(HOME); break; case 27915312: keyboard.pressSpecialKey(PAGEUP); break; case 27915212: keyboard.pressSpecialKey(END); break; case 27915412: keyboard.pressSpecialKey(PAGEDOWN); break; case 279167: keyboard.pressSpecialKey(RIGHTARROW); break; case 279168: keyboard.pressSpecialKey(LEFTARROW); break; case 279166: keyboard.pressSpecialKey(DOWNARROW); break; case 279165: keyboard.pressSpecialKey(UPARROW); break; case 127: keyboard.pressSpecialKey(DELETE); break; case 27915112: keyboard.pressSpecialKey(DELETE); break; case 4: keyboard.pressSpecialKey((LCTRL | ALT), DELETE); break; //CTRL+ALT+DELETE нажать Ctrl + d case 6: keyboard.pressSpecialKey(ALT, F4); break; //alt+f4 нажать Ctrl + f case 19: keyboard.pressSpecialKey(ALT | SHIFT); break;//смена раскладки нажать Ctrl+s case 2: keyboard.pressSpecialKey(LCTRL | SHIFT); break;//смена раскладки нажать Ctrl+b //управление логическим состоянием портов case 17: controlPin(Port1); break;//Ctrl+q case 23: controlPin(Port2); break;//Ctrl+w case 5: controlPin(Port3); break;//Ctrl+e case 18: controlPin(Port4); break;//Ctrl+r case 20: controlPin(Port5); break;//Ctrl+t default: keyboard.pressKey(key); break;//нажать клавишу } keyboard.releaseKey();//отпустить клавишу Serial.print(" string: "); Serial.print(key);//для отладки Serial.print(" KEY: "); Serial.write(bufkey.toInt()); bufkey = "0";//на всякий случай обнулить } } } } //check UART for data if(Serial.available()){ size_t len = Serial.available(); uint8_t sbuf; Serial.readBytes(sbuf, len); //push UART data to all connected telnet clients for(i = 0; i < MAX_SRV_CLIENTS; i++){ if (serverClients[i] && serverClients[i].connected()){ serverClients[i].write(sbuf, len); delay(1); } } } } }

Подключим нужную библиотеку:

Выбираем вкладку «Скетч» → «Подключить библиотеку» → «Добавить.ZIP библиотеку» Выбираем библиотеку под названием «UNO-HIDKeyboard-Library-master(mod).zip», которую скачали с гитхаба. Компилируем и заливаем прошивку. Для загрузки прошивки подключаем ESP8266 TTL, выставляем нужный порт в ArduinoIDE. Настройки должны выглядеть примерно так:


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

Логика работы ESP8266 такова: При подаче питания микроконтроллер в течении примерно тридцати секунд пытается подключиться к FI-WI точке доступа:

Если подключение удалось : открывает 23 порт, к которому можно подключиться, используя telnet и передавать нажатия клавиш. Кроме клавиш можно передавать комбинации, построенные на «Ctrl + key» которые будут нажимать определенные сочетания. Например, если передать «Ctrl + d», на управляемом компьютере будет нажато сочетание CTRL+ALT+DELETE.

Также имеются комбинации для управления портами ESP8266, можно, например, подсоединить реле и при помощи комбинации «Ctrl+q» включать и отключать реле, тем самым удаленно включать и отключать управляемый компьютер. Эти и другие комбинации можете посмотреть в исходниках.

Если не удалось : ESP8266 переходит в режим точки доступа, с названием «TD», Паролем «testtest» и открывает небольшой веб интерфейс, доступный по адресу 192.168.4.1, в котором можно задать настройки для подключения по WI-FI.


Таким образом, устройство можно легко подключить к другой точке доступа. Да, тут скрывается ложка дегтя, для работы нашего IP KVM"а потребуется и LAN кабель и Wi-Fi. Такова будет плата за дешевизну устройства.

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

Подключение компонентов

Для правильного подключения компонентов прикладываю схему. Только учтите, это примерная схема, она служит для наглядности и не претендует на звание идеальной. Каждый волен «обставить» её как ему нравится.


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

Есть, конечно, альтернатива схеме, но это вариант для богатых, или ленивых. И кстати этот вариант не отменяет вышеизложенное:


На снимке Arduino UNO без чипа Atmega328p подключенный к китайскому аналогу NodeMCU. Линия 3.3 вольта подключена к линии того же уровня напряжения на Arduino, а так же подключена земля и контакт GPIO2(ESP8266) к контакту RX на Arduino(ошибка, следует подключать к TX ).

Финальная проверка

Подключаемся по телнету на 23 порт и проверяем работоспособность. Сделать это можно: На Windows командой telnet .

На Linux будет немного сложнее:

$ telnet 192.168.***.*** Trying 192.168.***.***... Connected to 192.168.***.***. Escape character is "^]". ^] telnet> l

Все готово, можем проверять работу созданного нами устройства. Напомню возможные комбинации для нажатий «Alt + Tab», «Ctrl + Alt + Del» и д.р. можете посмотреть в скетче. Вот и все, третья инкарнация DIY IP KVM"а готова.

Подведем итоги

Плюсы:

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

Минусы:

  • Необходимость в подключении и по Wi-Fi, и по Lan кабелю
  • При использовании с VGA необходим переходник, что естественно отразится на себестоимости

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

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

Благодарю за внимание. До новых встреч!

Mak2k2 4 октября 2017 в 17:41

IP KVM своими руками 3.0

  • Сетевое оборудование ,
  • DIY или Сделай сам
  • Tutorial

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

Для тех, кто только что присоединился, краткое содержание предыдущих серий:


  • Собирали IP KVM на Arduino и Raspberry Pi, получилось дорого и с плохим качеством видео.

  • OrangePI и Atmega16u2, поучилось дешево, но качество изображения всё также отвратительно.
И вот, наконец, в этой статье будут исправлены все минусы предыдущих. Особый упор будет сделан на максимальное снижение стоимости компонентов.

По традиции рассмотрим составные части собираемого устройства:

1. Наша старая знакомая Atmega16u2:


Это единственный компонент, который перейдет из предыдущих статей.

2. Небезызвестный ESP8266, в данном случае ESP8266-12e:


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

3. И, собственно, виновник торжества LKV373A

Благодаря этому устройству стало возможным передавать видео по локальной сети, с высоким
разрешением вплоть до full-HD.

План мероприятия таков:

  1. Игра в прятки: Ищем LKV373A куда он спрятался в сети под каким ip адресом
  2. Игра в хакера: Заливаем прошивку, сбрасываем пароли и настраиваем LKV373A
  3. Заводим новых друзей: ESP8266, Arduino IDE и веселые картинки
  4. Финал торжества. Подключаем все компоненты и передаем нажатия клавиш через telnet

LKV373A, предыстория

Итак, приступим! LKV373A или HDMI Extender устройство для захвата изображения прямо с порта hdmi и трансляции в локальную сеть, эти устройства еще называют hdmi удлинителями. По задумке производителей комплект подобных устройств должен состоять из transmitter (передатчика) и receiver (приемника), обозначение TX и RX соответственно. Использоваться они должны, вероятно, для того, чтобы приносить производителю больше прибыли, только в паре. Но нашелся человек, под ником Danman вот ссылка на его блог, которому стало интересно как это работает. Открыл он Wireshark, поснифал трафик передаваемый TX устройством, что же оказалось?

Видеопоток передается без какого-либо шифрования, и при помощи VLC Player"a можно посмотреть его без лишних усилий. Но на этом он не остановился, «пощупал»: и веб интерфейс, и TTL, и телнет, и даже прошивку программатором стянул. Об этом он в подробностях рассказал в своем блоге. Там же была выложена прошивка, которая интересует нас в первую очередь: IPTV_TX_PKG_v4_0_0_0_20160427.PKG. В этой прошивке веб интерфейс с расширенными настройками, а не как у стандартной только кнопка обновить. К тому же, в этой прошивке есть телнет с множеством команд для настройки. Именно при помощи этой прошивки мы перенастроим HDMI Extender под наши задачи. Прошивку и все необходимое я выложил на github вот ссылка , она понадобится нам позднее, а пока заканчиваем с теорией. Переходим к практике.

Передатчик

Ищем LKV373A в сети

Мне попался в руки такой же extender что и Danman(у). Все что будет изложено далее подходит именно для HDMI Extender LKV373A версии V3.0!

Подключаем LKV373A к локальной сети, включаем питание. Теперь попробуем удостовериться, что устройство видно в сети ping 192.168.1.238 .

192.168.1.238 это IP-адрес по умолчанию. Если на extender"ре старая прошивка адрес не изменится, независимо от того, присутствует DHCP сервер в сети или нет. Более новые версии прошивки используют IP по умолчанию только, если устройство не смогло получить адрес от DHCP. Если на ping запрос получили ответ, продолжаем дальше. Если нет, не отчаивайтесь, попробуйте подключить extender напрямую к LAN порту компьютера и воспользоваться сниффером.

Прошиваем

HDMI Extender нашли, перейдем к прошивке. Зайдем на гитхаб и скачаем все, что там выложено. Теперь откроем веб интерфейс extender"а через браузер и увидим такую картину:

Нажимаем «Browse…», выбираем прошивку, файл с названием IPTV_TX_PKG_v4_0_0_0_20160427.PKG, и нажимаем «Upgrade». Тадам! Прошивка завершена, теперь подключимся к LKV373A по телнету, чтобы сбросить пароль.

Команда для подключения будет выглядеть примерно так Telnet 192.168.1.238 9999, где 9999 - это порт для подключения. КЭП предупреждает: адрес полученный от DHCP можно узнать при помощи сканера сети.

Подключаемся через телнет

При подключении должно появиться следующее сообщение:

IPTV TX Server======== ============================== input>
Пишем list . В ответ получаем список команд:

IPTV TX Server======== ============================== input>list set_group_id get_group_id set_dhcp get_dhcp set_uart_baudrate get_uart_baudrate set_static_ip get_static_ip set_mac_address get_mac_address get_lan_status get_hdcp get_video_lock get_ip_config set_session_key set_device_name get_device_name set_video_bitrate get_video_bitrate set_downscale_mode get_downscale_mode set_video_out_mode get_video_out_mode set_streaming_mode get_streaming_mode get_fw_version get_company_id factory_reset reboot list exit
Для сброса всех настроек и пароля используем команду factory_reset . Пишем команду, нажимаем enter и получаем такую картину:

Input>factory_reset Processing factory reset! System will reboot after few seconds! Connection closed by foreign host.

Веб интерфейс

Теперь мы можем настраивать устройство как нужно нам. Зайдем в веб интерфейс. Используем стандартные логин:admin пароль:123456 и вот он, «желанный» веб интерфейс с дополнительными пунктами настройки:

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

Вот, устройство в сети нашли, прошивку «накатили», пароли сбросили. Передатчик почти готов перейдем к приемнику.

Приемник

Давайте определимся, на устройство, под управлением какой операционной системы, будем передавать поток. Муки выбора думаю испытывать не будете, варианта всего два:

Windows

Для виндовс попробовал несколько плееров, но результаты получились так себе. При захвате и последующем воспроизведении видеопотока появляется задержка, которая в первую очередь зависит от плеера воспроизводящего поток. На различных плеерах задержка варьировалась от одной секунды, до пяти и даже больше. Лучше всех, на виндовс, показал себя VLC Player.

Перейдем к делу. Запустим VLC Player, сверху в выпадающем меню «Медиа» выбираем пункт «Открыть URL…» в поле сетевого адреса пишем udp://@:5004 , ставим галку в чекбоксе «Показать дополнительные параметры» и в поле «Кэширование» ставим свое значение, этот параметр определяется индивидуально. Чем меньше значение в этом поле, тем меньше задержка, но слишком малое значение может привести к «артефактам» и выпадениям кадров, все будет зависеть от инфраструктуры локальной сети. Лучшие результаты, которых смог добиться, это задержка около одной секунды. На линуксе же, результаты получились гораздо лучше около 200-300 миллисекунд.

Linux

Как показывает практика, лучшие результаты получаются при использовании связки программ socat и mplayer. На моем компьютере установлена Ubuntu, поэтому команда для установки socat будет выглядеть так:

Sudo apt-get install socat
Mplayer устанавливается аналогично:

Sudo apt-get install mplayer
Ну, и прислушаемся совету Danman"а:

Sudo iptables -t raw -A PREROUTING -p udp -m length --length 28 -j DROP
Эта команда нужна для того что бы убрать из потока так называемые «Zero Length UDP Packets» - пакеты нулевого размера, которые «засоряют» поток.

Выходим в прямой эфир!

Приемник готов, можно запускать трансляцию! Набираем в консоли принимающего компьютера ifconfig или ipconfig в зависимости от операционной системы, Запоминаем IP-адрес приемника и вернемся к веб интерфейсу передатчика. Откроем веб-интерфейс, если вдруг уже закрыли, введем логин, пароль и прямо в адресной строке браузера пишем:

Http://АйПиПередатчика/dev/info.cgi?action=streaminfo&udp=n&rtp=y&multicast=n&unicast=y&mcastaddr=АйПиПриемника&port=5004
Подставляем свои IP-адреса и нажимаем enter. Эта строка настроит HDMI Extender для трансляции захваченного видео на выбранный вами ip и выключит мультикаст.

Запускаем VLC в Windows. Или в терминале Linux пишем:

Socat UDP-RECV:5004 - | mplayer –
Абра кадабра! И вот наш, или не наш, рабочий стол в прямом эфире.

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

С передачей видео разобрались, переходим к удаленному управлению.

Передача управления

Выбор комплектующих

Т.к. стоимость HDMI Extender"а невысока, около 1800 рублей, а так же из-за комментариев что, мол, дороговато, выдвигаю лозунг: «Даешь IP KVM за 2000 рублей!». На верность этого утверждения будет сильно влиять курс рубля, но не будем о грустном, хочется верить в светлое будущее. Для достижения цели нам понадобятся очень дешевые элементы, мой выбор пал: на ESP8266 в качестве контроллера, и все того же Atmega(8/16/32)u2 в качестве исполнительного устройства.

Можно конечно рассмотреть и другие кандидатуры на роль исполнительного устройства(клавиатуры). Прошивка, эмулирующая клавиатуру, написана на библиотеке LUFA(вроде как). Эта библиотека может использоваться для всего семейства AVR с возможностью подключения к USB. Из этого следует, что в качестве эмулятора клавиатуры можно подобрать, возможно более дешевый, микроконтроллер. Это информация к размышлению, а сейчас продолжим.

Приобрести ESP8266 можно примерно за 90 рублей, Atmega(8/16/32)u2 примерно за 100 рублей, и даже дешевле, если брать небольшими партиями по 5, или больше штук. Конечно, нужны будут расходные материалы для обвязки микроконтроллеров, но стоимость их крайне мала, поэтому рассматривать их не буду.

ESP8266

Это чудо китайской промышленности не нуждается в представлении, посему скажу только, что в этом проекте я использовал версию ESP8266-12e. Использовать другие версии конечно можно, только нужно учитывать расположение портов, т.к. в данном варианте один из портов ESP8266 используется для включения питания на Atmega(8/16/32)u2, ниже будет указано на схеме.

Прошивка

Прошивка для ESP8266 написана в среде ArduinoIDE так что давайте скачаем свежую версию с сайта разработчика . Далее нужно добавить поддержку ESP8266 - самый простой способ можете найти по этой ссылке . На той же странице можно найти много полезной информации, например схема подключения к питанию и TTL. Для тех, кто не в курсе ESP8266 использует строго 3.3 вольта! Если не уверены в собственных силах, лучше использовать вариант платы адаптированный для USB, вроде NodeMCU:

Если все готово открываем ArduinoIDE и копируем мой скетч:

Скетч

#include #include #include #include #include #include #define MAX_SRV_CLIENTS 3 HIDKeyboard keyboard; const char* host = "esp8266"; const char* ssid = ""; const char* pass = ""; int rebootdev = 0; int modeswitch = 0; //номера портов, на разных реализациях ESP8266 могут быть разными #define Port1 15 #define Port2 14 #define Port3 12 #define Port4 4 #define Port5 5 //цвет кнопок String ColorB1; String ColorB2; String ColorB3; String ColorB4; String ColorB5; ESP8266WebServer server(80); WiFiClient serverClients; const char* serverIndex = "

BACK";//Update //Возврат на главную страницу void handleRedirect(){ String content = ""; server.send(200, "text/html", content); } //Страница авторизации WI-FI void handleLogin(){ String msg = ""; if (server.hasArg("SSID") && server.hasArg("PASSAP")){ if ((server.arg("SSID") != NULL) && (server.arg("PASSAP") != NULL)){ String header = "HTTP/1.1 301 OK\r\r\nLocation: /\r\nCache-Control: no-cache\r\n\r\n"; server.sendContent(header); String web_ssid = server.arg("SSID"); String web_pass = server.arg("PASSAP"); ssid = web_ssid.c_str();//перевод строки в массив символов в стиле C pass = web_pass.c_str(); Serial.println(); Serial.print("SSID "); Serial.println(ssid); Serial.print("Pass "); Serial.println(pass); WiFi.begin(ssid, pass); digitalWrite(LED_BUILTIN, LOW); ESP.reset(); return; } msg = "Wrong ssid/password! try again."; Serial.println("Login Failed"); } String content = "
Enter the access point name and password
";//страница ввода SSID и пароля content += "Name AP:
"; content += "Password:

"; content += "
" + msg + "
"; content += "Firmware update UPDATE"; server.send(200, "text/html", content); } void handleNotFound(){ String message = "File Not Found\n\n"; message += "URI: "; message += server.uri(); message += "\nMethod: "; message += (server.method() == HTTP_GET)?"GET":"POST"; message += "\nArguments: "; message += server.args(); message += "\n"; for (uint8_t i=0; i 0){ int StPort; if (digitalRead(UsePin) == 1) {//проверяет в каком состоянии находится порт digitalWrite(UsePin, LOW); StPort = 0; } else { digitalWrite(UsePin, HIGH); StPort = 1; } digitalWrite(LED_BUILTIN, HIGH);//индикация при получении данных Serial.print("Port "); Serial.print(UsePin); Serial.print("="); Serial.println(StPort); delay(500); digitalWrite(LED_BUILTIN, LOW); return(StPort); } return(-1); } //Ожидание подключения int clientConnect(int Seconds){ Serial.print("connection "); for (int i=0; i <= Seconds; i++){ WiFi.begin(ssid, pass); digitalWrite(LED_BUILTIN, LOW); delay(250); digitalWrite(LED_BUILTIN, HIGH); delay(250); Serial.print(" "); Serial.print("."); if (WiFi.status() == WL_CONNECTED) return(0); } return(1); Serial.println(); } void setup(void){ Serial.begin(115200); delay(1000); pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); //uint8_t i = 0; if (modeswitch == 0) WiFi.mode(WIFI_STA);//если переменная modeswitch = 0 преход в режим клиента Serial.println(); Serial.println(); if (clientConnect(30) != 0) modeswitch = 1;//старт подключения с ожиданием если не подключился переход в режим точки доступа if (modeswitch == 1){ Serial.println(""); Serial.println("WiFi switch AP mode"); //WiFi.mode(WIFI_AP_STA);Это для режима клиент + точка доступа. Закоментил потому что притормаживало WiFi.mode(WIFI_AP); WiFi.softAP("TD", "testtest"); Serial.print("AP mode ip adress "); Serial.println(WiFi.softAPIP()); digitalWrite(LED_BUILTIN, LOW); } if (modeswitch != 1){ WiFiServer server(23); Serial.println(); Serial.print("Client mod ip address: "); Serial.println(WiFi.localIP()); digitalWrite(LED_BUILTIN, LOW); } MDNS.begin(host); pinMode(Port1, OUTPUT); pinMode(Port2, OUTPUT); pinMode(Port3, OUTPUT); pinMode(Port4, OUTPUT); pinMode(Port5, OUTPUT); if (modeswitch == 1){ //Действия при переходе по ссылкам server.on("/", handleLogin);//Страница ввода логина(SSID) и пароля //Обновление прошивки server.on("/upload", HTTP_GET, (){ server.sendHeader("Connection", "close"); server.send(200, "text/html", serverIndex); }); server.on("/update", HTTP_POST, (){ server.sendHeader("Connection", "close"); int uperror = Update.hasError(); Serial.printf("UPERR %u\nRebooting...\n",Update.hasError()); if (uperror == 0) server.send(200, "text/html", "Firmware update successfully BACK"); else server.send(200, "text/html", "Update error BACK"); ESP.restart(); },(){ HTTPUpload& upload = server.upload(); if(upload.status == UPLOAD_FILE_START){ Serial.setDebugOutput(true); WiFiUDP::stopAll(); Serial.printf("Update: %s\n", upload.filename.c_str()); if (upload.filename == NULL) { Serial.printf("ERROR: zero file size"); server.send(200, "text/html", " zero file size BACK"); return(-1); } uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; if(!Update.begin(maxSketchSpace)){//start with max available size Update.printError(Serial); } } else if(upload.status == UPLOAD_FILE_WRITE){ if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){ Update.printError(Serial); } } else if(upload.status == UPLOAD_FILE_END){ if(Update.end(true)){ //true to set the size to the current progress Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); } else { Update.printError(Serial); } Serial.setDebugOutput(false); } yield(); }); //Управление портами server.on("/PoRt1", { controlPin(Port1); handleRedirect();//переход на главную страницу }); server.on("/PoRt2", { controlPin(Port2); handleRedirect(); }); server.on("/PoRt3", { controlPin(Port3); handleRedirect(); }); server.on("/PoRt4", { controlPin(Port4); handleRedirect(); }); server.on("/PoRt5", { controlPin(Port5); handleRedirect(); }); server.on("/reboot", { rebootdev = 1;//перезагрузка при переходе на главную страницу handleRedirect(); }); server.onNotFound(handleNotFound);//если нет такой страницы //server.begin(); MDNS.addService("http", "tcp", 80); Serial.println(); Serial.println("HTTP server started"); } server.begin(); } void loop(void){ uint8_t i; if (modeswitch == 1) server.handleClient(); delay(100); if (modeswitch != 1){ if (WiFi.status() != WL_CONNECTED) clientConnect(30); else { digitalWrite(5, HIGH);//включение питания ATMEGA16U2 digitalWrite(LED_BUILTIN, LOW); } } WiFiServer server(23); server.setNoDelay(true); server.begin(); keyboard.begin(); while(WiFi.status() == WL_CONNECTED) { if (server.hasClient()){ for(i = 0; i < MAX_SRV_CLIENTS; i++){ //find free/disconnected spot if (!serverClients[i] || !serverClients[i].connected()){ if(serverClients[i]) serverClients[i].stop(); serverClients[i] = server.available(); Serial.println("New client: "); Serial.print(i); continue; } } //no free/disconnected spot so reject WiFiClient serverClient = server.available(); serverClient.stop(); } //check clients for data for(i = 0; i < MAX_SRV_CLIENTS; i++){ if (serverClients[i] && serverClients[i].connected()){ if(serverClients[i].available()){ //get data from the telnet client and push it to the UART String bufkey; while(serverClients[i].available()) bufkey += (serverClients[i].read());//сбор данных в строковую переменную if (bufkey != 0) { bufkey = bufkey.substring(0, 8);//обрезка строки int key = bufkey.toInt();//перевод в целочисленные switch (key){ case 277980: keyboard.pressSpecialKey(F1); break; case 277981: keyboard.pressSpecialKey(F2); break; case 277982: keyboard.pressSpecialKey(F3); break; case 277983: keyboard.pressSpecialKey(F4); break; case 27914953: keyboard.pressSpecialKey(F5); break; case 27914955: keyboard.pressSpecialKey(F6); break; case 27914956: keyboard.pressSpecialKey(F7); break; case 27914957: keyboard.pressSpecialKey(F8); break; case 27915048: keyboard.pressSpecialKey(F9); break; case 27915049: keyboard.pressSpecialKey(F10); break; case 27915051: keyboard.pressSpecialKey(F11); break; case 27915052: keyboard.pressSpecialKey(F12); break; case 1310: keyboard.pressSpecialKey(ENTER); break; case 130: keyboard.pressSpecialKey(ENTER); break; case 27: keyboard.pressSpecialKey(ESCAPE); break; case 8: keyboard.pressSpecialKey(BACKSPACE); break; case 9: keyboard.pressSpecialKey(TAB); break; case 32: keyboard.pressSpecialKey(SPACEBAR); break; case 27915012: keyboard.pressSpecialKey(INSERT); break; case 27914912: keyboard.pressSpecialKey(HOME); break; case 27915312: keyboard.pressSpecialKey(PAGEUP); break; case 27915212: keyboard.pressSpecialKey(END); break; case 27915412: keyboard.pressSpecialKey(PAGEDOWN); break; case 279167: keyboard.pressSpecialKey(RIGHTARROW); break; case 279168: keyboard.pressSpecialKey(LEFTARROW); break; case 279166: keyboard.pressSpecialKey(DOWNARROW); break; case 279165: keyboard.pressSpecialKey(UPARROW); break; case 127: keyboard.pressSpecialKey(DELETE); break; case 27915112: keyboard.pressSpecialKey(DELETE); break; case 4: keyboard.pressSpecialKey((LCTRL | ALT), DELETE); break; //CTRL+ALT+DELETE нажать Ctrl + d case 6: keyboard.pressSpecialKey(ALT, F4); break; //alt+f4 нажать Ctrl + f case 19: keyboard.pressSpecialKey(ALT | SHIFT); break;//смена раскладки нажать Ctrl+s case 2: keyboard.pressSpecialKey(LCTRL | SHIFT); break;//смена раскладки нажать Ctrl+b //управление логическим состоянием портов case 17: controlPin(Port1); break;//Ctrl+q case 23: controlPin(Port2); break;//Ctrl+w case 5: controlPin(Port3); break;//Ctrl+e case 18: controlPin(Port4); break;//Ctrl+r case 20: controlPin(Port5); break;//Ctrl+t default: keyboard.pressKey(key); break;//нажать клавишу } keyboard.releaseKey();//отпустить клавишу Serial.print(" string: "); Serial.print(key);//для отладки Serial.print(" KEY: "); Serial.write(bufkey.toInt()); bufkey = "0";//на всякий случай обнулить } } } } //check UART for data if(Serial.available()){ size_t len = Serial.available(); uint8_t sbuf; Serial.readBytes(sbuf, len); //push UART data to all connected telnet clients for(i = 0; i < MAX_SRV_CLIENTS; i++){ if (serverClients[i] && serverClients[i].connected()){ serverClients[i].write(sbuf, len); delay(1); } } } } }


Подключим нужную библиотеку:

Выбираем вкладку «Скетч» → «Подключить библиотеку» → «Добавить.ZIP библиотеку» Выбираем библиотеку под названием «UNO-HIDKeyboard-Library-master(mod).zip», которую скачали с гитхаба. Компилируем и заливаем прошивку. Для загрузки прошивки подключаем ESP8266 TTL, выставляем нужный порт в ArduinoIDE. Настройки должны выглядеть примерно так:

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

Логика работы ESP8266 такова: При подаче питания микроконтроллер в течении примерно тридцати секунд пытается подключиться к Wi-Fi точке доступа:

Если подключение удалось : открывает 23 порт, к которому можно подключиться, используя telnet и передавать нажатия клавиш. Кроме клавиш можно передавать комбинации, построенные на «Ctrl + key» которые будут нажимать определенные сочетания. Например, если передать «Ctrl + d», на управляемом компьютере будет нажато сочетание CTRL+ALT+DELETE.

Также имеются комбинации для управления портами ESP8266, можно, например, подсоединить реле и при помощи комбинации «Ctrl+q» включать и отключать реле, тем самым удаленно включать и отключать управляемый компьютер. Эти и другие комбинации можете посмотреть в исходниках.

Если не удалось : ESP8266 переходит в режим точки доступа, с названием «TD», Паролем «testtest» и открывает небольшой веб интерфейс, доступный по адресу 192.168.4.1, в котором можно задать настройки для подключения по WI-FI.

Таким образом, устройство можно легко подключить к другой точке доступа. Да, тут скрывается ложка дегтя, для работы нашего IP KVM"а потребуется и LAN кабель и Wi-Fi. Такова будет плата за дешевизну устройства.

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

Подключение компонентов

Для правильного подключения компонентов прикладываю схему. Только учтите, это условная схема, она служит для наглядности и не претендует на звание идеальной. Каждый волен «обставить» её как ему нравится.

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

Есть, конечно, альтернатива схеме, но это вариант для богатых, или ленивых. И кстати этот вариант не отменяет вышеизложенное:

На снимке Arduino UNO без чипа Atmega328p подключенный к китайскому аналогу NodeMCU. Линия 3.3 вольта подключена к линии того же уровня напряжения на Arduino, а так же подключена земля и контакт GPIO2(ESP8266) к контакту TX на Arduino.

Финальная проверка

Подключаемся по телнету на 23 порт и проверяем работоспособность. Сделать это можно: На Windows командой telnet .

На Linux будет немного сложнее:

$ telnet 192.168.***.*** Trying 192.168.***.***... Connected to 192.168.***.***. Escape character is "^]". ^] telnet> l
Все готово, можем проверять работу созданного нами устройства. Напомню возможные комбинации для нажатий «Alt + Tab», «Ctrl + Alt + Del» и д.р. можете посмотреть в скетче. Вот и все, третья инкарнация DIY IP KVM"а готова.

Подведем итоги

Плюсы:

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

Минусы:

  • Необходимость в подключении и по Wi-Fi, и по Lan кабелю
  • При использовании с VGA необходим переходник, что естественно отразится на себестоимости
В общем и целом, устройство получилось достойное внимания, конечно, оно не конкурент серийным IP KVM"ам со всеми их «плюшками», но зато сильно выигрывает в цене. И для домашнего использования, а может и не только, вполне подходит.

Пользуясь случаем хотел поблагодарить пользователя

Если вы уже, длительное время пользуетесь персональным компьютером, то вам приходится периодически обновлять не только программное обеспечение, но и "железо", тоесть, сам компьютер. А что делать со старым? Особенно, если от него остается только системный блок (монитор "перебрался" к новому). Приобретенный десять лет назад "486DX33", который был тогда последним достижением техники, сейчас уже продать невозможно.

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

Устанавливать всю эту "архитектуру" на новый проблематично, - не хватает портов СОМ и LPT (в старом было три СОМ и два LPT), а с портами USB нового ничего из старой периферии работать не может. Вот и было решено сохранить два системных блока и две "мышки".

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

Приобретенный механический переключатель рабочих мест "Мaxtro" проработал не более месяца. Потом начались "выкрутасы" - то клавиатура неправильно работает, то на мониторе не все цвета. Приходится все-время его рукоятку как-то пододвигать, подталкивать, чтобы поймать момент, когда все будет работать нормально.

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

На удивление, "Мaxtro" переключал все контакты разъемов клавиатуры и монитора, потому и в нем было так много контактных. На самом деле, для таких переключений достаточно семи контактных групп, потому что все "GND" можно соединить вместе, а питание клавиатуры коммутировать двумя диодами. Каждое РЭС-22 имеет по четыре контактные группы, поэтому, даже остается одна из них, которую можно использовать для управления самими реле (триггерный эффект реле).

Принципиальная схема

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

Оно самоблокируется свободной контактной группой К1.4 и после окончания зарядки С2 останется включенным. В таком состоянии клавиатура и монитор переключатся на компьютер 2. В обесточенном состоянии клавиатура и монитор переключены на старый компьютер 1.

Рис. 1. Принципиальная схема коммутатора консолей.

Все детали собраны в железном корпусе от переключателя "Maxtro". "Мышки" используются отдельные для каждого из компьютеров. Питается переключатель от сетевого адаптера от неисправного принтера.

Проблема переключения принтера между двумя компьютерами решена иначе. Принтер (Samsung 1210) имеет два порта - LPT и USB. Порт LPT подключен к старому системному блоку, a USB - к новому.