====== Универсальный пульт управления из старого телефона ====== {{ :projects:ir-repeater:161124_lin_3808.jpg?nolink |}} * Платформы: Iskra Neo / Arduino Leonardo * Языки программирования: C++ * Версия Arduino IDE: 1.6.9 * Тэги: умный дом, ИК-управление, пульт дистанционного управления, bluetooth, RemoteXY, android. ===== Что это? ===== У каждого есть целая коллекция пультов управления от бытовых приборов. Часто мы нажимаем одни и те же последовательности кнопок сначала на одном пульте, затем на другом. Было бы здорово иметь для таких случаев только одну кнопку. А чтобы не искать эту кнопку по всей квартире, мы поместим её в смартфон, который всегда с собой. ===== Что нам понадобится? ===== {{ :projects:ir-repeater:161124_lin_3825.jpg?nolink |}} - [[amp>product/iskra-neo?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Iskra Neo]] - [[amp>product/arduino-troyka-slot-shield?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Troyka Slot Shield]] - [[amp>product/troyka-bluetooth-hc-05?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Bluetooth HC-05 (Troyka-модуль)]] - [[amp>product/troyka-ir-transmitter-5mm-led-module?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|ИК-передатчик (Troyka-модуль)]] - [[amp>product/troyka-ir-receiver?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|ИК-приёмник (Troyka-модуль)]] - [[amp>product/structor-slot-box?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Slot Box (#Структор)]] - [[amp>product/usb-cable-micro?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Кабель USB (A — Micro USB)]] - [[amp>product/usb-power-plug-3a?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Импульсный блок питания с USB-разъёмом (5 В, 3100 мА)]] - 8 × [[amp>product/nylon-screw-m3x8?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Винт М3]] - [[amp>product/box-cutter?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Канцелярский нож]] - Android-смартфон - [[https://play.google.com/store/apps/details?id=com.shevauto.remotexy.free|Приложение RemoteXY]] Все электронные компоненты собраны в коллекцию [[http://amperka.ru/collection/IR-repeater?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|«ИК-репитер»]] ===== Как собрать? ===== - Используя канцелярский нож, извлеките детали #Slot Box-а. - Вкрутите винты М3 в плашку #структора для крепления Arduino.{{ :projects:ir-repeater:161124_lin_3821.jpg?nolink |}} - Разместите Iskra Neo на винтах{{ :projects:ir-repeater:161124_lin_3817.jpg?nolink |}} - Установите Troyka Slot Shield на Arduino. Установите боковые стенки корпуса. {{ :projects:ir-repeater:161124_lin_3816.jpg?nolink |}} - Установите лицевую панель на #Slot Box {{ :projects:ir-repeater:161124_lin_3813.jpg?nolink |}} - Соедините ИК-приёмник с пином ''3'', ИК-передатчик с пином ''9''. Соедините пин ''TX'' Bluetooth-модуля с пином ''0'', а контакт ''RX'' с пином ''1''. Закрепите модули винтами М3 {{ :projects:ir-repeater:161124_lin_3812.jpg?nolink |}} ===== Исходный код ===== Мы разделили код по функционалу на отдельные файлы. Проще всего скачать проект целиком из репозитория на [[https://github.com/amperka/IR-repeater|GitHub]]. ==== Скетч ==== // Подключаем код для работы ИК-приёмника и передатчика #include "IRremote_header.h" // Подключаем код для работы с приложением remoteXY #include "remoteXY_header.h" // Подключаем код с логикой работы с энергонезависимой памятью #include "eeprom_logic.h" // Подключаем код с логикой работы с ИК-командами #include "IRremote_logic.h" // Переменная для хранения состояния "запись ИК-сигналов" bool recordState = false; // Переменная для хранения состояния кнопки записи bool recButtonLastState = false; // Переменная для хранения номера последеней нажатой кнопки на телефоне byte buttonLast = 0; void setup() { // Инициализируем remoteXY RemoteXY_Init (); // Запускаем работу ИК-приёмника (IRremote_header.h) irrecv.enableIRIn(); // Если у нас есть записанные комбинации ИК-сигналов - достаём их (eeprom_logic.h) eepromDataInit(); } void loop() { // Опрашиваем remoteXY. Не пришли ли новые команды по bluetooth? RemoteXY_Handler (); // Запоминаем состояние кнопки record на смартфоне. bool recButtonState = RemoteXY.record; // Если состояние кнопки изменилось... if (recButtonLastState != recButtonState) { // ... запомним новое состояние этой кнопки. recButtonLastState = recButtonState; // Если кнопка record нажата... if (recButtonState) { // меняем состояние "запись ИК-сигналов" на противоположное recordState = !recordState; } // Если мы вошли в состояние записи... if (recordState) { // ... зажгём красный "светодиод" на смартфоне. RemoteXY.recMode_r = 255; // Если мы вышли из состояния записи } else { // ... сохраним записанные данные ИК-пульта saveAll(); // и потушим светодиод RemoteXY.recMode_r = 0; } } // Если на смартфоне была нажата новая кнопка... if (RemoteXY.buttons != buttonLast) { // ... сохраним её значение. buttonLast = RemoteXY.buttons; // Еслим мы в состоянии записи — сохраним записанные данные ИК-пульта, // если нет — отправим ИК-сигналы, соответствующие этой кнопке (IRremote_logic.h) recordState ? saveAll() : sendButton(buttonLast); } // Если мы в состоянии записи, и пришла новая команда с ИК-пульта... if ((recordState) && (irrecv.decode(&results))) { // сохраним команду с ИК-пульта (IRremote_logic.h) в последовательность команд текущей кнопки storeCode(&results, buttonLast); // перезапустим ИК-приёмник irrecv.resume(); } } // Функция для сохраняем данных с ИК-пульта void saveAll() { // Если была принята хотя бы одна команда с ИК-пульта if (macroPos > 0) { // записываем принятую последовательность команд (eeprom_logic.h) saveIRToEEPROM(); // обнуляем счётчик команд macroPos = 0; } } ==== Код инициализации ИК-приёмника и передатчика ==== // Библиотека для работы с ИК-приёмником и передатчиком #include // Имя пина, которому подключен приёмник #define RECV_PIN 3 // Имя пина, к которому подключен передатчик #define SEND_PIN 9 // Максимальная длина последовательности комманд ИК-пульта #define IR_BUTTONS_COUNT 15 // Максимальное количество таких последовательностей // Должно быть равно количеству кнопок в remoteXY #define MACROS_COUNT 5 // Создаём объект для ИК-приёмника IRrecv irrecv(RECV_PIN); // Создаём объект для ИК-передатчика IRsend irsend; // Создаём объект для декодированных команд с ИК-пульта decode_results results; // Структура для хранения команд с ИК-пульта struct irData { // Тип принятой команды: UNKNOWN, NEC, SONY, RC5, ... decode_type_t decode_type = UNKNOWN; // Адрес устройства. Используется в пультах Panasonic и Sharp unsigned int address = 0; // Значение декодированной ИК-команды unsigned long value = 0; // Длина команды в битах int bits = 0; }; // Массив для хранения последовательностей ИК-команд irData irSygnals[MACROS_COUNT][IR_BUTTONS_COUNT]; // Счётчик принятых команд в текущей последовательности byte macroPos = 0; ==== Логика работы с ИК-командами ==== // Функция для отправки последовательности команд // принимает на вход номер последовательности void sendButton(byte button) { // Переменная для хранения бита переключения. Нужна в протоколах RC5, RC6 int toggle = 0; // Перебираем в цикле всю последовательность команд for (int i = 0; i < IR_BUTTONS_COUNT; ++i) { // Сохраняем текущую команду во вспомогательные переменные decode_type_t decode_type = irSygnals[button][i].decode_type; unsigned int address = irSygnals[button][i].address; unsigned long codeValue = irSygnals[button][i].value; int codeLen = irSygnals[button][i].bits; // Если протокол команды известен if (decode_type != UNKNOWN) { // Если протокол команды RC5 или RC6 if (decode_type == RC5 || decode_type == RC6) { // Инвертируем бит переключения, toggle ^= 1; // обнулим бит переключения в самом значении команды, codeValue = codeValue & ~(1 << (codeLen - 1)); // и запишем в это место наш бит переключения... Вот такой уж протокол:) codeValue = codeValue | (toggle << (codeLen - 1)); // Если протокол RC5, if (decode_type == RC5) { // отправим ИК-команду по протоколу RC5 irsend.sendRC5(codeValue, codeLen); // Если протокол RC6, } else { // отправим ИК-команду по протоколу RC6 irsend.sendRC6(codeValue, codeLen); } } // В зависимости от протокола команды, используем соответствующие // этому протоколу функции из библиотеки IRremote switch (decode_type) { case NEC: irsend.sendNEC(codeValue, codeLen); break; case SONY: irsend.sendSony(codeValue, codeLen); break; case PANASONIC: irsend.sendPanasonic(address, codeValue); break; case JVC: irsend.sendJVC(codeValue, codeLen, false); break; case SAMSUNG: irsend.sendSAMSUNG(codeValue, codeLen); break; case WHYNTER: irsend.sendWhynter(codeValue, codeLen); break; case LG: irsend.sendLG(codeValue, codeLen); break; case DISH: irsend.sendDISH(codeValue, codeLen); break; case SHARP: irsend.sendSharp(address, codeValue); } // Подождём выполнения команды бытовыми приборами delay(1000); // если команда неизвестна — выходим из функции. } else { return; } } } // Функция для сохранения принятой ИК-команды // принимает на вход декодированную ИК-команду, номер текущей последовательности // (т.е. номер нажатой кнопки) и позицию команды в текущей последовательности void storeCode(decode_results *results, int buttonNumber) { // Если позиция команды меньше, чем максимальное количество команд в последовательности if (macroPos < IR_BUTTONS_COUNT) { // Если протокол команды известен, и это не команда повтора команды if ((results->decode_type != UNKNOWN) && (results->value != REPEAT)) { // Сохраняем команду в массив хранения последовательностей команд irSygnals[buttonNumber][macroPos].decode_type = results->decode_type; irSygnals[buttonNumber][macroPos].address = results->address; irSygnals[buttonNumber][macroPos].value = results->value; irSygnals[buttonNumber][macroPos].bits = results->bits; //увеличиваем счётчик принятых комманд macroPos += 1; // Пометим следующую команду как неизвестную, чтобы // остановится на этом месте в функции отправки последовательностей команд if (macroPos < IR_BUTTONS_COUNT) irSygnals[buttonNumber][macroPos].decode_type = UNKNOWN; } } } ==== Логика работы с энергонезависимой памятью ==== // Подключаем библиотеку для работы с энергонезависимой памятью #include // Функция для загрузки сохранённых последовательностей ИК-команд void eepromDataInit() { // Волшебное число int notFirstStartVal = 12345; // Переменная для считанного из EEPROM волшебного числа int val; // Если число, считанное по адресу 0 не равно волшебному... if (EEPROM.get(0, val) != notFirstStartVal) { // ... значит мы только что прошили скетч, и в EEPROM лежит мусор // Кладём в EEPROM волшебное число по адресу 0 EEPROM.put(0, notFirstStartVal); // Кладём в EEPROM пустой массив для хранения последовательностей ИК-команд // начиная с адреса 0 + размер волшебного числа EEPROM.put(sizeof(notFirstStartVal), irSygnals); } else { // Если волшебное число лежит в EEPROM, значит в EEPROM не мусор // Достаём записанные последовательности ИК-команд EEPROM.get(sizeof(notFirstStartVal), irSygnals); } } // Функция для записи последовательностей ИК-команд в энергонезависимую память void saveIRToEEPROM() { // Записываем последовательности, начиная с адреса 0 + размер волшебного числа EEPROM.put(sizeof(int), irSygnals); } ==== Автоматически сгенерированный код редактора RemoteXY ==== RemoteXY предоставляет инструмент online-конструктора интерфейсов для смартфона. Это кнопки, тумблеры, джойстики и другие элементы управления. Конструктор нашего интерфейса можно посмотреть и отредактировать на свой вкус [[http://remotexy.com/en/editor/a1cf60078a90eb8434b694227961932b/|по ссылке]]. // Файл автоматически сгенерирован online-конструктором // форм для смартфона remoteXY // http://remotexy.com/en/editor/a1cf60078a90eb8434b694227961932b/ ////////////////////////////////////////////// // RemoteXY include library // // use library version 2.2.5 or up // // use ANDROID app version 3.7.1 or up // ////////////////////////////////////////////// /* RemoteXY select connection mode and include library */ #define REMOTEXY_MODE__HC05_HARDSERIAL #include /* RemoteXY connection settings */ #define REMOTEXY_SERIAL Serial1 #define REMOTEXY_SERIAL_SPEED 9600 /* RemoteXY configurate */ #pragma pack(push, 1) uint8_t RemoteXY_CONF[] = { 2,1,26,0,6,5,0,3,133,1 ,10,98,11,2,1,0,63,40,12,12 ,1,82,0,65,4,25,40,12,12,2 }; /* this structure defines all the variables of your control interface */ struct { /* input variable */ uint8_t buttons; /* =0 if select position A, =1 if position B, =2 if position C, ... */ uint8_t record; /* =1 if button pressed, else =0 */ /* output variable */ uint8_t recMode_r; /* =0..255 LED Red brightness */ /* other variable */ uint8_t connect_flag; /* =1 if wire connected, else =0 */ } RemoteXY; #pragma pack(pop) ///////////////////////////////////////////// // END RemoteXY include // ///////////////////////////////////////////// ===== Как пользоваться? ===== {{ :projects:ir-repeater:remotexy_display.png?nolink |}} * Включите устройство. * Запустите приложение remoteXY на Android-смартфоне. * Нажмите на кнопку выбора последовательности ИК-команд. * Нажмите кнопку записи. Загорится индикатор записи. * Наведите на устройство ИК-пульт, и нажмите несколько кнопок. * Повторно нажмите кнопку записи. Индикатор записи погаснет. * Нажмите кнопку выбора последовательности, с записанной последовательностью. Устройство воспроизведёт последовательность ИК-команд. ===== Альтернативный вариант сборки ===== Бывает так, что #Структор не подходит к интерьеру. Попробуем собрать устройство в стиле IKEA. {{ :projects:ir-repeater:161124_lin_3778.jpg?nolink&300 |}} ==== Что нам понадобится? ==== {{ :projects:ir-repeater:161124_lin_3828.jpg?nolink |}} - [[amp>product/iskra-neo?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Iskra Neo]] - [[amp>product/arduino-troyka-shield-lp?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Troyka Shield LP]] - [[amp>product/troyka-bluetooth-hc-05?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Bluetooth HC-05 (Troyka-модуль)]] - [[amp>product/troyka-ir-transmitter-5mm-led-module?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|ИК-передатчик (Troyka-модуль)]] - [[amp>product/troyka-ir-receiver?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|ИК-приёмник (Troyka-модуль)]] - [[amp>product/toggle-switch?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Тумблер]] - [[amp>product/krona-battery?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Батарейка Крона]] - [[amp>product/krona-21mm-cable?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|Кабель питания от батарейки Крона]] - Суперклей - Канцелярский нож - Android-смартфон - [[https://play.google.com/store/apps/details?id=com.shevauto.remotexy.free|Приложение RemoteXY]] - Козлик из IKEA ==== Как собрать? ==== - Распотрошим козлика канцелярским ножом. Порежем пополам, сделаем отверстия под ИК-приёмник, ИК-передатчик и тумблер {{ :projects:ir-repeater:161124_lin_3795.jpg?nolink |}} - Вклеим ИК-приёмник и ИК-передатчик в козлика при помощи суперклея {{ :projects:ir-repeater:161124_lin_3790.jpg?nolink |}} {{ :projects:ir-repeater:161124_lin_3792.jpg?nolink |}} {{ :projects:ir-repeater:161124_lin_3793.jpg?nolink |}} - Установите Troyka Shield LP на Iskra Neo. Соедините ИК-приёмник с пином ''3'', ИК-передатчик с пином ''9''. Соедините пин ''TX'' Bluetooth-модуля с пином ''0'', а контакт ''RX'' с пином ''1''.{{ :projects:ir-repeater:161124_lin_3788.jpg?nolink |}} - Установите электронику в козлика {{ :projects:ir-repeater:161124_lin_3787.jpg?nolink |}} - Разрежьте красный провод кабеля питания от батарейки Крона и зачистите концы. Прикрутите контакты к тумблеру {{ :projects:ir-repeater:161124_lin_3797.jpg?nolink |}} - Подключите питание к Iskra Neo. {{ :projects:ir-repeater:161124_lin_3781.jpg?nolink |}} - Установите тумблер в отверстие для него и зафиксируйте гайкой {{ :projects:ir-repeater:161124_lin_3779.jpg?nolink |}} ===== Демонстрация работы устройства ===== {{youtube>7rVPqa1X6UI?large}} ===== Что дальше? ===== Добавьте [[amp>product/troyka-rtc?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|часы реального времени]] и управляйте бытовыми приборами по расписанию. Используйте [[amp>product/troyka-mini-relay?utm_source=proj&utm_campaign=IR-repeater&utm_medium=wiki|реле]], для управления приборами поглупее.