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

Здравствуй хабрачитатель! Меня зовут Константин и я программист, а именно занимаюсь программированием систем «Умный дом».
За четыре года работы в этой области, довелось попробовать много интересных контроллеров и ПО для решения задач домашней автоматизации. Одними из интереснейших устройств с которыми мне приходилось иметь дело — это устройства компании Global Cache серии IP2IR. Их предназначение состоит в том что-бы принять текстовую команду от клиента и излучить ее через светодиод ик-спектра.
Использование подобных устройств упрощает жизнь пользователям сразу по нескольким направлениям:
  • в качестве ПДУ можно использовать планшеты, телефоны и ПК (с небольшой оговорочкой, нужен специальный софт);
  • управление можно централизовать т.е. специальный софт можно поднять на сервере и обращаться к нему;
  • в прицельной стрельбе нет необходимости, если с телевизором, например, таких проблем нет (мы в него и так смотрим), то вот для устройств вроде Blu-Ray проигрывателей или MediaServer'ов это может стать проблемой в случае если они заперты в шкафу или работают в режиме мультирум;
  • одна кнопка ПДУ может выполнить целый макрос, например включить телевизор и Blu-Ray, затем переключить ТВ на нужный источник, например HDMI1, и пользователь может вообще не париться о том где и как у него подключены устройства.

Такая игрушка жизнь конечно упрощает, но и по карману тоже бьет, если мне не изменяет память, то GlobalCache для управления тремя устройствами стоит порядка 6000-7000 деревянных рублей. Не очень гуманное ценообразование, вот и было решено сделать собственную реализацию подобного устройства.

Честно признаться, идея крутилась достаточно давно, но вот опыта схемотехника и программиста МК у меня нет, ну или почти нет, вообщем из этих соображений была выбрана платформа Arduino со своей недосредой разработки. Поиски на просторах паутины дали мне готовое решение под названием IRRemote, как же я плевался обнаружив, что этот модуль заточен на работу с одним единственным выходом, явно не то что мне нужно, за то просмотр исходника дал мне уверенность в своих теоретических знаниях ИК управления оборудованием.

Теория проста, хоть и производители любят использовать собственные протоколы для описания команд, все сводится к одному и тому же принципу передачи данных. На некоторых сайтах можно найти команды для своих устройств в формате HEX или ProntoHEX, что по сути одно и тоже.
Команда в HEX формате выглядит следующим образом: 0000 FREQ CNT1 CNT2 ON_1 OFF1 ON_2 OFF2 ON_n OFFn.
Суть такова:
  1. 0000 — всегда четыре ноля, но мы для своих целей будем использовать первые два нуля заменяем на количество повторов кода, вторые два нуля это будет номер выхода на ардуинке. Таким образом код 010D — повторит команду дважды и отправит ее на выход 13 (тот что со встроенным светодиодом);
  2. FREQ — опорная частота сигнала. Обычно в диапазоне 35-40кГц и записывается хитро — 36кГц = 0073 = 115, не очень понятно почему десятичное 115 дает частоту 36кГц, скажу лишь, что посчитать это можно так 4145 / FREQ;
  3. CNT1 — не самая обязательная часть, я ее игнорирую;
  4. CNT2 — тоже игнорирую, если кому интересно, что это ссылка внизу;
  5. ON — а вот это уже данные. Это количество периодов на опорной частоте когда мы «мерцаем» ик-диодом;
  6. OFF — а это количество периодов когда мы держим на ножке логический ноль.

