Рано или поздно любой ардуинщик приходит к идее автоматизировать управление домашним медиацентром или кондиционером. И здесь начинаются грабли: один протокол шлет повторы, другой требует тройной отправки, третий — инверсии контрольного бита.

Разбираемся с библиотекой IRremote — мощным инструментом, который берет на себя всю низкоуровневую магию NEC, Sony SIRC и Philips RC5/6 и прочих, да еще оставляет пространство для тюнинга. От захвата «сырых» данных до эмуляции редких или «безымянных» пультов — создаем по‑настоящему обучаемый контроллер.

Содержание
Как отправить
Как получить
Настройка
Коротко о структуре ИК‑команд
Работа с «сырыми» данными
Считываем коды нашего пульта
Как работает прием
Как работает отправка
Добавление новых протоколов
Поиск неисправностей
В заключение

Для работы проще всего воспользоваться менеджером библиотек Arduino IDE. Исходники лежат на GitHub. Код не модифицировался много лет, но продолжает поддерживаться автором — он просит сообщать о проблемах.

Как отправить

Архитектура библиотеки разделена на две части: IRsend отвечает за передачу ИК‑пакетов, а IRrecv берет на себя прием и декодирование сообщений. Передатчик использует ИК‑светодиод, подключенный к третьему пину. Чтобы отправить команду, достаточно вызвать метод send для выбранного протокола, указав сами данные и их разрядность. Самый простой пример такой отправки — в examples/IRsendDemo:

#include <IRremote.h>
IRsend irsend;

void setup()
{
  Serial.begin(9600);
}

void loop() {
  if (Serial.read() != -1) {
    for (int i = 0; i < 3; i++) {
      irsend.sendSony(0xa90, 12); // Код управления питанием телевизора Sony
      задержка(100);
    }
  }
}

Здесь Arduino превращается в пульт управления: стоит отправить любой символ в последовательный порт — и микроконтроллер отреагирует ИК‑кодом включения или выключения телевизора Sony. Учтите нюанс протокола: для Sony команды необходимо передавать трижды, иначе устройство их просто проигнорирует.

Как получить

Для приема сигналов IRrecv задействует ИК‑детектор, который можно подключить к любому цифровому пину. Схема соединения и базовый принцип работы с входящими кодами наглядно разобраны в примере examples/IRrecvDemo:

#include <IRremote.h>

int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results результаты;

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // Запуск приемника
}

void loop() {
  if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX);
    irrecv.resume(); // Получить следующее значение
  }
}

IRrecv инициализируется командой enableIRIn() и берет на себя все заботы по декодированию. Чтобы проверить, пришел ли новый сигнал, используется метод decode(). Если ИК‑пакет успешно принят, функция вернет ненулевое значение и сохранит данные в структуре decode_results — ее подробное устройство можно изучить в examples/IRrecvDump.

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

30+ бесплатных курсов на IT‑темы в Академии Selectel

Для начинающих и опытных специалистов.

Изучить →

Настройка

Для работы подойдет любой цифровой пин Arduino, способный принять сигнал от ИК‑приемника на 38 кГц. Библиотека отлично себя показала с модулями Radio Shack 276‑640 и Panasonic PNA4602. Подключение стандартное: первый контакт — к питанию, второй — к земле, а третий (выход) — к цифровому входу, например, к 11‑му пину.

Важно
Обычные фотодиоды или фототранзисторы здесь не справятся. Нужен именно специализированный приемник. Он сам фильтрует помехи и выдает на выходе чистый демодулированный сигнал в инвертированной логике. Такие детекторы видят на приличном расстоянии и уверенно покрывают всю комнату.

Для вывода сигнала ИК‑светодиод подключается через токоограничивающий резистор к третьему пину (PWM). Важно не перепутать полярность: длинная ножка светодиода — это анод. В тестовой сборке с ИК‑диодом NTE 3027 и резистором на 100 Ом дальность передачи составила около 4,5 метров. Если этого мало и нужно «пробивать» дальше, придется усилить сигнал, добавив выходной каскад с транзистором.

Коротко о структуре ИК‑команд

ИК‑пульт передает команды, включая и выключая светодиод в строго определенной последовательности. Чтобы солнечный свет или комнатные лампы не создавали помех, сигнал модулируют — светодиод вспыхивает не постоянно, а с высокой частотой (обычно 36, 38 или 40 кГц). Состояние, когда идет модулированный сигнал, называют «меткой», а период бездействия светодиода — «паузой».

