Урок 14: ИК-пульт Дистанционного Управления

1. Цель эксперимента

Познакомиться с работой инфракрасного приёмника VS1838B и научиться управлять светодиодами при помощи бытового ИК-пульта. Вы научитесь:

  • подключать 38-кГц приёмник к Arduino;

  • декодировать сигналы пульта библиотекой IRremote;

  • выполнять действия (включать / выключать LED) по полученному коду.

2. Компоненты

Кол-во

Деталь

1

Плата Arduino UNO

1

Приёмник VS1838B (38 кГц)

1

ИК-пульт ДУ (любая модель с кодировкой NEC)

6

LED 5 мм

6

Резисторы 220 Ω — ограничение тока LED

11

Перемычки «папа–папа»

1

Макетная плата (breadboard)

1

USB-кабель A-B

3. Немного знаний

  • Приёмник настроен на частоту 38 кГц — именно на ней модулируется сигнал пульта.

  • Три вывода (слева → направо, если смотреть на линзу): VCC, GND, VOUT.

    Распиновка выводов VS1838B

4. Схема подключения

Подключение VS1838B и 6 LED к Arduino
  • VOUT11

  • GNDGND

  • VCC5 V

  • LED1 … LED6 → пины 2–7 через резисторы 220 Ω

5. Протокол NEC (кратко)

Логические 0 и 1
Длительности логического 0 и 1 (NEC)
Кадр данных NEC
Полный кадр данных NEC
  • Бита 1 — 562 µs импульс + 1,69 мс пауза.

  • Бита 0 — 562 µs импульс + 562 µs пауза.

  • Адрес (8 бит) и команда (8 бит) передаются в нормальном и инверсном виде для контроля ошибок.

  • При удержании кнопки каждые 110 мс отправляется «repeat-код».

Тайминг повторяющегося кода NEC

Особенности NEC-формата

  1. 8-битный адрес и 8-битная команда ⇒ всего 16 значимых бит.

  2. Надёжность: сразу после адреса/команды передаются их инвертированные копии (ещё 16 бит). Приёмник сравнивает пары и убеждается, что данные дошли без искажений.

  3. Модуляция импульсного расстояния (pulse-distance modulation): логические 0 и 1 различаются длиной паузы после импульса, а не длительностью самого импульса (он всегда 562 µs).

  4. Несущая частота — 38 кГц, ширина импульса 562 µs содержит ~21 цикл.

  5. Время передачи одного бита: 1,125 мс (лог.0) или 2,25 мс (лог.1).

Кадр данных

  • «Заголовок» — 9 мс непрерывного 38-кГц сигнала (AGC burst), после которого 4,5 мс тишины. Такой паттерн легко отфильтровать программно и аппаратно.

  • 32 бит полезных данных (LSB → MSB): адрес, ~адрес, команда, ~команда.

  • Завершается тишиной ≥40 мс.

Повторяющийся код

Если кнопку удерживать, вместо полного кадра каждые ~110 мс посылается «repeat»: 9 мс AGC + 2,25 мс пауза + 562 µs импульс. Ардуино узнаёт, что кнопку не отпустили, и может повторять действие (например, плавно менять громкость).

Таким образом, чтобы распознать конкретную клавишу, достаточно считать первые 32 бита кадра. Библиотека IRremote делает это за нас и возвращает шестнадцатеричный код вида 0x00FF6897.

6. Коды кнопок (пример пульта)

Ниже приведены HEX-коды, которые часто встречаются у «мини-пульта» из набора. Снимите их своим приёмником и убедитесь, что значения совпадают — производители иногда меняют прошивку.

FFA25D

FF629D

FFE21D

FF22DD

FF02FD

FFC23D

FFE01F

FFA857

FF906F

FF6897

FF9867

FFB04F

FF30CF

FF18E7

FF7A85

FF10EF

FF38C7

FF5AA5

FF42BD

FF4AB5

FF52AD

Коды FF6897 и FF9867 использованы в примерах выше для включения и выключения первого светодиода. Остальные можете привязать к своим действиям.

7. Установка библиотеки IRremote

Перед компиляцией скетчей убедитесь, что библиотека IRremote +(maintainer Arduino) установлена в среде Arduino IDE.

  1. Откройте Tools ▸ Manage Libraries… (или «Скетч → Подключить библиотеку → Управлять библиотеками…»).

  2. В поле поиска введите IRremote.

  3. Найдите библиотеку Arduino-IRremote от Arduino (бывшая shirriff).

  4. Нажмите Install и дождитесь окончания загрузки.

  5. Перезапустите IDE, если потребуется.

Альтернативный способ — скачать ZIP-архив с GitHub, затем в IDE выбрать Sketch ▸ Include Library ▸ Add .ZIP Library… и указать скачанный файл.

После установки убедитесь, что в скетче подключается файл #include <IRremote.h> (для версии 3.x и выше). Если у вас старая версия 2.x, путь тот же, но пространство имён меняется — учитывайте это при миграции.

8. Пример кода: один светодиод

ir_single_led.ino
// Урок 14 — ИК-пульт, управление одним LED
#include <IRremote.h>

const byte RECV_PIN = 11;   // VOUT приёмника
const byte LED_PIN  = 2;    // LED на пине 2

const unsigned long ON_CODE  = 0x00FF6897;  // «включить»
const unsigned long OFF_CODE = 0x00FF9867;  // «выключить»

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
  irrecv.enableIRIn();        // старт приёмника
}

void loop() {
  if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX); // выводим код

    if (results.value == ON_CODE)  digitalWrite(LED_PIN, HIGH);
    if (results.value == OFF_CODE) digitalWrite(LED_PIN, LOW);

    irrecv.resume();               // слушаем дальше
  }
}