Что бы не заставлять Вас вдумчиво вчитываться в эту теорию просто проиллюстрируем выше сказанное:
иллюстрация к теории ИК-сигнала
Ну когда с теорией разобрались, самое время приступить к программированию, не буду описывать все трудности которые возникли у меня в начале работы с новым для меня зверьком и начнем пожалуй со следующего:
В составе среды ардуино существует специальная функция tone(pin,frequency,duration), она генерирует на порту вход/выхода сигнал — прямоугольную «волну», заданной частоты и с 50% рабочим циклом, используя прерывания таймера.
Сварганив на скорую руку, более или менее, рабочий вариант используя эту функцию, захотелось увеличить точность получаемых результатов, для этого в каталоге "\Arduino\hardware\arduino\cores\arduino\" был скопирован файл Tone.cpp и переименован в IR_Gen.cpp. Затем в получившемся файле я объявил недостающие переменные и удалил лишние, да и такие там тоже были. Функция tone модифицировалась в функцию startIR(frequency) которая ни где не объявлена и использовать ее можно только внутри этого файла.
Так же добавились еще две объявленные функции sendIR(char ir_string[]) и sendLastIR() — которые и вызывают бывший tone() (startIR()), однако перед тем как запустить модуляцию необходимо убедиться в том, что строка переданная в sendIR является набором данных ик-команды, с этой целью была включена функция парсига ParseIRString, она вернет ноль если данные не соответствую формату ик-команды и вернет значение опорной частоты если данные соответствуют. Приведу код на всякий случай, что бы стало ясно, как происходит парсинг строки:
unsigned int ParseIRString(char IRString[]){
  byte xBool = 0;
  unsigned int bInt = 0;
  ir_data_array_length = 0;
  unsigned int IR_Frequency = 0;
  unsigned int IR_String_Length = 1024;
  char Separator = ' ';
  unsigned int i = 5;
  char bChArr[4] = {0};
  byte NumSys = 16;
  ir_out_pin = 255;
  if(IRString[0]=='s'){                                         //Если команда в формате Global Cache
        ir_out_pin = (((IRString[7]-48)*3)-(3-(IRString[9]-48)))-1;
	bChArr[0] = IRString[19];
	bChArr[1] = IRString[20];
	bChArr[2] = IRString[21];
	bChArr[3] = '\r';
	ir_repeat_count = StrToInt(bChArr,10)-1;
        Separator = ',';
        i = 13;
	NumSys = 10;
  }else{                                                             //Если команда в формате HEX
	bChArr[0] = IRString[3];
	bChArr[1] = IRString[4];
	bChArr[2] = '\r';
    ir_out_pin = StrToInt(bChArr,16);
	bChArr[0] = IRString[0];
	bChArr[1] = IRString[1];
    ir_repeat_count = StrToInt(bChArr,16);
  }
  if ((ir_out_pin>13)||(ir_out_pin<0)){return 0;}
  
  for (i; i<IR_String_Length; i++){ 
    if((IRString[i]>='a')&&(IRString[i]<='f')){IRString[i]-=32;}  //UpperCase
    if((IRString[i] != Separator)&&(IRString[i] != '\r')){             //Если число, а не сепаратор
      if((IRString[i]>47)&&(IRString[i]<58)){                             //Цифры в строке
        bInt = bInt * NumSys + (IRString[i] - 48);                      //Получение числа
      }else{
        if ((IRString[i]>='A')&&(IRString[i]<='F')&&(NumSys==16)){//буквы от A до F
          bInt = bInt * NumSys + (IRString[i] - 55);                      //Получение числа
        }else{
          return 0;
        }
      }
    }else{
      if(IR_Frequency==0){
        if(NumSys==16){                             //Если в HEX 
		  IR_Frequency = (4145/bInt)*1000; //считаем значение частоты
		}else{                            //если Global Cache
		  IR_Frequency = bInt;              //просто пишем частоту
		}
        xBool++;
      }else{
          if(xBool>2){
            ir_data_array[ir_data_array_length]=bInt*2;  //количество полупериодов
            ir_data_array_length++;
          }else{
            xBool++;
          }
      }
      bInt = 0;
      if(IRString[i] == '\r'){Serial.println(IR_Frequency,DEC); return IR_Frequency;}
    }  
  }
}