При нажатии кнопки пульт передает уникальный код длиной от 12 до 32 бит. Если удерживать клавишу, большинство пультов просто повторяют эту посылку. Однако протокол NEC работает иначе: вместо дублирования основного кода он транслирует короткую специальную метку повтора. В пультах Philips RC5 и RC6 используется другой механизм — контрольный бит, который меняет состояние при каждом новом нажатии. Так приемник понимает, держит ли пользователь кнопку или нажал на нее несколько раз подряд.

Для тех, кто хочет глубже зарыться в теорию и изучить структуру ИК‑кодов разных производителей, пригодится база знаний SB IR — лучший справочник из всех, что попадались.

Приемник демодулирует входящий поток и превращает его в обычный логический сигнал: есть несущая частота — на выходе «единица», нет — «ноль». В идеале частоты передатчика и детектора должны совпадать, но на практике небольшое расхождение не критично.

Работа с «сырыми» данными

Библиотека умеет работать с «сырыми» данными, просто опираясь на длительность передаваемых и принимаемых импульсов. В первую очередь это нужно для отладки, но пригодится и в других случаях — например, если нужный протокол не поддерживается или нужен вообще уникальный.

Измеряется длительность меток и пауз с шагом в 50 мкс. Отсчет начинается с первой паузы — промежутка перед началом передачи, а завершается финальной меткой.

Для отправки ИК‑сигнала в сыром формате используются значения длительности меток и пауз, выраженные напрямую в микросекундах. Массив данных открывается первой меткой и завершается последней.

Форматы буферов для приема и отправки различаются в двух деталях. При отправке используются микросекунды, а для приема — такты по 50 мкс. Кроме того, буфер отправки открывается первой «меткой», тогда как буфер приема фиксирует еще и паузу перед началом сигнала.

Разница в точности обоснована технически. При чтении для уверенного декодирования вполне достаточно шага в 50 мкс. Заодно промежутки защищены от переполнения. А вот при передаче погрешность может запросто превысить 10%, что критично для многих устройств. По этой причине для исходящих сигналов выбрана микросекундная точность.

Считываем коды нашего пульта

Простейший способ получить рабочие коды — заставить саму библиотеку считать и расшифровать сигналы с пульта.

В сети полно баз ИК‑кодов, но многие из них закрыты или используют проприетарные форматы. Если нужно открытое решение, то пригодится проект LIRC (Linux Infrared Remote Control) — там собраны описания команд для сотен устройств. Если какой‑то конкретной модели нет в списке, то не страшно — производители часто используют одну и ту же систему кодирования для всей линейки своих продуктов.

Нужно иметь в виду: разные справочники могут трактовать одни и те же протоколы по‑своему. Кто‑то меняет порядок бит, кто‑то инвертирует логические уровни, а кто‑то по‑разному учитывает стартовые и стоповые биты. Если данные из библиотеки не совпадают со значениями из других источников — причина, скорее всего, именно в этих тонкостях интерпретации.

Как работает прием

IRrecv, приемная часть библиотеки, работает в два этапа. Сначала прерывание каждые 50 мкс измеряет длительность «меток» и «пауз», аккуратно складывает результаты в буфер. В какой‑то момент пользователь вызывает подпрограмму декодирования. Она берет накопленные замеры и превращает их в готовое число — тот самый код длиной от 11 до 32 бит.

Алгоритм поочередно прогоняет сигнал через декодеры разных протоколов и останавливается, как только находит подходящий. На выходе получается структура данных, в которой хранятся:

  • «сырые» данные,

  • итоговый декодированный код,

  • его разрядность,

  • название взятого протокола.

Для декодирования используется макрос MATCH. Он проверяет, укладывается ли измеренная длительность «метки» или «паузы» в допустимый диапазон ожидаемого времени.

Декодирование RC5/6 устроено иначе. Значение бита определяет не длительность импульса, а последовательность «метка + пауза» или «пауза + метка». Чтобы разобраться с такой логикой, используется вспомогательный метод getRClevel — он анализирует временны́е интервалы и определяет логический уровень сигнала для каждого конкретного такта.

При удержании кнопки декодер будет раз за разом выдавать одно и то же значение. Исключение — протокол NEC, который вместо дублирования основного кода отправляет короткую специфическую метку. В этом случае процедура декодирования вернет константу REPEAT.

