Ресивер у друга стоит в закрытой тумбе, пульт в неё не стреляет, дверь открывать лень. Магазинный ИК-удлинитель решает ровно эту задачу и внутри содержит простую связку: приёмник в комнате, провод, светодиод напротив глаза техники. Ту же штуку Uno собирает за вечер: слушает комнату приёмником на пине 11 и отстреливает команды ИК-светодиодом на пине 3 — стандартный пин отправки IRremote на Uno, за ним стоит Timer2. По энергии это муха против слона: приёмник пассивно ждёт, а светодиод в импульсе ест десятки миллиампер, поэтому Uno хватает, но при длинных проводах питание уже отдельная тема.
Ничего нового про ИК мы тут не придумываем: приём кодов и отправку через sendNEC библиотека умеет сама. Хитрость в другом — в порядке операций. decode() забирает кадр и останавливает приёмник, sendNEC блокирует loop на время кадра, и если перепутать очерёдность, ретранслятор начнёт глотать команды. Как формируется кадр на отправке, я разбирал в уроке про IrSender.sendNEC, а здесь — устройство целиком.
Магазинные удлинители бывают двух сортов: с одним глазом на длинном проводе и с двумя приёмниками — второй сидит рядом с техникой и ловит то, что не долетело из комнаты. Внутри такого удлинителя обычно и стоит пара TSOP плюс мелкий контроллер — разница с нами в цене, корпусе и свободе пина. Начинайте с одного: если светодиод видит глаз устройства, второй приёмник не нужен.
Поток decode → resume → send: что идёт раньше
Полный кадр NEC — это 67,5 мс битов, плюс паузы, плюс ожидание до 108 мс цикла. У других протоколов кадры другие, но блокирующая отправка у IRremote общая для всех: логика окна потерь остаётся той же. Пока sendNEC тянет кадр, loop занят, а тики приёма стоят: кадры, пришедшие в это окно, теряются безвозвратно. Поэтому правильный порядок такой: decode() вернул кадр — сразу скопировать address и command в свои переменные, вызвать resume(), чтобы приёмник снова слушал, и только потом отправлять. Скопировать до resume — обязательное условие: после resume приёмник может начать писать в те же поля новые кадры, и ваши адрес с командой превратятся в тыкву. Очередей в библиотеке нет: decode() отдаёт кадры по одному, буфера на время отправки не предусмотрено — вся дисциплина держится на раннем resume() и коротком цикле.
С repeat-кадром отдельная история. Повтор не содержит битов команды — переслать его как есть невозможно, библиотека просто не знает, что повторять, если своя память пуста. Практичное решение: на каждый повтор отправлять полный кадр с последними запомненными адресом и командой. Устройство-получатель увидит серию быстрых повторных нажатий — громкость и яркость реагируют нормально, и рука не замечает подмены. А строгие протоколы вроде кондиционеров могут и заметить: полный кадр там иногда несёт смысл «новое нажатие», и повторение без изменения состояния они прощают, но проверять нужно на своей технике.
#include <IRremote.hpp> const int IR_PIN = 11; // приёмник TSOP4838 смотрит в комнатуconst int IR_LED_PIN = 3; // ИК-светодиод через резистор 100 Ом uint16_t lastAddress = 0;uint8_t lastCommand = 0; void setup() { Serial.begin(9600); IrReceiver.begin(IR_PIN, ENABLE_LED_FEEDBACK); IrSender.begin(IR_LED_PIN); Serial.println("retranslyator gotov");} void loop() { if (IrReceiver.decode()) { bool repeat = IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT; lastAddress = IrReceiver.decodedIRData.address; // копия ДО resume lastCommand = IrReceiver.decodedIRData.command; IrReceiver.resume(); // приёмник снова слушает: отправка займёт кадр IrSender.sendNEC(lastAddress, lastCommand, 0); // полный кадр, без доборов if (repeat) { Serial.print("povtor pereslan kak polnyj kadr: "); } else { Serial.print("pereslano: "); } Serial.println(lastCommand); }}Таймерная арифметика на Uno сурова: приём и отправка делят Timer2, и во время кадра приёмник стоит. Пульты, которые шлют свежий кадр и следом повтор, попадают в это окно — второй кадр пропадает. По этой же причине ретранслятор не тянет двух независимых пультов в одной комнате: чужой кадр, влетевший в окно отправки, теряется, и с каждым дополнительным говорящим шанс коллизии растёт. Для телевизора это незаметно: repeat-поток возобновится со следующего кадра. Критично это для устройств, где пульт и приёмник обмениваются парами кадров, — такие через простой ретранслятор ходят плохо. Неблокирующий приём с resume и millis-задачами я разбирал отдельно, а конфликт таймеров с tone и Servo — в уроке про разводку таймеров.
Пин отправки переносится, но с оговорками: в четвёртой версии IrSender.begin() принимает любой цифровой пин, а кадр с нестандартного пина формируется программно, бит за битом, и тайминги становятся чувствительнее к прерываниям. На Uno стандартный D3 модулируется аппаратно таймером — при прочих равных держитесь его. Если D3 занят сервой или зуммером на tone(), сначала разведите таймеры, потом выбирайте пин: конфликтов в одном скетче и так хватает.
Очередность даёт честную задержку: приём, копия, отправка — команда доезжает до устройства за 100-200 мс. Телевизор и ресивер такой разницы не замечают, голосовые задержки на ИК не живут. Если хотите добор надёжности на свежих кадрах, ставьте в sendNEC количество повторов 1: устройство получит два кадра подряд, но окно потерь вырастет — плата занята уже два кадра.
| Этап цикла | Что происходит | Сколько занимает | Где промахнуться |
|---|---|---|---|
| decode() | приёмник отдал кадр и встал на паузу | мгновенно | забыть resume() — приём мёртв после первого кадра |
| Копия полей | address и command уходят в свои переменные | микросекунды, пара присвоений | копировать после resume — поля может перезаписать следующий кадр |
| resume() | приёмник снова слушает комнату | мгновенно | вызвать после sendNEC — окно потерь вырастет на 68 мс |
| sendNEC() | светодиод отыгрывает полный кадр | 67,5 мс битов | Timer2 занят: кадры в этом окне теряются без вопросов |
Прогон в симуляторе и стендовая сборка
На столе ретранслятор проверяют втроём: первая плата работает пультом и шлёт команды sendNEC со скетча из урока про отправку, вторая — сам ретранслятор, третья — техника или приёмник с монитором. В доме размещение важнее кода: приёмник с линзой в комнату, где лежит пульт, светодиод — напротив глаза устройства, провод до 3-5 м, питание по возможности отдельное или с тем самым резистором и конденсатором из урока про помехи.
Корпус решает больше, чем кажется: светодиод, приклеенный наугад за платой, честно светит в стенку тумбы. Я сверлю в корпусе отверстие под линзу приёмника в комнате и второе под передающий светодиод у техники, а плату сажаю между ними на шлейфе. Если глаз устройства спрятан за закрытой дверцей, выносите светодиод проводами прямо к дверце: тридцать сантиметров провода на отправке стоят дешевле, чем невидимый пульт.
Пределы устройства честные. Ответного канала нет: кондиционеры и дорогая техника, которые мигают подтверждением в сторону пульта, ретранслятор не услышит — он однонаправленный. Питание Uno от USB тянет приёмник и светодиод в импульсе, но при длинных проводах лучше короткий отдельный БП, иначе просадки вылезут случайными перезагрузками приёмника. И последний штрих отладки: камера телефона видит ИК-вспышки — наведите объектив на передающий светодиод, жмите пульт, и на экране появится фиолетовая пульсация. Так вы за десять секунд отличите мёртвый светодиод от неправильного кода. И держите в голове чистовик: ретранслятор — не только удлинитель пульта. Ту же связку используют для ИК-барьеров, подсчёта посетителей в дверном проёме и скрытых кнопок, когда пульт не должен быть виден в комнате.
- 1. Почему resume() вызывают до sendNEC, а не после?
- 2. Когда копировать address и command из decodedIRData?
- 3. Пришёл repeat-кадр. Что отправляет корректный ретранслятор?
- 4. Что теряется при работе ретранслятора?
- 5. Стандартный пин отправки IRremote на Uno — это...
- Ретранслятор — связка TSOP на 11 и ИК-светодиода на 3: та же схема, что у магазинного удлинителя
- Порядок decode → скопировать → resume → send: после resume поля кадра могут перезаписаться
- sendNEC блокирует loop на 67,5 мс кадра — кадры в этом окне теряются
- Repeat-кадр без битов не переслать как есть: шлём полный кадр с запомненными полями
- Светодиод через резистор 33-150 Ом, камера телефона показывает ИК-вспышки для быстрой отладки
Частые вопросы по теме урока
- Почему resume() вызывают до sendNEC, а не после?
- Отправка стоит 67,5 мс, и loop всё это время занят. resume() до отправки возвращает приёмник в строй сразу, и окно потерь остаётся равным самому кадру — это нижняя граница для одной платы. чтобы приёмник вернулся к работе, пока отправка блокирует loop на время кадра
- Когда копировать address и command из decodedIRData?
- resume() возвращает приёмник к работе, и библиотека может начать класть в decodedIRData следующий кадр. Копия до resume — единственный безопасный порядок. Отсюда и правило больших скетчей: между decode и resume держите не больше пары строк присвоения, и всё. сразу после decode и до resume: после resume кадр может перезаписаться
- Пришёл repeat-кадр. Что отправляет корректный ретранслятор?
- В repeat-кадре нет битов команды, воспроизвести его один в один нечем. Полный кадр с запомненными полями устройство видит как быстрое повторное нажатие — громкость и яркость реагируют корректно, а частота пересылки при удержании сохраняется той же, около 108 мс. полный кадр sendNEC с последними адресом и командой
- Что теряется при работе ретранслятора?
- Приём и отправка делят Timer2, на время кадра тики приёма стоят. Кадр, влетевший в это окно, пропадает — это честная цена простого ретранслятора на одной плате, а не дефект конкретного скетча. кадры, пришедшие в окно блокирующей отправки около 68 мс
- Стандартный пин отправки IRremote на Uno — это...
- На классике отправка живёт на D3 — выход OC2B таймера Timer2. Его же тики использует приёмник, поэтому во время кадра приём приостановлен. Сменить пин можно, но аппаратная модуляция останется только на ногах таймера. D3, за ним стоит Timer2