Вторая жизнь старых пультов: DIY умные устройства на Arduino
Компоненты и материалы
| Arduino Nano R3 | 1 шт. |
| Красный светодиод 5 мм | 3 шт. (один — индикатор режима, остальные — для идентификации) |
| Зелёный светодиод 5 мм | 1 шт. |
| Жёлтый светодиод 5 мм | 1 шт. |
| Синий светодиод | 2 шт. |
| Беспаяльная макетная плата (полноразмерная) | 1 шт. |
| Перемычки (набор) | 1 шт. |
| Тумблер (Off)-On, SPST | 1 шт. |
| Модуль питания для макетной платы 5V/3.3V (SparkFun) | 1 шт. (опционально) |
| IR-приёмник (например, KY-022, 37.9 кГц) | 1 шт. |
О проекте
Проект позволяет применить старые пульты от видеомагнитофонов, телевизоров, DVD-проигрывателей для управления бытовыми приборами: лампами, обогревателями и т.д. через реле или силовые транзисторы. В демонстрационной версии управляются 6 светодиодов, но логику можно расширить на любые нагрузки, управляемые 5-вольтовым сигналом.
Принцип работы
Система «запоминает» 6 ИК-кодов с любого пульта (удобно использовать цифры 1–6). Любая другая кнопка работает как сброс — все выходы гаснут. Коды хранятся в EEPROM Arduino (по 4 байта каждый, всего 24 байта из 1024 доступных), поэтому настройки сохраняются после выключения питания.
Для надёжного приёма сигнала автор наклонил фототранзистор приёмника на ~45° и накрыл его картонным коробом с одним открытым концом — это устранило ложные срабатывания от бокового света и обеспечило уверенный приём на 1.5–2.5 метра.
Режимы работы
- Learn (обучение): переключатель в положение «Learn» — загорается красный индикатор. Мигает первый выходной светодиод — нажмите кнопку на пульте. При распознавании кода индикатор режима быстро мигает («вибрирует») и переходит к следующему выходу. После программирования всех 6 — индикатор медленно мигает раз в секунду: пора переключиться в Run.
- Run (работа): приём кодов и переключение соответствующих выходов. Дубликаты кодов при запуске подсвечиваются миганием «виновных» светодиодов (но не блокируют работу — может быть нужно, чтобы несколько кнопок делали одно действие).
Все события дублируются в Serial Monitor — удобно для отладки без запоминания кодов мигания.
Дополнение: управление реле
Автор протестировал схему с 5 реле, параллельно соединёнными со светодиодами 1–5 — все переключали настольную лампу 220 В. Для первых 4 выходов потребовалась инверсия сигнала (логический 0 вместо 1) через схему на CD4011 (четырёхканальный NAND). Внимание: работа с сетевым напряжением опасна!
Код (Arduino IDE)
/* Toggle 6 LEDs using an IR remote — repurposing surplus remotes.
Learns 6 IR codes in "Learn" mode (toggle switch), saves to EEPROM.
In "Run" mode toggles 6 outputs. Can drive relays, transistors, etc.
Paul M Dunphy VE1DX, March 2020 */
#include <IRremote.h>
#include <EEPROM.h>
long unsigned int intIRCode;
long unsigned int savedIRCodes[6];
long unsigned int dupeCheck[6];
const int Recv_Pin = 2;
const int PinOne = 12, PinTwo = 11, PinThree = 10, PinFour = 9, PinFive = 8, PinSix = 7;
const int switchPin = 4;
const int statusPin = 5;
const unsigned long int repeatKeyPress = 0xFFFFFFFF;
boolean learnMode, first_iteration;
int togglestate1=0, togglestate2=0, togglestate3=0, togglestate4=0, togglestate5=0, togglestate6=0;
int current_remote_code;
int remote_code_1, remote_code_2, remote_code_3, remote_code_4, remote_code_5, remote_code_6;
IRrecv irrecv(Recv_Pin);
decode_results results;
// EEPROM helper: write 32-bit long
void EEPROMWritelong(int address, long value) {
byte four = (value & 0xFF);
byte three = ((value >> 8) & 0xFF);
byte two = ((value >> 16) & 0xFF);
byte one = ((value >> 24) & 0xFF);
EEPROM.write(address, four);
EEPROM.write(address + 1, three);
EEPROM.write(address + 2, two);
EEPROM.write(address + 3, one);
}
long EEPROMReadlong(long address) {
long four = EEPROM.read(address);
long three = EEPROM.read(address + 1);
long two = EEPROM.read(address + 2);
long one = EEPROM.read(address + 3);
return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & repeatKeyPress);
}
int Flip_LED(int led, int toggle_state) {
if (toggle_state == 0) { digitalWrite(led, HIGH); toggle_state = 1; }
else { digitalWrite(led, LOW); toggle_state = 0; }
return toggle_state;
}
void Reset() {
digitalWrite(PinOne, LOW); digitalWrite(PinTwo, LOW); digitalWrite(PinThree, LOW);
digitalWrite(PinFour, LOW); digitalWrite(PinFive, LOW); digitalWrite(PinSix, LOW);
togglestate1 = togglestate2 = togglestate3 = togglestate4 = togglestate5 = togglestate6 = 0;
}
void guessType() {
Serial.print("Remote appears to be a ");
switch (results.decode_type) {
case NEC: Serial.println("NEC"); break;
case SONY: Serial.println("SONY"); break;
case RC5: Serial.println("RC5"); break;
case RC6: Serial.println("RC6"); break;
case DISH: Serial.println("DISH"); break;
case SHARP: Serial.println("SHARP"); break;
case JVC: Serial.println("JVC"); break;
case SANYO: Serial.println("SANYO"); break;
case MITSUBISHI: Serial.println("MITSUBISHI"); break;
case SAMSUNG: Serial.println("SAMSUNG"); break;
case LG: Serial.println("LG"); break;
case WHYNTER: Serial.println("WHYNTER"); break;
case AIWA_RC_T501: Serial.println("AIWA_RC_T501"); break;
case PANASONIC: Serial.println("PANASONIC"); break;
case DENON: Serial.println("DENON"); break;
default: Serial.println("UNKNOWN"); break;
}
}
int learnCodeRead(int pinCode) {
if (irrecv.decode(&results)) pinCode = results.value;
return pinCode;
}
void Confirm() {
for (int i=0; i<=20; i++) { digitalWrite(statusPin, HIGH); delay(50); digitalWrite(statusPin, LOW); delay(50); }
digitalWrite(statusPin, HIGH);
}
void learn_Mode() {
boolean goodCode; int i, j, location = 0;
int pins[6] = {12,11,10,9,8,7};
if (first_iteration) { Serial.println(); Serial.println("Entering learn mode"); Serial.println(); }
intIRCode = 0; goodCode = true; j = 0;
while (goodCode && j <= 5) {
for (i=0; i<=25; i++) {
digitalWrite(pins[j], HIGH); delay(200);
intIRCode = learnCodeRead(intIRCode);
digitalWrite(pins[j], LOW); delay(200);
intIRCode = learnCodeRead(intIRCode);
goodCode = (intIRCode != repeatKeyPress && intIRCode != 0);
if (goodCode) i = 30;
irrecv.resume();
}
goodCode = (intIRCode != repeatKeyPress && intIRCode != 0);
if (goodCode) {
if (j == 0) guessType();
Serial.print("Writing to EEPROM Location "); Serial.print(location);
Serial.print(" IR code = "); Serial.println(intIRCode, HEX);
EEPROMWritelong(location, intIRCode);
location += 4; j++; Confirm(); intIRCode = 0; irrecv.resume();
}
}
Serial.println(); Serial.println("Put Arduino back in run mode.");
while (digitalRead(switchPin) == HIGH) { digitalWrite(statusPin, HIGH); delay(1000); digitalWrite(statusPin, LOW); delay(1000); }
Serial.println(); Serial.println("Returning to run mode.");
delay(50); Serial.flush(); delay(50); asm volatile ("jmp 0");
}
void run_Mode() {
if (first_iteration) Serial.println("Entering run mode");
if (irrecv.decode(&results)) {
if (results.value != repeatKeyPress) {
current_remote_code = results.value;
Serial.print("Key press detected, IR code = "); Serial.println(current_remote_code, HEX);
if (current_remote_code == remote_code_1) togglestate1 = Flip_LED(PinOne, togglestate1);
else if (current_remote_code == remote_code_2) togglestate2 = Flip_LED(PinTwo, togglestate2);
else if (current_remote_code == remote_code_3) togglestate3 = Flip_LED(PinThree, togglestate3);
else if (current_remote_code == remote_code_4) togglestate4 = Flip_LED(PinFour, togglestate4);
else if (current_remote_code == remote_code_5) togglestate5 = Flip_LED(PinFive, togglestate5);
else if (current_remote_code == remote_code_6) togglestate6 = Flip_LED(PinSix, togglestate6);
else Reset();
}
delay(500); irrecv.resume();
}
}
void setup() {
first_iteration = true;
Serial.begin(9600);
irrecv.enableIRIn();
pinMode(PinOne, OUTPUT); pinMode(PinTwo, OUTPUT); pinMode(PinThree, OUTPUT);
pinMode(PinFour, OUTPUT); pinMode(PinFive, OUTPUT); pinMode(PinSix, OUTPUT);
Reset();
pinMode(statusPin, OUTPUT); pinMode(switchPin, INPUT);
// Проверка "сырого" EEPROM (0xFFFFFFFF) — подмена на коды Sony RM-YD104
boolean defaultToSony = false;
long unsigned int IRCode = 0; int location = 0;
for (int i=0; i<=5; i++) { IRCode = EEPROMReadlong(location); if (IRCode == repeatKeyPress) defaultToSony = true; location += 4; }
if (defaultToSony) {
Serial.println("HEX FFFFFFFF found in EEPROM. Setting codes for Sony RM-YD104 remote.");
EEPROMWritelong(0, 0x10); delay(50);
EEPROMWritelong(4, 0x810); delay(50);
EEPROMWritelong(8, 0x410); delay(50);
EEPROMWritelong(12, 0xC10); delay(50);
EEPROMWritelong(16, 0x210); delay(50);
EEPROMWritelong(20, 0xA10); delay(50);
}
// Чтение сохранённых кодов
Serial.println(); Serial.println("Reading stored IR remote codes ...");
location = 0;
for (int j=0; j<=5; j++) {
savedIRCodes[j] = EEPROMReadlong(location);
Serial.print("Location "); Serial.print(location); Serial.print(" = "); Serial.println(savedIRCodes[j], HEX);
location += 4; dupeCheck[j] = savedIRCodes[j];
}
// Проверка на дубликаты (последовательные)
for (int i=0; i<5; i++) for (int j=i+1; j<6; j++)
if (dupeCheck[i] == dupeCheck[j]) {
Serial.println("Duplicate codes found. Suggest re-running learn mode");
for (int k=0; k<=5; k++) {
digitalWrite(pins[i], HIGH); digitalWrite(pins[j], HIGH); delay(1000);
digitalWrite(pins[i], LOW); digitalWrite(pins[j], LOW); delay(1000);
}
}
remote_code_1 = savedIRCodes[0]; remote_code_2 = savedIRCodes[1];
remote_code_3 = savedIRCodes[2]; remote_code_4 = savedIRCodes[3];
remote_code_5 = savedIRCodes[4]; remote_code_6 = savedIRCodes[5];
delay(1000); Serial.println("Stored codes read."); Serial.println();
}
void loop() {
learnMode = (digitalRead(switchPin) == HIGH);
if (learnMode) { first_iteration = true; Reset(); digitalWrite(statusPin, HIGH); learn_Mode(); Reset(); first_iteration = false; }
else { digitalWrite(statusPin, LOW); run_Mode(); first_iteration = false; }
}
Схема подключения
См. раздел «Schematics» оригинала: два варианта — с 6 светодиодами через MOSFET и с 5-вольтовым питанием / 3.3-вольтовой логикой.
Производственный процесс
- Светодиоды (LED): от зарождения к 100-летнему ресурсу
- Удалённый мониторинг погоды на Raspberry Pi 3 с PubNub
- Создание профессионального универсального пульта на Raspberry Pi с LIRC
- Система удалённого мониторинга сердечного ритма с алертами AWS IoT
- Проект Arduino: мелодия Coffin Dance — сборка и воспроизведение вирусного трека
- Превращаем старый кнопочный телефон в умный удаленный выключатель
- Рисование светом одним светодиодом: построение динамических изображений на DIY-манипуляторе
- Звукоактивируемый светодиодный дисплей 8 LED на Arduino
- 5 ключевых критериев выбора контроллерной платформы
- Безопасная и эффективная утилизация устаревшего промышленного оборудования