Немного технических подробностей

Обработчик прерываний срабатывает каждый раз, когда переполняется таймер TIMER1, настроенный на интервал в 50 мкс. При каждом таком «тике» система проверяет состояние входного пина и инкрементирует счетчик.

Задача подпрограммы — измерить чистую длительность «меток» (когда идет модулированный сигнал) и «пауз» (когда сигнала нет), после чего сохранить эти значения в буфер.

Запись всегда начинается с предварительной паузы перед пакетом данных, а далее следуют чередующиеся замеры импульсов и промежутков. Все данные в буфере хранятся не в секундах, а в количестве тех самых 50‑микросекундных «тиков».

Обработчик прерываний реализован как конечный автомат. В режиме ожидания он находится в состоянии STATE_IDLE. Как только фиксируется первая «метка», система переключается в STATE_MARK для замера ее длительности. Далее автомат циклично переходит между STATE_MARK и STATE_SPACE, фиксируя каждый импульс и промежуток. Если же пауза затягивается и превышает установленный порог, активируется состояние STATE_STOP — сигнал о том, что пакет данных принят полностью. В этом режиме прерывание продолжает отсчитывать время, но новые данные в буфер не записываются до сброса флага.

Флаг STATE_STOP служит сигналом для декодера: «Пакет принят полностью, можно обрабатывать». Как только работа с данными окончена, необходимо вызвать метод resume() — он вернет систему в состояние STATE_IDLE, разрешая прерыванию записывать следующую команду.

Стоит учесть пару важных моментов:

  • Подсчет пауз. Даже в состояниях STOP и IDLE таймер продолжает тикать — что позволяет библиотеке точно измерять интервалы между отдельными посылками.

  • Защита буфера. Если не вызвать resume() вовремя, новая передача будет просто проигнорирована. Такая логика «стоп‑старт» гарантирует, что буфер не перезапишется прямо посреди процесса декодирования — без этого отладка превратилась бы в кошмар из‑за постоянно меняющихся данных.

Как работает отправка

Логика отправки устроена довольно просто. Чтобы добиться точной несущей частоты и правильной скважности, используется аппаратный ШИМ‑таймер, а не обычные программные задержки.

На низком уровне это работает так.

  1. Функция enableIROut настраивает таймер для генерации ШИМ‑сигнала на 3‑м пине с нужной частотой (обычно 38 кГц).

  2. Метод mark() формирует «метку»: он включает ШИМ‑выход и удерживает его активным заданное время.

  3. Метод space() формирует «паузу»: он просто отключает ШИМ на указанный интервал.

IRremote работает с популярными протоколами, учитывая их специфику.

  • NEC — передает 32 бита, начиная со старшего (MSB).[описание]

  • Sony — обычно использует 12 или 20 бит (MSB). Стоит учесть, что в официальной документации Sony принят обратный порядок — младшим битом вперед (LSB), но библиотека приводит их к стандартному виду.[описание, объяснение]

  • RC5 — передает от 12 бит (MSB). Сообщение всегда открывается двумя стартовыми битами, которые не включаются в итоговое значение кода.[описание]

  • RC6 — стандартно передает 20 бит (MSB). Посылка начинается с ведущего импульса и стартового бита. Особенность протокола — четвертый бит (контрольный), который в два раза шире остальных.[описание]

Для протоколов Sony и RC5/6 стандарт требует троекратного повторения каждой посылки. Обратите внимание: библиотека не управляет «битом переключения» в автоматическом режиме — логику его изменения при каждом новом нажатии нужно реализовать в основном коде программы.

Добавление новых протоколов

Существует огромное количество протоколов, и библиотека поддерживает лишь часть из них. Однако архитектура позволяет легко добавить новый формат, опираясь на готовые примеры в коде.

Вот несколько советов по «реверс‑инжинирингу»:

  • ищите документацию — намного проще работать с готовым описанием таймингов, чем пытаться восстановить их с нуля по осциллограммам;

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

  • внимание к финалу — легко ошибиться в последнем бите: завершающая пауза часто бывает неявной и не фиксируется библиотекой как отдельное событие.

Поиск неисправностей

Для упрощения поиска ошибок в ИК‑связи в библиотеку встроен режим отладки. Чтобы его активировать, нужно поместить строку #define DEBUG в самое начало скетча — тогда подробная техническая информация будет выводиться прямо в консоль.

