Седат Игдеджи, "Бекир Игдеджи", "Лидия Игдеджи", "Елизавета Игдеджи", Седат Бекирович Игдеджи, Лидия Седатовна Игдеджи, Бекир Седатович Игдеджи, Елиховета Седатовна Игдеджи, Cedat Igdedji, Lidya Igdedji, Elizaveta Igdedji, Bekir Igdedji, Sedat Igdedzhi, Lidya Igdedzhi, Elizaveta Igdedzhi, Bekir Igdedzhi
Технологии

Управляем Windows пультом от телевизора или как передать сигналы через последовательный порт

Pinterest LinkedIn Tumblr
Посмотреть оригинал

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

Идея есть, железо есть, а вот теория хромает. Как заставить компьютер понимать инфракрасные сигналы пульта и выполнять требуемые действия? Я надумал использовать ардуино для приема сигналов пульта через инфракрасный датчик на макетной плате и посылать сообщения в ноутбук через USB. Для этого требовались хоть какие то познания, как все это работает.

Было решено разобраться.

Знакомим ардуино с пультом

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

6ced764199729bd85eb11904809ece76 - Управляем Windows пультом от телевизора или как передать сигналы через последовательный порт
967a7619c314ea05ed2fb7dd9943da8e - Управляем Windows пультом от телевизора или как передать сигналы через последовательный порт
5d06c9530bd0d5ee2b99c197a5dce315 - Управляем Windows пультом от телевизора или как передать сигналы через последовательный порт
кодировка сигнала по протоколу NEC

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

Сама библиотека

Моим желанием было научиться менять громкость звука компьютера и управлять медиа (пауза/ переключение треков). Для этого необходимо 5 кнопок пульта.

Для того, чтобы понять, какую информацию нам передает пульт, необходимо воспользоваться командой IrReceiver.decodedIRData.decodedRawData. На мониторе порта мы увидим подробную информацию о том, что содержит сигнал. Здесь нас интересует значение команд. Каждая кнопка пульта содержит свою команду, их мы и будем использовать для управления медиа. Прощёлкав все интересующие нас кнопки и записав коды команд, мы можем написать следующее:

#include <IRremote.h>
int IR_RECEIVE_PIN = 2; // Получаем сигнал на 2-ой пин
long command;

void setup()
{
  Serial.begin(9600);
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); 
}

void loop() {
  if (IrReceiver.decode()) // Расшифровываем последовательность сигналов пульта
  {
    command = IrReceiver.decodedIRData.decodedRawData; /* Проверяем какие комманды пульта 
    																											соответствуют нужным нам кнопкамм*/
    switch(command) //Задаем действия в соответствии с полученным провеяремым сигналом
    {
      case 0xEA15FF00:
        Serial.write("D"); delay(120);
      break;

      case 0xB946FF00:
        Serial.write("U"); delay(120);
      break;

      case 0xBF40FF00:
        Serial.write("P"); delay(120);
      break;

      case 0xBC43FF00:
        Serial.write("N"); delay(120);
      break;

      case 0xBB44FF00:
        Serial.write("R"); delay(120);
      break;
    }
    
    IrReceiver.resume(); // Продолжаем получать сигналы
  }
}

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

Управление воспроизведением и громкостью Windows

Как управлять громкостью и медиа Windows, я нашел в этом посте.

Управление можно осуществлять с помощью виртуальных кодов — имитации действий клавиатурой и мышью. Я использовал С++ и Visual Studio так как там есть удобная для этой задачи библиотека Windows.h

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

 INPUT Input = { 0 };
 Input.type = INPUT_KEYBOARD;
 Input.ki.wVk = VK_VOLUME_UP; /* Пишем здесь нужный нам виртуальный код, 
 																который мы хотим имитировать*/
 SendInput(1, &Input, sizeof(Input));
 ZeroMemory(&Input, sizeof(Input));

Нас интересуют следующие коды: увеличение и уменьшение громкости (VK_VOLUME_UP, VK_VOLUME_DOWN); проигрывание и пауза медиа (VK_MEDIA_PLAY_PAUSE); «перелистывание» медиа (VK_MEDIA_NEXT_TRACK, VK_MEDIA_PREV_TRACK)

Полный набор виртуальных кодов доступен здесь.

Что такое Serial port и как с этим бороться?

Ардуино уно передает сигналы компьютеру через USB, эмулируя последовательный порт (Serial port), который в Windows называется COM порт, как дань памяти старым последовательным портам IBM PC. Для того, чтобы получить сообщение от ардуино и выполнить какое то действие в зависимости от сообщения, необходима программа. Функции работы с последовательным портом также есть в библиотеке Windows.h

