ИК-пульт и IRremote: приём кодов кнопок на Arduino
ИК-приёмник стоит копейки и превращает любой пульт в пульт управления Arduino: свет, робот, жалюзи, замок. Библиотека IRremote версии 4.x использует новый API: IrReceiver.begin, decode и decodedIRData.command.
Потренироваться можно в симуляторе с виртуальным пультом. Уроки по теме: приём кодов и управление светом.
Ниже разбираем проводку, физику ИК-сигнала, снятие дампа кодов вашего пульта, разбор двух типовых граблей с таймерами и шумом, а в конце список из семи шагов для проверки, что приём заработал.
Подключение TSOP4838 / VS1838B
| Вывод (лицом к себе, ножками вниз) | Arduino |
|---|---|
| Левый, OUT | Цифровой пин, например D11 |
| Центр, GND | GND |
| Правый, VCC | 5 В |
Распиновка модулей одинаковая, но цепи внутри у них разные. У TSOP4838 выход с открытым коллектором, то есть он замыкает линию на землю, когда поймал пачку импульсов. На готовой плате с подтяжкой это не имеет значения, а вот если вы впаяете чистый корпус VS1838B, подтяжку к питанию надо поставить самим, обычно 10 кОм. Без неё приёмник либо молчит, либо ловит случайные всплески. Питать приёмник можно и от 3,3 В: на TSOP4838 при этом дальность приёма растёт, потому что падает уровень шума на входе.
Что такое ИК-код: несущая, маркеры и паузы
Пульт не «передаёт цифру», он передаёт последовательность вспышек света с несущей 38 кГц. Сам ИК-светодиод пульта работает именно так: светодиод включается на частоте 38 кГц, а сам сигнал кодируется тем, когда он включён, а когда выключен. Приёмник выделяет эту несущую полосным фильтром и превращает вспышки в обычные нули и единицы. В протоколе NEC, который использует почти вся бытовая техника, логический ноль это вспышка 562,5 мкс и пауза 562,5 мкс, а логическая единица это та же вспышка и пауза 1687,5 мкс. Кадр NEC состоит из 32 бит: 16 бит адреса и 8 бит команды, причём обе восьмёрки продублированы с инверсией для защиты от ошибок.
Полезно понимать, как это выглядит внутри библиотеки. Сырые длительности хранятся не в микросекундах, а в тиках приёмника, и один тик равен 50 мкс, это шаг таймера, которым библиотека меряет линию. Чётные элементы записи это вспышки, нечётные тишины, и они неделимы: автомат читает пару целиком, потому что смысл бита живёт в отношении длительностей, а не в каждой по отдельности. Знание об этом спасает, когда приёмник ловит мусор и кажется, что надо менять прошивку.
Код: читаем кнопки
#include <IRremote.hpp> void setup() { IrReceiver.begin(11, ENABLE_LED_FEEDBACK); Serial.begin(9600);} void loop() { if (IrReceiver.decode()) { Serial.print("Команда: "); Serial.println(IrReceiver.decodedIRData.command); IrReceiver.resume(); }}Три обязательных шага в этом скетче. IrReceiver.begin запускает приёмник на пине 11 и включает светодиодную подсветку на плате, по ней видно, что библиотека вообще слушает линию. Условие IrReceiver.decode() проверяет, пришёл ли новый пакет, и внутри забирает его данные. IrReceiver.resume() возвращает приёмник в режим ожидания: без этого вызова он примет один пакет и замолчит навсегда, и это самая частая причина «работало и перестало».
Снимаем дамп: адрес, команда, протокол
#include <IRremote.hpp> void setup() { IrReceiver.begin(11, ENABLE_LED_FEEDBACK); Serial.begin(9600);} void loop() { if (IrReceiver.decode()) { auto &d = IrReceiver.decodedIRData; Serial.print("protocol="); Serial.print(d.protocol); Serial.print(" address="); Serial.print(d.address); Serial.print(" command="); Serial.println(d.command); if (d.flags & IRDATA_FLAGS_IS_REPEAT) { Serial.println(" eto povtor"); } IrReceiver.resume(); }}Дамп нужен, чтобы понять структуру вашего пульта. У большинства бытовых пультов протокол NEC, адрес у всех кнопок общий, а различаются они только командой: команда 16 означает кнопку «1», команда 17 означает «2». Но встречаются пульты, где адрес меняется от кнопки к кнопке, и там удобнее реагировать на пару адрес плюс команда. Расшифровку делайте один раз, записывайте таблицу в текст скетча, а в рабочей программе опирайтесь на числа, а не на предположения о смысле кнопок.
Автоповтор кнопок
Удержание кнопки шлёт повторные пакеты каждые примерно 110 мс. В 4.x повтор помечен флагом IRDATA_FLAGS_IS_REPEAT, и его нужно отбрасывать: иначе при одном удержании яркость будет прыгать, а счётчик нажатий насчитает десяток вместо одного. Второй слой защиты это задержка: после обработки команды подождите 100–150 мс и игнорируйте всё, что пришло в это окно.
Тупик: пульт срабатывает через раз
Первый наш стенд с пультом работал нестабильно: команда срабатывала, но через раз, а иногда вместо нужной кнопки приходила соседняя. Мы начали с физики: посмотрели осциллографом на выход приёмника и увидели, что между пакетами проходит в несколько раз больше времени, чем должно. На снимке это выглядело как пропавший пакет.
Причина оказалась не в пульте и не в приёмнике, а в нас: мы повесили приёмник рядом с блоком питания и силовым реле. Импульсная помеха от реле пролезала в тот же вход, а приёмник отрабатывал её как короткий пакет, и в Serial попадали значения команд, которых на пульте нет. Мы вынесли приёмник на отдельный провод подальше от силовой части, зашторили его фольгой и добавили в скетч фильтр по времени между пакетами. После этого срабатывания стали стабильными. Общий вывод: если ИК-пульт работает через раз, проверьте не код, а то, что рядом с приёмником.
Личный опыт: таймеры, которые глушат приёмник
Отдельная история, которую мы записали для себя в шпаргалку. По умолчанию IRremote 4.x занимает на Uno Timer2, а этот же таймер кормит ШИМ пинов 3 и 11 и используется функцией tone(). Каждый вызов tone() полностью перенастраивает таймер под себя, и тик приёма пропадает. Симптом узнаваемый: приёмник работает, пока молчит зуммер, а после первого писка decode() не отвечает до перезагрузки платы. Снять тревогу пультом под вой невозможно в принципе.
Лечение однострочное: добавьте #define IR_USE_AVR_TIMER1 перед подключением библиотеки, и приёмник переедет на Timer1, а tone() останется на Timer2. Плата за это понятна: Timer1 достанется приёмнику, значит библиотека Servo и ШИМ на пинах 9 и 10 в этом проекте станут недоступны. Мы принимаем это сознательно, потому что проект с сиреной на tone и без сервопривода прекрасно живёт на обычных пинах.
Авторская позиция: какой приёмник взять новичку
Берите модуль на TSOP4838, а не на VS1838B, и берите сразу два: один в проекте, второй в сумке на случай, если первый сгорит от неверного подключения. Модуль TSOP4838 выигрывает по чувствительности и дальности, а на готовой плате уже распаяна подтяжка, о которой новичок обычно и не знает. Для первого опыта этого достаточно с запасом.
Не берите сразу ИК-диод для передачи. Приём и передача это разные задачи: диод для передачи требует правильного тока, отдельного резистора и расчёта скважности, а большинство учебных проектов обходятся приёмом. Сначала научитесь читать пульт, потом, если задача того стоит, добавите отправку команд обратным каналом, чтобы дублировать пульт телевизора.