Pull to refresh

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

Delphi *
Задравствуй хабр! По отзывам понял, что в статье «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
Tags:
Hubs:
Total votes 10: ↑8 and ↓2 +6
Views 36K
Comments Comments 5