#include <Windows.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    HANDLE Port;
    BOOL Status;
    DCB dcbSerialParams = { 0 };
    COMMTIMEOUTS timeouts = { 0 };
    DWORD dwEventMask;
    char ReadData;
    DWORD NoBytesRead;
    bool Esc = FALSE;

    Port = CreateFile(L"\.COM3", GENERIC_READ, 0, NULL,  // Открываем последовательный порт
    OPEN_EXISTING, 0, NULL);

    if (Port == INVALID_HANDLE_VALUE)
    {
        printf("nError to Get the COM staten");
        CloseHandle(Port);
    }
    else
    {
   
        printf("nopening serial port is succesfuln");

    }

    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

    Status = GetCommState(Port, &dcbSerialParams); 			// Принимаем существующие настройки порта
    if (Status == FALSE)
    {
        printf("n Error to Get the COM state n");
        CloseHandle(Port);
    }

    dcbSerialParams.BaudRate = CBR_9600;									// Задаем настройки порта
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;

    Status = SetCommState(Port, &dcbSerialParams);   
  
    if (Status == FALSE)
    {
        printf("n Error to Setting DCB Structure n ");
        CloseHandle(Port);
    }

    timeouts.ReadIntervalTimeout = 10;						/* Задаем временные интервалы приема сигналов
   																								с порта (я их от балды поставил) */
   timeouts.ReadTotalTimeoutConstant = 200;				
    timeouts.ReadTotalTimeoutMultiplier = 2;

    if (SetCommTimeouts(Port, &timeouts) == FALSE)
    {
        printf("n Error to Setting Timeouts");
        CloseHandle(Port);
    }

        while (Esc == FALSE)
        {
          
            Status = SetCommMask(Port, EV_RXCHAR);

            if (Status == FALSE)
            {
                printf("nError to in Setting CommMaskn");
                CloseHandle(Port);
            }

            Status = WaitCommEvent(Port, &dwEventMask, NULL); 		/* Задаем ожидание события 
          																												(поступления сообщения в порт) */
            if (Status == FALSE)
            {
                printf("nError! in Setting WaitCommEvent () n");
                CloseHandle(Port);
            }


            Status = ReadFile(Port, &ReadData, 3, &NoBytesRead, NULL); // Считываем сообщение 


            printf("nNumber of bytes received = % dnn", sizeof(ReadData) - 1);

            switch (ReadData)																/* В зависимости от сообщения
            																									симулируем нажатие медиа клавиш */
            {
                case 'U':
                {
                    INPUT Input = { 0 };
                    Input.type = INPUT_KEYBOARD;
                    Input.ki.wVk = VK_VOLUME_UP;
                    SendInput(1, &Input, sizeof(Input));
                    ZeroMemory(&Input, sizeof(Input));
                }
                break;

                case 'D':
                {
                    INPUT Input = { 0 };
                    Input.type = INPUT_KEYBOARD;
                    Input.ki.wVk = VK_VOLUME_DOWN;
                    SendInput(1, &Input, sizeof(Input));
                    ZeroMemory(&Input, sizeof(Input));
                }
                break;

                case 'P':
                {
                    INPUT Input = { 0 };
                    Input.type = INPUT_KEYBOARD;
                    Input.ki.wVk = VK_MEDIA_PLAY_PAUSE;
                    SendInput(1, &Input, sizeof(Input));
                    ZeroMemory(&Input, sizeof(Input));
                }
                break;

                case 'N':
                {
                    INPUT Input = { 0 };
                    Input.type = INPUT_KEYBOARD;
                    Input.ki.wVk = VK_MEDIA_NEXT_TRACK;
                    SendInput(1, &Input, sizeof(Input));
                    ZeroMemory(&Input, sizeof(Input));
                }
                break;

                case 'R':
                {
                    INPUT Input = { 0 };
                    Input.type = INPUT_KEYBOARD;
                    Input.ki.wVk = VK_MEDIA_PREV_TRACK;
                    SendInput(1, &Input, sizeof(Input));
                    ZeroMemory(&Input, sizeof(Input));
                }
                break;

                default:
                    printf("n Errorn");
                    break;
            }
          

            PurgeComm(Port, PURGE_RXCLEAR);    // Очищаем порт от всякого мусора

        }


    CloseHandle(Port);  /* Закрываем порт при завершении работы программы,
    										чтобы дргуие программы могли получить к нему доступ */

}

Информацию по работе с последовательными порта я нашел здесь.

https://www.xanthium.in/Serial-Port-Programming-using-Win32-API

http://citforum.ru/hardware/articles/comports/

Итог

Теперь моей лени нет предела, мне не нужно дотягиваться до ноутбука, чтобы переключить трек или ролик на ютубе. Не зря говорят: Лень — двигатель прогресса.

У подобной комбинации (пульт + виртуальные коды) есть потенциал в управлении разными частями ОС. Например, можно назначить на кнопки запуск программ или сделать из пульта что-то вроде контроллера. Но самое удобное, на мой взгляд, это управление медиа.

Седат Игдеджи, "Бекир Игдеджи", "Лидия Игдеджи", "Елизавета Игдеджи", Седат Бекирович Игдеджи, Лидия Седатовна Игдеджи, Бекир Седатович Игдеджи, Елиховета Седатовна Игдеджи, Cedat Igdedji, Lidya Igdedji, Elizaveta Igdedji, Bekir Igdedji, Sedat Igdedzhi, Lidya Igdedzhi, Elizaveta Igdedzhi, Bekir Igdedzhi