У колонки на кухне пульт доживал последние месяцы: кнопка mute протёрлась первой — сенсорная плёнка её не вынесла. Замена пульта — месяц ожидания и цена половины колонки; вместо этого я собрал двойника на макетке: Arduino, тактовая кнопка и ИК-светодиод. Одна кнопка, одна команда, один кадр на нажатие. За вечер прибор научился гасить звук с двух метров, а за неделю выяснилось, что у честности такого двойника есть границы, о которых туториалы молчат.
Аппаратная сторона уже описана в разделе: диод 940 нанометров и его ток — в уроке про передатчик, вызов sendNEC с адресом, командой и числом повторов — в уроке про IrSender.sendNEC. Здесь вся соль в двух решениях, которых там не было: сколько кадров несёт команда mute и что делать с состоянием, которого Arduino не знает. Адрес и код кнопки снимаются сниффером с родного пульта — приём разобран в уроке про сниффер чужого пульта.
Честная граница примера: цифры адреса и команды ниже — мои, снятые с пульта моей колонки. У вашей техники они будут своими, и снимать их придётся тем же сниффером: гуглить коды по модели я не советую — сетевые базы путают редакции пультов, и чужой адрес съедает вечер.
Один кадр, ноль повторов
Mute — команда-переключатель: каждый кадр переворачивает состояние звука, и это меняет всю дисциплину отправки. Громкость терпит повторы — кадры складываются в плавный ход; mute повторов не прощает: второй кадр в пачке вернёт звук, и колонка промолчит в самый неподходящий момент. Поэтому третьим аргументом sendNEC едет ноль, а скетч шлёт ровно один кадр на нажатие, сколько бы ни держался палец. Полный кадр NEC длится около 68 миллисекунд, и sendNEC честно держит процессор всё это время — мигание индикации вокруг вызова можно не размазывать по millis.
Кнопка читается по фронту, а не по уровню: INPUT_PULLUP прижимает пин к HIGH, нажатие валит его в LOW, и кадр уходит только на переходе. Зажатая кнопка на уровне слала бы кадры без остановки — то самое удвоение mute. Антидребезг в 50 миллисекунд отсекает звон контактов; приём разобран в уроке про антидребезг, здесь он ужат до одного сравнения millis. Скетч двойника:
// Кнопка тишины: одно нажатие - один кадр mute#include <IRremote.hpp> const int BUTTON_PIN = 2;const int LED_PIN = 12; // только индикация, ШИМ не нужен const uint16_t SB_ADDR = 0x02; // адрес колонки: снят сниффером с её пультаconst uint8_t CMD_MUTE = 0x0A; // команда mute того же пульта bool lastLevel = HIGH; // подтяжка: покой - HIGHunsigned long lastEdge = 0; void setup() { pinMode(BUTTON_PIN, INPUT_PULLUP); pinMode(LED_PIN, OUTPUT); IrSender.begin(); // несущая 38 кГц сама встанет на пин 3 Serial.begin(9600); Serial.println("mute: odno nazhatie - odin kadr");} void loop() { bool level = digitalRead(BUTTON_PIN); if (level == LOW && lastLevel == HIGH) { // фронт нажатия if (millis() - lastEdge > 50) { // антидребезг lastEdge = millis(); IrSender.sendNEC(SB_ADDR, CMD_MUTE, 0); // ноль повторов! digitalWrite(LED_PIN, HIGH); // кадр ушёл (догадка) delay(30); digitalWrite(LED_PIN, LOW); Serial.println("kadr mute ushel"); } } lastLevel = level;}Проверка занимает минуту и делается телефоном: камера в режиме съёмки показывает фиолетовое пятно на диоде в момент нажатия — кадры уходят. Дальше колонка: её приёмник при первом запуске стоит в упор к диоду, и только после подтверждённого mute дистанцию можно растягивать. Мой двойник держит два с половиной метра при пике тока через диод под полсотни миллиампер и резисторе 150 Ом; для трёх метров диод поднимают на ножках и добавляют тока.
Про батарейку отдельно. Uno в роли брелока — расточительство: пятьдесят миллиампер съедают крону за вечер. Мой двойник живёт на Pro Mini 3,3 вольта с 8-мегагерцовным кристаллом, и пара пальчиковых элементов держит его месяцами. Если остаётесь на Uno — сажайте её на зарядку: брелок это не про неё.
И про паузу между нажатиями. Колонке нужно время разобрать кадр и щёлкнуть своим реле звука; пачка из двух кадров с интервалом меньше сотни миллисекунд у моей модели превращалась в одну команду. С тех пор окно в 200 миллисекунд между фронтами живёт в скетче — той же механикой, что замок шага в следующем уроке, и рука быстро привыкает жать размеренно.
Отдельно про адрес. У пультов из набора адрес почти всегда 0x00, и двойник с таким адресом будет вторить чужой технике на том же протоколе — люстра в соседней комнате отзовётся вместе с колонкой. Моё правило: если колонка единственный NEC-прибор в комнате, адрес родного пульта и есть рабочий; если рядом живут другие приборы — берите адрес с их пультов точечно и проверяйте каждый кадр камерой телефона по отдельности.
Состояние, которого Arduino не знает
Главная ловушка двойника — односторонний канал. Светодиод на макетке мигает на каждом кадре, но он показывает факт отправки, а не состояние колонки: кадр мог не дойти, мог прийти в момент, когда колонка уже спит, мог утонуть в кадре от настоящего пульта. Двойник не знает, включён ли звук, — и никакой код это не изменит: у NEC нет ответного кадра. Для mute это терпимо — нажал и услыхал результат ушами; для «выключить всё в доме» — нет: разница между догадкой и фактом встанет в полный рост.
Честность индикации лечится компоновкой, а не честностью диода. На кухне я повесил рядом двухстрочный экран, и двойник печатает на нём последнюю отправленную команду с пометкой «ушло» — это дисплей-протокол из урока про обратную связь на LCD. Кто не хочет второй платы — слушает эфир тем же приёмником и ловит собственные кадры, как в мосте между платами; обе роли на одном чипе конфликтуют по таймеру, и это разобрано в уроке про таймер 2.
Есть и вторая сторона той же монеты: колонка не знает, кто шлёт кадр. Настоящий пульт, вернувшись из ящика, будет молча переворачивать mute рядом с двойником, и оба прибора так и останутся в неведении друг о друге. Я решил по-деревенски: старый пульт лежит в столе, двойник — на столе. Семейный договор надёжнее любых протоколов согласования, которых у ИК просто нет.
| Действие | Что уходит в эфир | Что происходит у колонки |
|---|---|---|
| одно нажатие кнопки | полный кадр NEC: адрес, команда mute, ноль повторов | звук гаснет — или возвращается, состояние двойнику неведомо |
| кнопка зажата | по-прежнему один кадр на нажатие | фронт пойман один раз, повторов скетч не шлёт |
| двойной щелчок | два кадра с интервалом от 200 мс | mute переворачивается дважды — звук возвращается |
| кадр из пачки ближе 100 мс | два кадра подряд | приёмник колонки может слить их в одну команду |
Сравнение с готовыми решениями короткое. Замена пульта по каталогу вернёт все кнопки сразу, но стоит денег и месяца ожидания; двойник стоит копейки и растёт под ваши привычки. Мой итог после месяца: mute жмут втрое чаще, чем любую другую кнопку родного пульта, и отдельная большая кнопка на макетке честно лучше маленькой протёртой резинки.
Границы приёмника колонки тоже стоят оговорки. Колонки с пультами RC5 или сырыми протоколами не ответят честному кадру NEC: язык устройства определяет читалка за минуту, и тогда sendNEC меняется на sendRC5 или raw-дампы — маршрут показан в уроке про RAW и чужие протоколы. Остальная механика двойника — фронт, антидребезг, индикация догадки — не меняется ни на бит.
И последний штрих — про батарейный отсек. Двойник между нажатиями не делает ничего: приёмника на плате нет, loop крутится вхолостую, и ток упирается в собственную обвязку платы. Пара пальчиковых элементов на Pro Mini живёт у меня четвёртый месяц; проверка простая — амперметр в разрыв минуса, и цифры в микроамперах говорят сами за себя. Если счёт идёт на миллиамперы, виноват не скетч, а стабилизатор и светодиоды платы.
- 1. Почему mute отправляется с нулём в третьем аргументе sendNEC?
- 2. Откуда взять адрес и код mute для своей колонки?
- 3. Что на самом деле показывает светодиод на макетке двойника?
- 4. Почему кнопка читается по фронту, а не по уровню?
- 5. Почему у двойника нет и приёмника на той же плате?
- Mute — команда-переключатель: один кадр на нажатие, ноль повторов, окно 200 мс между фронтами.
- Адрес и команду снимает сниффер с родного пульта; чужие базы кодов путают редакции.
- Кнопка — по фронту с антидребезгом 50 мс: уровень слал бы кадры, пока палец зажат.
- Светодиод двойника показывает факт отправки, а не состояние колонки: канал NEC односторонний.
- Передача и приём на одном чипе дерутся за Таймер 2 — двойник только отправляет.
Частые вопросы по теме урока
- Почему mute отправляется с нулём в третьем аргументе sendNEC?
- Mute — команда-переключатель: каждый кадр переворачивает состояние. Пачка из двух кадров вернёт звук. Громкости повторы нужны, mute — противопоказан: третьим аргументом едет ноль. повторный кадр вернул бы звук обратно — команда инвертирует состояние
- Откуда взять адрес и код mute для своей колонки?
- Коды в сети часто привязаны к другой редакции пульта, и чужой адрес съедает вечер. Сниффер за минуту печатает адрес и команду каждой кнопки живого пульта — надёжнее ничего не придумали. снять сниффером с родного пульта колонки
- Что на самом деле показывает светодиод на макетке двойника?
- У NEC нет ответного кадра, канал односторонний. Диод мигает на отправке; дошёл ли кадр и что с ним сделала колонка, Arduino не знает. Честность лечится компоновкой: дисплей с пометкой «ушло» или слушающий приёмник. только факт отправки кадра — догадку о результате
- Почему кнопка читается по фронту, а не по уровню?
- Зажатая кнопка держит пин в LOW и на уровне слала бы mute каждые 50 мс — звук ходил бы по кругу. Фронт ловит переход HIGH-LOW один раз, антидребезг отсекает звон контактов. на уровне кадры уходили бы без остановки, пока кнопка зажата
- Почему у двойника нет и приёмника на той же плате?
- Генерация несущей и приём на ATmega328 делят Таймер 2. Правило раздела: передаёт — отдельная плата, принимает — отдельная; на одной плате поведение плавает от версии к версии. приёмник и передача 38 кГц дерутся за второй таймер: один из соседей оглохнет или замолчит