Пульт от увлажнителя воздуха стал для меня стеной: читалка из первых уроков печатала UNKNOWN на любую кнопку, и я два вечера ждал, что библиотека как-нибудь сама в нём разберётся. Не разобралась — и честно: декодер сравнивает интервалы кадра с шаблонами известных протоколов, а шаблона моего увлажнителя в списке не было. Кадр при этом доезжал целиком, просто никто не собирался его читать.
Выход из тупика оказался жанром диктофона. Библиотека умеет не только разбирать кадры по полям, но и записывать их как есть: каждую вспышку и каждую тишину длительностью в микросекундах. Записал, перенёс числа в массив, воспроизвёл передатчиком — и увлажнитель услышал плату, хотя скетч не понимает в этом кадре ни одного бита смысла. За пару вечеров так записались вентилятор, увлажнитель и светильник из строительного гипермаркета — все, кого декодер отказывался узнавать.
Здесь разберём, где лежит сырая запись кадра в четвёртой версии и в старых статьях, как перенести её в массив и отправить через sendRaw, и сколько RAM это стоит — на Uno это число решает, будет ли скетч вообще жить.
Диктофон: что лежит в кадре при UNKNOWN
Механика приёма не зависит от того, узнал декодер шаблон или нет: прерывание меряет интервалы и складывает их в буфер — этот конвейер мы разбирали в уроке про анатомию decodedIRData. Буфер живёт своей жизнью даже под надписью UNKNOWN: в нём лежит полная запись вспышек и пауз. В четвёртой версии у неё три адреса: decodedRawData хранит сырые слова кадра, а запись длительностей — в rawDataPtr, там же, где rawlen, число записанных интервалов, и rawbuf, массив длительностей. Печать всего этого одной строкой — IrReceiver.printIRResultRawlinated(&Serial): построчно, парами длительность вспышки и длительность тишины.
В API второй версии запись та же, но адреса и единицы другие: results.rawlen и results.rawbuf, причём каждое число — тик по 50 микросекунд, а не микросекунды. Это не история для эрудитов, а ловушка при переносе кода — тема та же, что в уроке про установку и отличия версии 4: массив из туториала 2016 года и sendRaw четвёртой версии живут в разных единицах. Калибровочное число: лидер NEC — вспышка 9000 мкс; в свежем дампе это 9000, в старом rawbuf — 180. Видите цифру порядка сотни — умножайте всё на 50.
Смотреть в дамп полезно и без разбора протокола. Повторное нажатие даёт почти копию дампа — так строится таблица по кнопкам. Климатическая техника шлёт кадр дважды подряд — в дампе это видно сразу, и этому посвящён третий урок тройки. А ещё дамп честно показывает здоровье пульта: у подсевшей батарейки интервалы плывут раньше, чем декодер перестаёт узнавать свои же кадры.
#include <IRremote.hpp> const int IR_PIN = 2;unsigned int frames = 0; void setup() { Serial.begin(9600); IrReceiver.begin(IR_PIN, ENABLE_LED_FEEDBACK); Serial.println("Diktofon gotov");} void loop() { if (IrReceiver.decode()) { frames = frames + 1; Serial.print("kadr="); Serial.println(frames); if (IrReceiver.decodedIRData.protocol == UNKNOWN) { // сколько интервалов записано в буфере Serial.print("rawlen="); Serial.println(IrReceiver.decodedIRData.rawDataPtr->rawlen); // полная запись: пары длительностей в микросекундах IrReceiver.printIRResultRawlinated(&Serial); } IrReceiver.resume(); }}Читается дамп слева направо: первая пара — шапка, дальше биты. У NEC после пары 9000 и 4500 идут 560/560 и 560/1690 — те самые ноль и единица из урока про протокол NEC. У незнакомого протокола числа свои, структура та же: вспышка, тишина, вспышка, тишина. Мерить такие интервалы умеет и pulseIn — точные замеры мы делали руками в уроке про микросекунды; библиотека делает то же самое внутри прерывания.
Практика снятия дампа, отработанная на десятке пультов: свежая батарейка, дистанция 20-30 сантиметров, приёмник смотрит прямо на диод пульта. Солнце в окно добавляет свою засветку, и интервалы в записи плывут — я однажды полчаса боролся с рваным дампом, пока не сдвинул макетку со скатерти у подоконника. Повторите нажатие дважды: два почти одинаковых дампа — гарантия честной записи, а не случайной помехи, которая притворилась кадром.
Теперь RAM, без неё никуда. Буфер длительностей по умолчанию рассчитан на 200 интервалов, каждый хранится в uint16_t, то есть два байта: 400 байт из двух килобайт RAM на Uno. Мой увлажнитель укладывался в 80 пар, а климатическая техника легко пробивает сотню-другую — кадр кондиционера с повтором занимает около 200 пар, и буфер встаёт впритык. Нужен запас — поднимайте константу RAW_BUFFER_LENGTH в шапке библиотеки, помня цену: каждые 100 интервалов стоят ещё 200 байт RAM. На Mega 2560 восемь килобайт — буфер хоть на пару тысяч, но про цену байтов помнить стоит и там. Для длинных кадров я теперь сразу беру ESP32 — там эта арифметика не считается.
Воспроизводим чужой язык: sendRaw
Воспроизведение — запись наоборот. Массив длительностей, длина в элементах, несущая в килогерцах: IrSender.sendRaw(кадр, длина, 38). Диод мигает по чужому кадру бит в бит, и техника слышит плату как своего пульта. Передатчик инициализируется одной строкой IrSender.begin(3) — на Uno по умолчанию берётся пин 3. И тут же соседняя ловушка: на ATmega328 библиотека поднимает таймер 2, а его ШИМ сидит как раз на пинах 3 и 11 — планируя analogWrite, обходите эти пины стороной, иначе вспышки кадра и яркость диода начнут воевать за таймер.
Несущая в дампе не хранится: запись про огибающую, а не про 38 килогерц внутри вспышек, поэтому число для sendRaw выбираешь сам. Почти вся домашняя техника светит на 38 кГц, Sony — на 40, RC5 — на 36. Мой тупик здесь: первый кадр увлажнителя я отправлял на 56 кГц по привычке из другой статьи — приёмник у того оказался обычный 38-килогерцовый, и с полуметра ничего не доходило. Сменил одно число — сработало с первого кадра.
И честно про блокировку: sendRaw занят всю длину кадра. Кадр из 100 пар при среднем интервале около миллисекунды — это примерно 100 миллисекунд, когда скетч не отвечает ни на кнопки, ни на Serial. Разово включить вентилятор — незаметно; сыпать кадры в цикле — уже план. Если RAW-отправки в проекте чаще раза в секунду, я переношу их на ESP32: там кадр уходит аппаратному блоку RMT, и loop продолжает жить — об этом следующий урок.
| Что нужно | IRremote 4.x | IRremote 2.x (старые статьи) |
|---|---|---|
| Где лежит запись | rawDataPtr->rawlen и rawbuf | results.rawlen и results.rawbuf |
| Единицы длительностей | микросекунды | тики по 50 мкс — умножить на 50 |
| Сырые слова кадра | decodedRawData | results.value |
| Печать дампа | printIRResultRawlinated(&Serial) | ручной цикл по rawbuf |
| Воспроизведение | IrSender.sendRaw(массив, длина, кГц) | irsend.sendRaw(массив, длина, кГц) |
- UNKNOWN — не пустой ответ: полная запись длительностей лежит в буфере, печать printIRResultRawlinated(&Serial)
- Единицы: версия 4 печатает микросекунды, старый rawbuf — тики по 50 мкс; лидер NEC 9000 мкс равен 180 тикам
- Массив для sendRaw — uint16_t, длина в элементах, несущая в кГц: почти всегда 38
- RAM: буфер по умолчанию 200 пар по 2 байта — 400 байт на Uno; каждые 100 пар сверху стоят ещё 200 байт
- sendRaw блокирует loop на всю длину кадра: 100 пар — около 100 мс
- На Uno таймер 2 занят передачей: ШИМ на пинах 3 и 11 воюет с кадрами
- 1. Декодер печатает UNKNOWN. Где лежит сам кадр?
- 2. В массиве из старой статьи первая пара — 180 и 90. Что это?
- 3. Сколько RAM съедает буфер длительностей по умолчанию на 200 интервалов?
- 4. Что делает loop, пока sendRaw воспроизводит кадр из 100 пар?
- 5. Несущая для воспроизведения чужого RAW-кадра…
Мой порядок с любым чужим пультом: сначала определитель протокола — вдруг библиотека узнает и всё сделает сама; не узнала — дамп каждой кнопки, два нажатия на кнопку для контроля; затем массивы и проверка каждой кнопки sendRaw, начав с полуметра: метр, потом комната — дальность набирается током диода, а не надеждой. И только после этого кадры вписываются в постоянный скетч. Чеклист экономит часы: без него я однажды вписал непроверенный кадр светильника в проект полива — и ловил редкие сбои две недели, пока не снял дамп заново со свежей батарейкой.
Итого по диктофону: UNKNOWN больше не тупик, а маршрут — дамп, массив, sendRaw, и техника слышит плату без единого разобранного бита. В уроке про RC5, Sony и RAW дамп мелькал как дверь в чужие протоколы — теперь вы проходите в неё целиком, с массивом и передатчиком в руках. Дальше по разделу — IRremoteESP8266 на ESP32 и ESP8266: другая библиотека, аппаратная передача и та же идея RAW в масштабе, где RAM уже не считается по байтам.
Частые вопросы по теме урока
- Декодер печатает UNKNOWN. Где лежит сам кадр?
- Разбор не удался, но запись интервалов жива: её печатают диктофоном и позже воспроизводят передатчиком. command при UNKNOWN пуст. В буфере длительностей: rawlen и rawbuf, печать через printIRResultRawlinated
- В массиве из старой статьи первая пара — 180 и 90. Что это?
- Старый rawbuf меряет в тиках по 50 мкс: 180 тиков — это 9000 мкс. Умножайте весь массив на 50 перед sendRaw четвёртой версии. Микросекунды, пульт Sony
- Сколько RAM съедает буфер длительностей по умолчанию на 200 интервалов?
- Каждый интервал хранится в uint16_t: 200 × 2 = 400 байт из 2 КБ на Uno. Длинные климатические кадры встают в него впритык. 2 КБ
- Что делает loop, пока sendRaw воспроизводит кадр из 100 пар?
- На AVR передача сидит на таймере и занята весь кадр: порядка 100 мс на кадр из 100 пар, и всё это время скетч глух. Ждёт: отправка блокирует скетч примерно на 100 мс
- Несущая для воспроизведения чужого RAW-кадра…
- Дамп про огибающую, килогерцы в него не пишутся. Мой тупик был с 56 кГц на 38-килогерцовом приёмнике — с полуметра кадр не дошёл. Сверяйте несущую по технике, а не по привычке из чужой статьи. Выбирается по технике: обычно 38 кГц, Sony 40, RC5 36