У каждого есть целая коллекция пультов управления от бытовых приборов. Часто мы нажимаем одни и те же последовательности кнопок сначала на одном пульте, затем на другом. Было бы здорово иметь для таких случаев только одну кнопку. А чтобы не искать эту кнопку по всей квартире, мы поместим её в смартфон, который всегда с собой.
3
, ИК-передатчик с пином 9
. Соедините пин TX
Bluetooth-модуля с пином 0
, а контакт RX
с пином 1
. Закрепите модули винтами М3 Мы разделили код по функционалу на отдельные файлы. Проще всего скачать проект целиком из репозитория на 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 <IRremote.h> // Имя пина, которому подключен приёмник #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 <EEPROM.h> // Функция для загрузки сохранённых последовательностей ИК-команд 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 предоставляет инструмент online-конструктора интерфейсов для смартфона. Это кнопки, тумблеры, джойстики и другие элементы управления. Конструктор нашего интерфейса можно посмотреть и отредактировать на свой вкус по ссылке.
// Файл автоматически сгенерирован 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.h> /* 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 // /////////////////////////////////////////////
Бывает так, что #Структор не подходит к интерьеру. Попробуем собрать устройство в стиле IKEA.
3
, ИК-передатчик с пином 9
. Соедините пин TX
Bluetooth-модуля с пином 0
, а контакт RX
с пином 1
.Добавьте часы реального времени и управляйте бытовыми приборами по расписанию. Используйте реле, для управления приборами поглупее.