Затем исправляем прерывание таймера под наши цели и имеем в результате нечто вроде:
ISR(TIMER2_COMPA_vect){
  if (ir_data_array[ir_data_current_step] > 0){
    if(ir_data_current_step%2==0){
	  *timer2_pin_port ^= timer2_pin_mask;       //мерцаем
	}else{
	  *timer2_pin_port &= ~(timer2_pin_mask); //не мерцаем
	}
    ir_data_array[ir_data_current_step]--;
  }else{
    ir_data_array[ir_data_current_step] = ir_data_buffer;
    ir_data_current_step++;
    ir_data_buffer = ir_data_array[ir_data_current_step];
  }
  if (ir_data_current_step == ir_data_array_length){
    if(ir_repeat_count>0){
	  ir_repeat_count--;
	  ir_data_current_step = 0;
	}else{
          stopIR(tone_pins[0]);
	}
  }
}


В конце этой феерии в файле Arduino.h необходимо объявить новоиспеченные функции byte sendIR(char ir_string[]); byte sendLastIR(); и вуа-ля, дело в шляпе. Результатом стала чуть расширенная среда ардуино, в составе которой есть готовое средство для управления бытовой техникой.
Знаю, не все фанатеют от исправления чего бы то ни было в среде разработки, но мне честно сказать так было удобнее, да и в конечном счете ничего не мешает сделать это просто сторонним модулем.

Информация по теме:
Сайт компании Global Cache
PDF с описанием API устройств Global Cache
Русскоязычная справка по функции tone
Как и обещал для любознательных, доп. инфа по ИК-управлению
Всем известная библиотека IRRemote

Similar posts

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 8

    +4
    Статья интересная, но не все раскрыто до конца. Хочется картинок с примерами применения.
    Насколько я понял, любым своим пультом можно сделать управление практически всеми устройствами, имеющими ИК-канал управления.
    Ваш блок, который с Arduino, он установлен в комнате и управляет всем, что есть в этом помещении?

    Если нужно организовать управление по всему дому (несколько помещений), то в каждую комнату необходимо по одному такому блоку, связанных каналами связи, или достаточно к этому блоку подключить несколько приемных фото- и излучающих диодов?
    Опишите возможность расширения системы.
    +1
    Вот вообще не врубился, что вы написали, сперва ангажируем Arduino, открываешь, там оды Global Cache, но дорог собака, поэтому мы все таки возьмемся строчать код на Arduino.
    Подходим к коду, почему-то опять встречаем проверку а не код ли в Global Cache формате? Его можно взять только из него же самого или злонамеренно сконвертировать малость кривым их конвертером (http://www.globalcache.com/files/software/iConvert.exe).

    Зачем в парсинге долго заполнять bChArr, потом ir_out_pin и ir_repeat_count который ни где не используется?

    Зачем надерганы куски кода, а нет цельного проекта?

    Да и зачем это все, если есть IRRemote и овер 200 ее форков с блекджеком и прелестями?
      0
      ir_repeat_count — используется в прерываниях, что бы одним посылом команды можно было воспроизвести код несколько раз.
      ir_out_pin — при парсинге хранит номер пина на который надо отправить команду.
      Коды для устройств можно использовать как в формате HEX, так и в формате Global Cache (в нем коды короче — парсинг быстрее)
      Цельный проект выложил в продолжении
      0
      Интересно, а у вас работа — программировать умные дома? :-) Или вы «занимаетесь» этим на досуге?
        0
        Работаю программистом в инсталяционной компании, занимаюсь в основном интеграцией различных домашних систем (Домашние кинотеатры, вентиляция, пожарно-охранная сигнализация и т.д.) с системами управления, например от компании AMX.
        0
        Нет, это просто жесть. Точно из вас схемотехник никакой. Ведь достаточно было бы применить демультиплексор на нужное количество выходов и готовый код всего лишь для одного выхода. Одна маленькая дополнительная железка и так резко упрощает программную часть…
          0
          В своё время баловались с другом с Arduino и занимались разбором данных с ИК-датчика. В большинстве инфракрасных пультов используется протокол NEC, к слову. Очень забавно, что вы реализовали управление с помощью кода генерации звука.
          Кстати, сделав такую же простую схемку, которая ловит команды с пультов, можно очень быстро составить базу команд всей домашней техники и реализовать управление с Arduino.
          Баловство с IR. Бессмысленное и беспощадное.

          Only users with full accounts can post comments. Log in, please.