Коды своего телевизора я снял читалкой за пять минут и решил, что самое сложное позади: вписал их в скетч, поднёс макетку к экрану, нажал кнопку — и телевизор отреагировал так, будто рядом вообще никого нет. Диод мигал, камера телефона честно показывала вспышки, код был верный до последнего бита. Второй вечер ушёл на охоту за призраком, пока я не догадался посмотреть, как ведёт себя родной пульт: он никогда не шлёт одиночный кадр. Каждое нажатие — кадр плюс повторы, а первый кадр пачки приёмник техники может проглотить, пока подстраивает усиление.
Отдельный позор ситуации был в том, что приёмник на моей же макетке этот кадр ловил идеально: декодер печатал NEC, адрес и команду до последнего бита. Эфир диод пачкал правильно — техника его игнорировала. Тонкая граница, о которой полезно знать заранее: стендовый приёмник из набора жадный и глупый, он ловит всё подряд, а приёмник телевизора привередлив — ждёт пачку, как от настоящего пульта, и одиночный кадр считает случайной помехой.
Здесь разбираем вторую половину библиотеки — передатчик: где IrSender берёт четыре байта кадра NEC, что значат три аргумента sendNEC и сколько всё это занимает в эфире. Обещание на конец урока: одна кнопка на плате включает телевизор так же надёжно, как родной пульт, — с повторами и честным таймингом.
Три аргумента: адрес, команда, повторы
Объект передатчика библиотека создаёт сама: класс называется IRsend, а готовый экземпляр — IrSender, по аналогии с IrReceiver у приёмника. Старт выглядит знакомо: IrSender.begin(3, ENABLE_LED_FEEDBACK) цепляет передатчик к пину 3 и просит библиотеку мигать встроенным светодиодом на каждый кадр — удобно видеть работу без камеры. Пин 3 на Uno и Nano не случайный: несущую 38 кГц генерирует таймер Timer2, и его выход сидит именно там. Классика вызова — IrSender.sendNEC(address, command, repeats): адрес и команда — те самые числа из decodedIRData, третий аргумент — сколько повторных кадров дослать за полным.
Структуру кадра собирать вручную не нужно: мы разбирали её по битам — адрес, инверсия адреса, команда, инверсия команды, каждая пара даёт 0xFF. С вашей стороны два числа, инверсии доложит библиотека. Тайминг считается на пальцах: полный кадр при смеси нулей и единиц — около 68 мс, каждый повтор добавляет 108–110 мс. Вызов sendNEC(0x00, 0x43, 2) занимает эфир примерно 68 + 2 × 110 ≈ 290 мс — пачка, неотличимая от настоящего нажатия.
Про повторы в документации IRremote написано прямо: не забывайте их слать. Приёмник техники после долгой тишины должен подстроить автоматическую регулировку усиления, и начало пачки иногда проглатывается — на форумах Arduino советуют шлать минимум два-три кадра. Мой рабочий выбор: 0 повторов для макеточного приёмника, который ловит и так; 2–3 — для включения техники; больше — для удержания громкости. Если телевизор глухой при верных кодах, сначала кручу число повторов и только потом ток диода из прошлого урока.
Время пачки можно посчитать и без осциллографа. Кадр NEC — это шапка 9 мс плюс пауза 4,5 мс, затем 32 бита, из которых ноль живёт 1,12 мс, а единица 2,25 мс. Минимум — 13,5 + 32 × 1,12 ≈ 49 мс, максимум — 13,5 + 32 × 2,25 ≈ 85 мс, средняя смесь — около 68 мс. Повторный кадр короче: только шапка, 11,25 мс, но между пачками выдерживается стандартный интервал 108–110 мс. Сложите и получите оценку, которую я приводил выше, — она пригодится, когда захотите понять, влезает ли ваша пачка команд в паузу между действиями техники.
#include <IRremote.hpp> const uint8_t IR_SEND_PIN = 3;const uint8_t BTN_PIN = 2;const uint16_t TV_ADDRESS = 0x00; // свой адрес из читалкиconst uint8_t TV_POWER = 0x45; // своя команда из читалкиbool lastBtn = HIGH; void setup() { Serial.begin(9600); pinMode(BTN_PIN, INPUT_PULLUP); IrSender.begin(IR_SEND_PIN, ENABLE_LED_FEEDBACK);} void loop() { bool now = digitalRead(BTN_PIN); if (lastBtn == HIGH && now == LOW) { // фронт: нажали IrSender.sendNEC(TV_ADDRESS, TV_POWER, 2); // кадр плюс два повтора Serial.println("Tx: addr=0x00 cmd=0x45 povtory=2"); } lastBtn = now; delay(20); // грубый антидребезг, хватит для одной кнопки}Повторы, удержание и границы метода
Эмуляция пульта начинается с воровства, и это легально. Коды снимает приёмник: направьте родной пульт на читалку из первого урока раздела, выпишите адрес и команды кнопок — у дешёвых пультов из наборов это 0x00 и номера вроде 67 и 21. Типовые команды вы уже знаете по виджету симулятора: 67 — OK или power, 21 и 7 — громкость, 9 — EQ, 69 и 70 — каналы. Дальше подставляете их в sendNEC, и Arduino говорит с техникой на её языке. Первый кандидат — включение: колонка и телевизор просыпаются от одного кадра с парой повторов, а команды печатаю в hex по привычке из урока про bin, hex и dec — сверка с таблицей кодов идёт быстрее.
Удержание громкости собирается из того же кирпича. Пульт при зажатой кнопке шлёт короткий кадр-повтор каждые 108 мс — мы ловили его по флагу IS_REPEAT. Передатчику достаточно попросить много повторов: sendNEC(0x00, 0x15, 15) растянется примерно на 68 + 15 × 110 ≈ 1700 мс, и колонка услышит непрерывную цепочку повтора — громкость поползёт так же плавно, как от родного пульта. Нюанс копирования: кадр-повтор не пересылает команду, он лишь говорит «ту же кнопку всё ещё держат», поэтому библиотека сама ставит полный кадр первым, а повторы после него.
Метод работает, пока техника говорит по-NEC. Мой старый телевизор — сониного семейства: его адрес и команды живут в другом словаре, и шлют их через sendSony, у которого свой лидер и другой словарь бит. Язык техники определяет та же читалка: название протокола печатается первой строкой паспорта кадра. И проверяйте адрес: если он не ноль, вписывайте своё значение, а не ноль из чужого туториала — иначе кадр уйдёт не тому адресату.
И ещё одна граница, которую нашла колонка: не шлите пачки чаще, чем техника успевает их переваривать. Пять вызовов sendNEC подряд с паузами в десяток миллисекунд она глотала целиком, реагируя на первую и последнюю. Рабочий темп для команд — 300–500 мс между пачками: колонка успевает применить громкость, телевизор — отрисовать смену канала. Если после внедрения пауз что-то перестало доходить, увеличивайте интервал, а не ток диода — и время пачки из расчёта выше подскажет, что у вас едет в эфир.
| Задача | Вызов | Что происходит в эфире |
|---|---|---|
| Проверка стенда | sendNEC(0x00, 0x43, 0) | один кадр около 68 мс; приёмник из набора ловит всегда и сразу |
| Включение ТВ или колонки | sendNEC(0x00, 0x45, 2) | кадр плюс два повтора, около 290 мс — начало пачки не глотается |
| Удержание громкости | sendNEC(0x00, 0x15, 15) | цепочка кадров-повторов по 108–110 мс, суммарно около 1,7 с |
| Техника не NEC | sendSony(0x01, 0x15, 2) | другой лидер и словарь бит; язык техники определяет читалка перед вписыванием в таблицу |
Симулятор: сборка кадра без эфира
- 1. Сколько аргументов у sendNEC и что они значат?
- 2. Сколько времени займёт эфир у вызова sendNEC(0x00, 0x43, 2)?
- 3. Приёмник техники не отреагировал на одиночный кадр с верными кодами. Что советует практика IRremote?
- 4. Что шлёт родной пульт, пока кнопку громкости держат зажатой?
- 5. Почему IrSender.begin(3, ...) на Uno стартует именно с пина 3?
- IrSender создаёт библиотека сама: IrSender.begin(3, ENABLE_LED_FEEDBACK); пин 3 — выход Timer2 на Uno и Nano
- sendNEC(адрес, команда, повторы): инверсии байтов доложит библиотека, каждая пара в кадре даёт 0xFF
- Полный кадр — около 68 мс, повтор — ещё 108–110 мс: кадр плюс два повтора занимают около 290 мс
- Повторы не украшение: приёмник подстраивает АРУ и глотает начало пачки, шлите минимум два-три кадра
- Удержание громкости — много повторов одним вызовом; для техники вне NEC есть sendSony и RAW-дампы
Передатчик шлёт, техника слушается — осталась маленькая человеческая проблема: не хочется дёргать макеткой у телевизора ради каждой команды. В третьем уроке тройки выученные коды ложатся в таблицу, а кнопкам на плате назначаются целые сценарии: свет, громкость и питание одним нажатием. Там же появится дисциплина пауз: сначала питание, потом громкость, и между ними честные 300 мс ожидания. Для техники, которая не говорит по-NEC, наготове RAW-дампы и чужие протоколы — язык устройства читалка определяет за минуту.
Практику начните с колонки: она честная, слышит пачки и не прячет реакцию за плавной индикацией, как телевизор. Три вызова из таблицы выше — проверка стенда, включение и удержание громкости — покрывают девять из десяти бытовых сценариев; мой домашний пульт вырос именно из этой тройки и потом только обрастал строками таблицы.
Частые вопросы по теме урока
- Сколько аргументов у sendNEC и что они значат?
- sendNEC(address, command, repeats): вы отвечаете за два числа, инверсии байтов библиотека доложит сама, а третий аргумент задаёт длину пачки. Три: адрес, команда и число повторных кадров
- Сколько времени займёт эфир у вызова sendNEC(0x00, 0x43, 2)?
- Полный кадр — около 68 мс, каждый повтор добавляет 108–110 мс. Пачка из кадра и двух повторов — примерно 290 мс, как у настоящего нажатия. Около 290 мс: кадр плюс два повторных по 108–110 мс
- Приёмник техники не отреагировал на одиночный кадр с верными кодами. Что советует практика IRremote?
- Документация прямо просит не забывать повторы: после тишины приёмник подстраивает усиление и начало пачки теряется. Два-три повтора лечат почти всегда. Шлать пачкой: кадр плюс два-три повтора, приёмнику нужно время на АРУ
- Что шлёт родной пульт, пока кнопку громкости держат зажатой?
- Удержание — это цепочка коротких кадров-повторов. Передатчику достаточно одного вызова с большим числом повторов: sendNEC(0x00, 0x15, 15) звучит как зажатая кнопка. Кадр-повтор из шапки 9 мс и 2,25 мс паузы каждые 108 мс, без пересылки 32 бит
- Почему IrSender.begin(3, ...) на Uno стартует именно с пина 3?
- Несущая — работа таймера: на Uno это Timer2 и его выход OC2B, он выведен на пин 3. Заодно таймер объясняет конфликт с tone() — он тоже живёт на Timer2. Потому что рядом удобно светодиоду