Pull to refresh

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

Reading time 16 min
Views 48K
Это уже третья вариация на тему 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 <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <SoftwareSerial.h>
#include <HIDKeyboard.h>

#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[MAX_SRV_CLIENTS];

const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form><a href='/'>BACK</a>";//Update

//Возврат на главную страницу
void handleRedirect(){
  String content = "<html><head><meta http-equiv='refresh' content='0;/'><head></html>";
  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 = "<html><body><form action='/' method='POST'>Enter the access point name and password <br>";//страница ввода SSID и пароля
  content += "Name AP:<input type='text' name='SSID' placeholder='SSID'><br>";
  content += "Password:<input type='password' name='PASSAP' placeholder='password'><br><br>";
  content += "<input type='submit' name='SUBMIT' value='Connect to WI-FI'></form><b><font color='red'>" + msg + "</font></b><br>";
  content += "Firmware update <a href='/upload'>UPDATE</a></body></html>";
  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<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}

//Управления портами
int controlPin(int UsePin){
  if (UsePin > 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 <a href='/'>BACK</a>");
      else server.send(200, "text/html", "Update error <a href='/'>BACK</a>");
      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", "<html> zero file size <a href='/upload'>BACK</a></html>");
            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[len];
    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 [ip-адрес ESP8266].

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

Команда telnet [ip-адрес ESP8266] далее нужно нажать управляющий символ, по умолчанию «Ctrl + ]», телнет должен перейти в командный режим, далее нажимаем «l» и «Enter». Этими действиями переключим телнет в символьный режим.

$ 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! Этот добрый человек подсказал верное направление, в котором нужно вести раскопки.

Благодарю за внимание. До новых встреч!
Tags:
Hubs:
+20
Comments 17
Comments Comments 17

Articles