Введение
ИК-приёмник (модуль на чипе VS1838/HX1838) в сочетании с прилагаемым в комплекте маленьким ИК-пультом — простой способ добавить беспроводное управление устройством без необходимости в более сложных Bluetooth/Wi-Fi модулях. В статье разберём приём и декодирование ИК-сигналов через библиотеку IRremote.
Концепция
ИК-пульт кодирует каждую нажатую кнопку как последовательность модулированных инфракрасных импульсов согласно одному из нескольких распространённых протоколов (NEC — наиболее типичен для дешёвых пультов из стартовых наборов) — ИК-приёмник демодулирует оптический сигнал в электрический, а библиотека IRremote разбирает временные интервалы этого сигнала в конкретный код нажатой кнопки. Поскольку коды кнопок конкретного пульта заранее неизвестны без документации, типичный первый шаг — написать диагностический скетч, выводящий полученный код каждой нажатой кнопки, чтобы затем использовать эти значения в финальной прошивке.
Пример кода
// firmware/src/diagnostic.cpp — диагностический скетч для определения кодов кнопок конкретного пульта
#include <IRremote.hpp>
#define IR_RECEIVE_PIN 4
void setup()
{
Serial.begin(9600);
IrReceiver.begin(IR_RECEIVE_PIN);
}
void loop()
{
if (IrReceiver.decode()) {
Serial.print("Получен код: 0x");
Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX);
IrReceiver.resume(); // обязательно — готовность к приёму следующего сигнала
}
}
// firmware/src/main.cpp — финальная прошивка с заранее определёнными кодами конкретного пульта
#include <IRremote.hpp>
#define IR_RECEIVE_PIN 4
#define LED_PIN 13
// Коды кнопок, полученные через диагностический скетч выше для конкретного пульта из комплекта
#define IR_CODE_POWER 0xFFA25D
#define IR_CODE_VOLUME_UP 0xFF629D
#define IR_CODE_VOLUME_DOWN 0xFFA857
void setup()
{
Serial.begin(9600);
IrReceiver.begin(IR_RECEIVE_PIN);
pinMode(LED_PIN, OUTPUT);
delay(100);
Serial.println("READY");
}
void loop()
{
if (IrReceiver.decode()) {
const unsigned long code = IrReceiver.decodedIRData.decodedRawData;
if (code == IR_CODE_POWER) {
digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // переключение состояния по кнопке питания
Serial.println("IR:POWER");
} else if (code == IR_CODE_VOLUME_UP) {
Serial.println("IR:VOLUME_UP");
} else if (code == IR_CODE_VOLUME_DOWN) {
Serial.println("IR:VOLUME_DOWN");
} else if (code != 0) {
Serial.print("IR:UNKNOWN:0x");
Serial.println(code, HEX);
}
IrReceiver.resume();
}
}
Пояснения к коду
Диагностический скетч — отдельная, явно выделенная программа (а не часть финальной прошивки), единственная задача которой — вывести в Serial Monitor шестнадцатеричный код каждой нажатой кнопки конкретного пульта; поскольку коды зависят от конкретной модели пульта и протокола, а не являются универсальной константой, этот этап ручного определения кодов необходим перед написанием реальной логики обработки конкретных кнопок.
Финальная прошивка использует заранее определённые (через диагностический скетч) константы кодов кнопок, явно сравниваемые с полученным decodedRawData — критичный, легко забываемый момент: вызов IrReceiver.resume() обязателен после обработки каждого полученного сигнала, иначе библиотека не будет готова к приёму следующего сигнала, и пульт фактически «перестанет работать» после первого же нажатия без видимой причины этого молчания.
Подводные камни
- Забытый вызов
IrReceiver.resume()после обработки декодированного сигнала — без этого вызова приёмник остаётся в состоянии «занят предыдущим сигналом» и не реагирует на последующие нажатия кнопок пульта, что выглядит как полная неработоспособность модуля после первого же успешного приёма, хотя реальная причина — простое отсутствие одной обязательной строки кода. - Коды кнопок специфичны для конкретной модели и даже конкретного физического экземпляра пульта — код, определённый для одного пульта (даже из того же комплекта), не гарантированно совпадёт с кодом аналогичной кнопки другого, отдельно приобретённого пульта той же визуальной модели, особенно если они произведены разными партиями или вообще разными производителями, маскирующимися под одинаковый внешний вид — для проектов, предполагающих замену пульта, стоит либо предусмотреть процедуру «обучения» новому пульту, либо явно зафиксировать использование только одного конкретного, проверенного экземпляра пульта.
- Повторяющиеся коды при удержании кнопки — большинство ИК-протоколов (включая NEC) при удержании кнопки нажатой передают специальный «повторный» код (отличный от основного кода кнопки), и код, проверяющий только основной код кнопки без обработки кода повтора, не заметит длительное удержание кнопки как продолжающееся действие, что может быть неожиданным для логики, предполагающей, например, плавное нарастание громкости при удержании кнопки «громче».
- Интерференция от других источников инфракрасного излучения (солнечный свет, некоторые типы ламп, другие ИК-пульты в помещении) может вызывать ложные срабатывания или искажение принимаемого сигнала — для надёжной работы в реальных, не идеально контролируемых условиях окружения, а не только в спокойной обстановке стола разработчика, стоит явно протестировать устойчивость приёма ИК-сигнала к типичным источникам помех, ожидаемым в реальном месте использования устройства.