Arduino: ИК-управление бытовой техникой (применение девайса)

    Задравствуй хабр! По отзывам понял, что в статье «Arduino: ИК-управление бытовой техникой» мало посвятил конечной цели устройства и как его применять на практике, по-этому займусь этим сейчас.
    Назначение нашего arduino-девайса обеспечить управление набором устройств, управляемых по ИК-каналу. На следующем рисунке изображен пример того как можно использовать данный девайс, сейчас пока ко мне не приехал Ethernet Shield опишу на примере USB соединения с сервером.
    Сразу скажу, что рисунок был сделан специально для пояснения возможностей устройства, дома у меня все гораздо проще, ибо управление происходит только телевизором, а качестве источника использую свой ПК он же сервер, как на втором рисунке.

    Предположим, у нас две комнаты и в каждой находятся устройства, а их марки и модели совпадают, соответственно наборы команд для телевизора, DVD-проигрывателя и спутникового ресивера в комнатах совпадают. Исходя из этого можно сделать вывод о необходимости использовать для каждого устройства свой выход Arduino.

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

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


    Излучающие диоды я использовал от компании Global Cache, диод в корпусе, как на рисунке ниже.

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

    Когда писал серверную часть, сильно морочиться не хотелось, поэтому было решено набросать на Delphi маленький и тупенький web-сервер со встроенным «монитором порта» (для передачи данных на arduino по виртуальному Com-порту), для этого использовались Indy idHTTPSever и CPort компоненты.
    На стороне сервера находится страничка index.html с файлами для нее (CSS, JS, графика и т. д.) и файл codes.txt в котором хранятся, коды для управления оборудованием. Коды можно хранить в двух форматах HEX и GlobalCache.
    В формате HEX, есть правило начинать команду с идентификатора 0000, мы будем считать это местом для манипуляции с количеством повторов кода и портом на который его необходимо послать.
    Количество повторов — это первые два символа переведенные из шестнадцатеричной в десятичную СС + 1
    т. е. 00 = 0 + 1 = 1 или 0A = 10 + 1 = 11;
    Адрес порта — это вторые два символа в которых просто скрыт номер порта arduino,
    например: 00 = digital port 0 или 0D = digital port 13;
    Остальные данные HEX кода изменять нет необходимости, оставляем как есть.
    Для кодов в GlobalCache-формате с этим чуть сложнее, сам формат выглядит так:
    sendir,{moduleaddress}:{connectoraddress},{ID},{frequency},{count},{offset},{on1},{off1}...
    Где:
    • {moduleaddress}:{connectoraddress} — именно это и должно являться номером цифрового порта на ардуино, для совместимости с устройствами GlobalCahce было решено пересчитывать эту пару значений в номер порта ардуино, это можно сделать по формуле:
      ArduinoPort = ({moduleaddress} * 3)-(3-{connectoraddress})-1;
      Например: 5:2 = (5*3)-(3-2)-1 = (15)-(1)-1 = digital port 13;
    • {ID} — Я игнорирую;
    • {frequency} — Частота в Гц;
    • {count} — Количество повторов (1-255);
    • {offset} — Опять игнорирую;
    • {on} и {off} — Это данные.

    Я использую преимущественно HEX-формат, но стоит заметить, что коды в формате GlabalCache короче почти в 2 раза и соответственно в 2 раза быстрее парсятся.
    Каждая команда должна находиться строго на отдельной строке, номер строки в файле используется в качестве указателя на конкретный код, у меня для удобства коды начинаются с цифр т.к. строки в файле номеруются с нуля, таким образом получается строка №0 — это код кнопки 0.

    Привожу в пример часть файла codes.txt с командами к своему Philips 47PLF4007:
    000D 0073 002A 000D 0061 0021 0010 0020 0010 0010 0010 0010 0030 0030 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0BDE 0061 0021 0010 0020 0010 0010 0010 0010 0030 0030 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0021//digit 0
    000D 0073 002A 000D 0061 0021 0010 0020 0010 0010 0010 0010 0010 0020 0020 000F 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0020 0BED 0061 0021 0010 0020 0010 0010 0010 0010 0010 0020 0020 000F 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0020 0021//digit 1
    000D 0073 0028 000D 0061 0021 0010 0020 0010 0010 0010 0010 0030 0030 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0020 001F 0010 0BDE 0061 0021 0010 0020 0010 0010 0010 0010 0030 0030 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0020 001F 0010 0021//digit 2
    ...
    
    000D 0073 0024 000D 0061 0021 0010 0020 0010 0010 0010 0010 0030 0030 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0020 001F 0020 001F 0020 001F 0010 0010 0010 0BDE 0061 0021 0010 0020 0010 0010 0010 0010 0030 0030 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0020 001F 0020 001F 0020 0020 0010 0010 0010 0021//home
    sendir,5:2,1,36000,1,1,97,31,16,30,16,16,16,16,16,30,32,14,16,16,16,16,16,16,16,16,16,16,16,16,16,16,32,14,16,30,16,16,32,14,16,30,16,16,16,720//list
    000D 0073 002A 0000 0061 0021 0010 0020 0010 0010 0010 0010 0010 0020 0020 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0020 000F 0010 0010 0010 0010 0010 0BEE 0061 0021 0010 0020 0010 0010 0010 0010 0010 0020 0020 000F 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0020 0010 0010 0010 0010 0010 0010 0021//info
    ...
    


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

    Сервер видит, что запрашиваемый файл начинается с «_», удаляет первый символ из названия и читает оставшееся число, далее отправляет устройству код находящийся на строке с номером этого числа.
    procedure TForm1.srvCommandGet(AThread: TIdPeerThread;
      ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    var
      S,codeStr:String;
      i:Cardinal;
    begin
      S:=ARequestInfo.Document;
      Delete(S,1,1);
      if S='' then S:=ExtractFilePath(ParamStr(0))+'webdata\index.html'
        else begin
          if (S[1]='_') then
            begin
              Delete(S,1,1);
              codeStr:=SL.Strings[StrToInt(S)];
              Delete(codeStr,pos('/',codeStr),255);
              if ComPort1.Connected then
                ComPort1.WriteStr(codeStr+#13);
              S:='ok.js';
            end;
          S:=StringReplace(S,'/','\',[rfReplaceAll]);
          S:=ExtractFilePath(ParamStr(0))+'webdata\'+S;
        end;
      try
        AResponseInfo.ContentType:=ARequestInfo.ContentType;
        AResponseInfo.ContentStream:=TFileStream.Create(S,0);
      except end;
    end;
    

    Для клиента я написал несколько функций отвечающих за обмен данными с сервером. Что бы запросить у сервера выполнение команды, достаточно добавить на страницу новый тэг с атрибутом scr, так мы и поступим:
    var boolSendRepeat = false;
    function addScript(src){
    	var element = document.createElement('script');
    	element.type = 'text/javascript';
    	element.src = src;
    	element.className = 'bufferresponsescript';
    	document.getElementsByTagName('head')[0].appendChild(element);
    }
    		
    function delScripts(){
    	var z = document.getElementsByClassName('bufferresponsescript');
    	for(var i=z.length;i>0;i--){
    		z[i-1].parentNode.removeChild(z[i-1]);
    	}
    }
    
    function sendCommand(cmdNumber){
    	addScript('_'+cmdNumber);
    }
    
    function sendMacro(arrCmdNumber,arrDelays){
    	var s='';
    	for(var i=0;i<arrCmdNumber.length;i++){
    		s = 'sendCommand('+arrCmdNumber[i]+')';
    		setTimeout(s,arrDelays[i]);
    	}
    }
    
    function repeatFunc(cmdNumber,repeatTime){
    	var s = 'repeatFunc('+cmdNumber+','+repeatTime+')';
    	if(boolSendRepeat==true){
    		sendCommand(cmdNumber);
    		setTimeout(s,repeatTime);
    	}
    }
    
    function repeatStop(){
    	boolSendRepeat=false;
    }
    function sendRepeat(cmdNumber,repeatTime){
    	boolSendRepeat=true;
    	repeatFunc(cmdNumber,repeatTime);
    }
    

    Применить в интерфейсе это можно вот так:
    	<td onclick="sendCommand(32)">List</td><!--послать команду, где 32 номер строки в файле codes.txt-->
    	<td onclick="sendMacro([46,43],[10,550])">Компьютер</td><!--послать команду из строки 46, а через 550 миллисекунд послать команду 43-->
    	<td onmousedown="sendRepeat(12,800)" onmouseup="repeatStop()">Vol+</td><!--слать команду из строки 12, каждые 800 миллисекунд пока нажата кнопка-->
    

    Результат всего вышеперечисленного показан на следующих изображениях:

    Буквально вчера добавил в клиентский интерфейс прямое переключение на каналы эфирного телевидения, получилось симпатично:


    Надеюсь в данном посте мне удалось ответить на возникшие вопросы и раскрыть то, что в прошлой статье осталось «за кадром».

    Ссылки:
    Компонент CPort
    Файлы проекта
    PDF с описанием API устройств Global Cache
    Поделиться публикацией

    Комментарии 5

      0
      Впечатляет!
      1. Какая марка светодиода, который от компании Global Cache?
      2. С какими трудностями столкнулись при работе ИК-канала в квартире?
        +1
        1. марку диода не знаю, они в комплекте с устройствами идут и наглухо запаяны в пластик…
        2. Что-то не очень вопрос понял)
      • НЛО прилетело и опубликовало эту надпись здесь
          0
          Имхо, такое количество ИК-светодиодов — избыточно.

          В этом решении я вижу только два «плюса»:
          — передаем сигнал «напрямую» и не думаем о том, что сигнал может пойти куда-то не туда (или банально заслонили приемник и сигнал не дошел)
          — можно распараллелить команды сразу нескольким устройствам (включить ТВ, включить AV-ресивер, включить спутниковый ресивер)

          … если рядом находится более одного устройства реагирующего на посланную команду...

          Такая ситуация (по моему опыту) — крайне редко встречается (по моему опыту, если не путаю, только какие-то ТВ Панасоник реагировали на пульты Dune неадекватно, но как-то быстро это все решили), так что это — не аргумент.

          Но это все еще и обладает своими «минусами»:
          — приклеенные ИК-светодиоды на окошко ИК-приемника — мешают использовать обычные ИК-пульты
          — Ардуина, имхо, не сможет параллельно сразу несколько команд генерить.

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

            А про то что обычным ПДУ управлять мешает — это смотря как наклеить, у меня работает и родной ПДУ…

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

          Самое читаемое