9. Пример кода: шесть светодиодов

ir_six_leds.ino
////////////////////////////////////////////////////////////////
// Проект 14: Дистанционное Управление https://github.com/shirriff/Arduino-IRremote

#include <IRremote.h>

int RECV_PIN = 11; // выбираем цифровой контакт 11 для инфракрасного приемника
int LED1 = 2; // выбираем цифровой контакт 2 для светодиода 1
int LED2 = 3; // выбираем цифровой контакт 3 для светодиода 2
int LED3 = 4; // выбираем цифровой контакт 4 для светодиода 3
int LED4 = 5; // выбираем цифровой контакт 5 для светодиода 4
int LED5 = 6; // выбираем цифровой контакт 6 для светодиода 5
int LED6 = 7; // выбираем цифровой контакт 7 для светодиода 6

long on1  = 0x00FF6897; // код включения для первой кнопки
long off1 = 0x00FF9867; // код выключения для первой кнопки
long on2  = 0x00FFB04F; // код включения для второй кнопки
long off2 = 0x00FF30CF; // код выключения для второй кнопки
long on3  = 0x00FF18E7; // код включения для третьей кнопки
long off3 = 0x00FF7A85; // код выключения для третьей кнопки
long on4  = 0x00FF10EF; // код включения для четвертой кнопки
long off4 = 0x00FF38C7; // код выключения для четвертой кнопки
long on5  = 0x00FF5AA5; // код включения для пятой кнопки
long off5 = 0x00FF42BD; // код выключения для пятой кнопки
long on6  = 0x00FF4AB5; // код включения для шестой кнопки
long off6 = 0x00FF52AD; // код выключения для шестой кнопки

IRrecv irrecv(RECV_PIN); // создание экземпляра приемника ИК-сигналов
decode_results results;  // создание экземпляра для результатов декодирования

void dump(decode_results *results) {
  int count = results->rawlen;
  if (results->decode_type == UNKNOWN) {
    Serial.println("Сообщение не удалось декодировать");
  } else {
    if (results->decode_type == NEC) {
      Serial.print("Декодирован NEC: ");
    } else if (results->decode_type == SONY) {
      Serial.print("Декодирован SONY: ");
    } else if (results->decode_type == RC5) {
      Serial.print("Декодирован RC5: ");
    } else if (results->decode_type == RC6) {
      Serial.print("Декодирован RC6: ");
    }
    Serial.print(results->value, HEX);
    Serial.print(" (");
    Serial.print(results->bits, DEC);
    Serial.println(" бит)");
  }
  Serial.print("Необработанное (");
  Serial.print(count, DEC);
  Serial.print("): ");
  for (int i = 0; i < count; i++) {
    if ((i % 2) == 1) {
      Serial.print(results->rawbuf[i]*USECPERTICK, DEC);
    } else {
      Serial.print(-(int)results->rawbuf[i]*USECPERTICK, DEC);
    }
    Serial.print(" ");
  }
  Serial.println("");
}

void setup() {
  pinMode(RECV_PIN, INPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(LED4, OUTPUT);
  pinMode(LED5, OUTPUT);
  pinMode(LED6, OUTPUT);
  pinMode(13, OUTPUT);
  Serial.begin(9600);
  irrecv.enableIRIn(); // Запуск приемника
}

int on = 0;
unsigned long last = millis();

void loop() {
  if (irrecv.decode(&results)) {
    if (millis() - last > 250) {
      on = !on;
      digitalWrite(13, on ? HIGH : LOW);
      dump(&results);
    }

    if (results.value == on1)  digitalWrite(LED1, HIGH);
    if (results.value == off1) digitalWrite(LED1, LOW);

    if (results.value == on2)  digitalWrite(LED2, HIGH);
    if (results.value == off2) digitalWrite(LED2, LOW);

    if (results.value == on3)  digitalWrite(LED3, HIGH);
    if (results.value == off3) digitalWrite(LED3, LOW);

    if (results.value == on4)  digitalWrite(LED4, HIGH);
    if (results.value == off4) digitalWrite(LED4, LOW);

    if (results.value == on5)  digitalWrite(LED5, HIGH);
    if (results.value == off5) digitalWrite(LED5, LOW);

    if (results.value == on6)  digitalWrite(LED6, HIGH);
    if (results.value == off6) digitalWrite(LED6, LOW);

    last = millis();
    irrecv.resume(); // Прием следующего значения
  }
}
////////////////////////////////////////////////////////////////

10. Результат теста

После загрузки скетча откройте Serial Monitor на 9600 бод. При нажатии кнопок пульта будут выводиться строки «Decoded NEC: XXXXXXX» — шестнадцатеричные коды кнопок.

Вывод декодированных кодов в Serial Monitor

11. Что может пойти не так

Симптом

Причина

Решение

Всегда «FFFFFFFF»

Приёмник не видит сигнал

Проверьте питание, частоту 38 кГц, направьте пульт прямо на датчик

Нет реакции на код

Неправильное значение ON_CODE / OFF_CODE

Выведите код в монитор и подставьте его в скетч

Светодиоды мигают хаотично

Помехи от другого пульта / лампы

Используйте экран, уменьшите дальность, добавьте проверку адреса

12. Эксперименты

  • Замените шесть отдельных if на switch или на таблицу структур — код будет компактнее.

  • Используйте функцию IrReceiver.decode() из новой версии библиотеки (2.x) — она сильно упростила API.

  • Управляйте не LED, а сервоприводом или реле: поставьте шаровой штатив и сделайте «ИК-поворотник» для камеры.