Важно
После добавления этой директивы может потребоваться принудительная перекомпиляция. Для этого должны быть удалены объектные файлы из папки сборки или перезапущена среда разработки.

Проблемы с передачей

Если передача не работает, первым делом надо проверить: а излучает ли что‑то ИК‑светодиод? Человеческий глаз не воспринимает инфракрасную часть спектра, но большинство камер мобильных телефонов видят ее отлично. Достаточно посмотреть на светодиод через экран смартфона — тот должен мигать фиолетовым или белым светом.

Также стоит попробовать поднести светодиод вплотную к приемнику. Без использования транзисторного усилителя дальность сигнала будет минимальной, так что для тестов «на столе» дистанция имеет решающее значение.

Другая распространенная проблема — приемник «не узнает» передатчик из‑за неверных данных или ошибки в выборе протокола. Самый надежный способ проверить это — взять оригинальный пульт от устройства и с помощью этой же библиотеки посмотреть, какие именно коды и в каком формате тот отправляет на самом деле.

Осциллограф — лучший способ наглядно увидеть, что именно передает Arduino или пульт. Чтобы поймать сигнал, подойдет обычный ИК‑фотодиод, подключенный напрямую к щупам осциллографа — его надо вплотную поднести к передатчику.

Если профессионального прибора под рукой нет — не беда. Можно воспользоваться программным осциллографом, работающим через звуковую карту компьютера — например, xoscope.

Выше уже отмечалось, что протоколы Sony и RC5/6 требуют, чтобы каждая команда отправлялась трижды. На практике же приемники часто игнорируют одиночную посылку, но срабатывают уже со второго раза.

Особое внимание — биту переключения в протоколах RC5/6. Его значение нужно менять программно при каждом новом нажатии кнопки. Если этого не делать, устройство решит, что вы просто удерживаете клавишу, и выполнит команду лишь один раз.

И последнее: в библиотеке все же могут встречаться ошибки. В частности, это касается протоколов RC5 и RC6 — на момент написания под рукой не нашлось подходящего приемника, чтобы протестировать их в реальных условиях.

Проблемы с приемом

Если прием сигнала не работает, первым надо проверить: видит ли Arduino хоть какой‑то входящий трафик. В библиотеку встроена простая индикация: при каждом получении ИК‑импульса должен мигать штатный светодиод на 13‑м пине. Если при нажатии кнопок на пульте он остается безжизненным, то, скорее всего, проблема в аппаратном подключении или неисправности самого ИК‑приемника.

Если библиотека видит сигнал, но не может его распознать, надо выяснить: входит ли протокол пульта в список поддерживаемых. Если декодирование все равно не срабатывает, скорее всего, реальные тайминги сигнала выходят за рамки 20%‑ной погрешности, заложенной в алгоритм.

Чтобы во всем разобраться, можно вывести в консоль ожидаемые минимальные и максимальные значения для конкретного протокола и сравнить их с «сырыми» замерами, которые выдает приемник.

Скетч‑пример examples/IRrecvDump выводит детальный отчет о каждом принятом сигнале. Заслуживает внимания формат: библиотека автоматически пересчитывает «тики» в микросекунды, а для наглядности добавляет знак минуса перед каждым значением паузы. Благодаря этому в консоли сразу видно, где был активный импульс, а где — пустой промежуток.

Обычно ИК‑датчики вносят свои искажения. «Метки» на выходе кажутся длиннее, чем они есть на самом деле, а «паузы» между ними — короче. Для исправления библиотека автоматически вносит поправку — вычитает из каждой метки 100 мкс (константа MARK_EXCESS). Если какой‑то датчик работает слишком специфично, может потребоваться вручную подправить ожидаемые значения или расширить допуски в настройках.

Одновременный обмен в обе стороны не поддерживается. Как только начинается передача сигнала — прием автоматически отключается.

В заключение

Библиотека уже нашла применение в самых разных проектах — от простых игрушек до систем автоматизации.

История создания библиотеки началась с вдохновения статьей «Создание универсального пульта дистанционного управления на Arduino». Хотя тот проект не претендовал на полную универсальность, он содержал важный теоретический базис.

Ключевым технологическим донором стала библиотека NECIRrcv — именно ее код послужил фундаментом для реализации обработчика прерываний.