ESP8266:Примеры/Управление RGB-лампочкой с помощью ИК-передатчика и ESP8266

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин (Cubewriter) Контакты:</br>* Skype: cubewriter</br>* E-mail: cubewriter@gmail.com</br>* Максим Кузьмин на freelance.ru
Проверка/Оформление/Редактирование: Мякишев Е.А.


Cat poo.png Черновик


Управление RGB-лампочкой с помощью ИК-передатчика и ESP8266

В этом проекте мы научимся использовать ИК-передатчик для управления RGB-светодиодной ИК-лампочкой при помощи веб-сервера на базе ESP8266. Мы сможем отправлять команды для включения/выключения лампочки, уменьшения/увеличения ее яркости, переключения ее на красный, зеленый, синий, фиолетовый цвета и т.д. Проект будет поделен на 2 части:

  1. Расшифровка ИК-сигналов, отправляемых пультом управления лампочкой
  2. Настройка ESP8266 на отправление этих ИК-сигналов

Передача и прием ИК-сигналов

На фото ниже изображен ИК-приемник TSOP4838.

TSOP4838 pinout 1.jpg

Как видите, он оснащен 3 контактами:

  • OUT (вывод данных);
  • GND (Заземление);
  • VCC (Источник напряжения);

Когда вы нажимаете на кнопки на пульте управления, он отправляет модулированные ИК-сигналы. Они содержат информацию, принимаемую ИК-приемником.

IR emitter.png

Каждая кнопка отправляет собственный ИК-сигнал. Таким образом, мы можем привязать каждый из этих ИК-сигналов к кнопкам на веб-странице, обслуживаемой ESP8266. Затем эти сигналы – отправляемые ИК-передатчиком, подключенным к ESP8266 – можно будет использовать для управления лампочкой.

Необходимое оборудование

Часть 1 – Расшифровка ИК-сигналов

В этой части проекта мы расшифруем ИК-сигналы, привязанные к каждой кнопке, чтобы потом эмулировать их при помощи ESP8266.

Схема

Подключите ИК-приемник и ESP8266 друг к другу согласно схеме ниже.

Esp8266 tsop connect 1.png
Примечание

Данная схема приведена для ИК приемника TSOP4838, другие модели могут иметь различную распиновку.

Код

Для работы с ИК-передатчиком и ИК-приемником нам нужно будет установить в IDE Arduino библиотеку «IRremoteESP8266». Для этого кликните в IDE Arduino на «Скетч» > «Подключить библиотеку» > «Управлять библиотеками...» (Sketch > Include Library > Manage Libraries…).

Затем вбейте в поиске «ir remote esp8266» и, найдя нужную библиотеку, кликните по кнопке «Установка» (Install). Или можете загрузить ее ZIP-архив отсюда и установить вручную.

Скопируйте код ниже в IDE Arduino, а затем загрузите его на ESP8266. Перед этим проверьте, правильные ли в IDE Arduino выбраны плата и COM-порт.

/*
 * https://github.com/markszabo/IRremoteESP8266/blob/master/examples/IRrecvDemo/IRrecvDemo.ino
 *
 * IRremoteESP8266: IRrecvDemo 
 *
 * Демонстрирует получение ИК-кодов при помощи библиотеки «IRrecv».
 * Ко входному контакту RECV_PIN 
 * должен быть подключен ИК-детектор/демодулятор.
 * Авторские права принадлежат Кену Ширриффу (2009), http://arcfn.com
 * Изменения:
 *   Версия 0.2, июнь 2017
 *    - Изменен GPIO-контакт
        (теперь он такой же, как и в других примерах);
 *    - Для печати «uint64_t» используется наш собственный метод;
 *    - Скорость передачи данных изменена на 115200 бод.
 *   Версия 0.1, сентябрь 2015
 *    - Основана на скетче «IrsendDemo» (версия 0.1, июль 2009), 
 *      написанном Кеном Ширриффом
 */

#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRutils.h>

// ИК-детектор/демодулятор подключен к контакту GPIO14
// (на плате NodeMCU это контакт D5):
uint16_t RECV_PIN = 14;

IRrecv irrecv(RECV_PIN);

decode_results results;

void setup() {
  Serial.begin(115200);
  irrecv.enableIRIn();  // запускаем приемник
}

void loop() {
  if (irrecv.decode(&results)) {
    // print() и println() не умеют 
    // обрабатывать печать вместительных типов данных  (uint64_t):
    serialPrintUint64(results.value, HEX);
    Serial.println("");
    irrecv.resume();  // получаем следующее значение
  }
  delay(100);
}

Открываем монитор порта на скорости 115200 бод.

В этом проекте мы будем эмулировать 10 кнопок пульта управления. Нажмите на пульте на кнопку «ON». В результате в мониторе порта должен появиться код для этой кнопки.

Примечание

На одну и ту же кнопку можно нажать несколько раз – чтобы убедиться, что код получен именно для той кнопки, что вам нужна. Если вы увидите что-то вроде «FFFFFFFF», просто игнорируйте, это мусор.

Esp8266 tsop ir reciever serial monitor 1.PNG

Повторите этот процесс и для других кнопок. Запишите коды для всех кнопок, т.к. позже они нам еще понадобятся. На картинке ниже показаны ИК-сигналы для всех нужных нам кнопок на пульте управления.

Remote buttons codes 1.png

Часть 2 – Настройка ESP8266 на отправку ИК-сигналов

В этой части мы построим цепь из светодиодного ИК-передатчика, управляемого с помощью веб-сервера на базе ESP8266.

Схема

Подключите ИК-передатчик к ESP8266 согласно схеме ниже.

Esp8266 ir led 1.png

ИК-передатчик нужно поместить рядом с лампочкой, чтобы она могла принимать отправляемые им ИК-сигналы.

Код

Код ниже запустит на вашей ESP8266 веб-сервер, передающий данные через порт «80». Веб-сервер будет показывать в браузере простую веб-страницу с 10 кнопками, управляющими RGB-светодиодной лампочкой.

Скопируйте код ниже в IDE Arduino, но пока не загружайте.

Сначала прочтите следующий раздел, в котором рассказывается, как отредактировать этот скетч, чтобы он заработал на вашей ESP8266.

Примечание

Если у вас код ниже работает нестабильно, то вы можете попробовать вот этот альтернативный вариант.

/*
 * Скетч для управления RGB-светодиодной ИК-лампочкой с помощью ESP8266 
 * Руи Сантос 
 * Более подробно о проекте на: http://randomnerdtutorials.com
 */

#include <ESP8266WiFi.h>

#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>

// ИК-передатчик управляется через контакт GPIO4 (D2):
IRsend irsend(4);  

// Вставьте здесь SSID и пароль для своей WiFi-сети:
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Переменная для хранения информации о том,
// какая кнопка была последней нажата на веб-странице:
String lastState;

// Создаем объект для сервера с портом «80»:
WiFiServer server(80);

void setup() {
  irsend.begin();
  
  Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
  delay(10);

  // Подготавливаем контакт GPIO4:
  pinMode(4, OUTPUT);
  digitalWrite(4, 0);
  
  // Подключаемся к WiFi-сети:
  Serial.print("Connecting to ");  //  "Подключаемся к "
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");  //  "Подключились к WiFi"
  
  // Запускаем сервер:
  server.begin();
  Serial.println("Server started");  //  "Сервер запущен"

  // Печатаем IP-адрес:
  Serial.println(WiFi.localIP());
}

void loop() {
  // Проверяем, подключился ли клиент:
  WiFiClient client = server.available();
  if (!client) {
    return;
  }
  
  // Ждем, пока клиент отправит какие-нибудь данные:
  Serial.println("new client");  //  "новый клиент"
  while(!client.available()){
    delay(1);
  }
  
  // Считываем первую строчку запроса:
  String req = client.readStringUntil('\r');
  Serial.println(req);
  client.flush();
  
  // Смотрим, какой именно запрос отправлен:
  int irCode;
  if (req.indexOf("/lamp/on") != -1) {
    irCode = 0xFFE01F;
    lastState = "on";
  }
  else if (req.indexOf("/lamp/off") != -1) {
    irCode = 0xFF609F;
    lastState = "off";
  }
  else if (req.indexOf("/lamp/brighten") != -1) {
    irCode = 0xFFA05F;
    lastState = "brighten";
  }
  else if (req.indexOf("/lamp/dim") != -1) {
    irCode = 0xFF20DF;
    lastState = "dim";
  }
  else if (req.indexOf("/lamp/red") != -1) {
    irCode = 0xFF906F;
    lastState = "red";
  }
  else if (req.indexOf("/lamp/green") != -1) {
    irCode = 0xFF10EF;
    lastState = "green";
  }
  else if (req.indexOf("/lamp/blue") != -1) {
    irCode = 0xFF50AF;
    lastState = "blue";
  }
  else if (req.indexOf("/lamp/yellow") != -1) {
    irCode = 0xFF8877;
    lastState = "yellow";
  }
  else if (req.indexOf("/lamp/cyan") != -1) {
    irCode = 0xFF708F;
    lastState = "cyan";
  }
  else if (req.indexOf("/lamp/purple") != -1) {
    irCode = 0xFF6897;
    lastState = "purple";
  }
  else {
    lastState = "none";
    Serial.println("Invalid request - IR signal not sent");
               //  "Неправильный запрос – ИК-сигнал не отправлен"
  }
  // Отправляем ИК-сигнал RGB-светодиодной лампочке:
  Serial.print("NEC: ");
  Serial.println(irCode, HEX);
  irsend.sendNEC(irCode, 32);
  client.flush();

  // Готовим ответ клиенту (содержит веб-страницу):
  String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>";
  s += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"></head>";  
  s += "<h1>RGB Lamp</h1>Last state: ";
  s += lastState;
  s += "<p><a href=\"/lamp/on\"><button>ON</button></a><a href=\"/lamp/off\"><button>OFF</button></a></p>";
  s += "<p><a href=\"/lamp/brighten\"><button>BRIGHTEN</button></a><a href=\"/lamp/dim\"><button>DIM</button></a></p>";
  s += "<p><a href=\"/lamp/red\"><button>RED</button></a><a href=\"/lamp/green\"><button>GREEN</button></a><a href=\"/lamp/blue\"><button>BLUE</button></a></p>";
  s += "<p><a href=\"/lamp/yellow\"><button>YELLOW</button></a><a href=\"/lamp/cyan\"><button>CYAN</button></a><a href=\"/lamp/purple\"><button>PURPLE</button></a></p>";
  s += "</html>\n";

  // Отправляем ответ клиенту и отключаем его:
  client.print(s);
  delay(1);
  Serial.println("Client disconnected");  //  "Клиент отключен"
  client.stop();
}

Редактируем скетч

Перед загрузкой скетча в него нужно вписать SSID и пароль для своей WiFi-сети:

// Вставьте здесь SSID и пароль для вашей WiFi-сети:
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Теперь возьмите ИК-коды, найденные в части 1, и впишите их в переменные «irCode», соответствующие нужным командам.

// Смотрим, какой именно запрос отправлен:
  int irCode;
  if (req.indexOf("/lamp/on") != -1) {
    irCode = 0xFFE01F;
    lastState = "on";
  }
  else if (req.indexOf("/lamp/off") != -1) {
    irCode = 0xFF609F;
    lastState = "off";
  }
  else if (req.indexOf("/lamp/brighten") != -1) {
    irCode = 0xFFA05F;
    lastState = "brighten";
  }
  else if (req.indexOf("/lamp/dim") != -1) {
    irCode = 0xFF20DF;
    lastState = "dim";
  }
(...)

Добавив новые операторы else if(), вы можете оснастить скетч и другими кнопками пульта. Но эти кнопки также нужно будет добавить на веб-страницу, и для этого в код нужно будет вписать примерно следующее (здесь показаны фрагменты для кнопок «ON», «OFF», «BRIGHTEN» и «DIM»):

s += "<p><a href=\"/lamp/on\"><button>ON</button></a><a href=\"/lamp/off\"><button>OFF</button></a></p>";
s += "<p><a href=\"/lamp/brighten\"><button>BRIGHTEN</button></a><a href=\"/lamp/dim\"><button>DIM</button></a></p>";

Внеся все необходимые изменения, загрузите скетч на ESP8266.

Узнаем IP-адрес ESP8266

Загрузив скетч на ESP8266, кликните в IDE Arduino на «Инструменты» > «Монитор порта» (Tools > Serial Monitor). Когда ESP8266 загрузится, в мониторе порта должен напечататься ее IP-адрес.

Esp8266 ir led serial monitor web server ip address print 1.PNG

В моем случае это «192.168.1.22» (как на скриншоте выше). В вашем случае IP-адрес, скорее всего, будет другим. Сохраните его, чтобы затем с его помощью заходить на веб-страницу для управления ИК-лампочкой.

Демонстрация

Откройте веб-браузер на любом устройстве, подключенном к той же сети, что и ESP8266, введите в его адресную строку IP-адрес ESP8266 и нажмите на  ↵ Enter .

Поместите светодиодный ИК-передатчик рядом с ИК-лампочкой. Понажимайте на кнопки на веб-странице для управления лампочкой – поменяйте цвет, яркость, включите/выключите ее и т.д.

Альтернативный вариант серверного кода

Если у вас предыдущий вариант работает нестабильно(частые разрывы, зависания и попросту браузер не может отобразить страницу), попробуйте более стабильный вариант:

#include <ESP8266WiFi.h>

#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>

// ИК-передатчик управляется через контакт GPIO4 (D2):
IRsend irsend(4);

// Вставьте здесь SSID и пароль для своей WiFi-сети:
const char* ssid = "ваш ssid";
const char* password = "ваш пароль";

// переменная для хранения HTTP-запроса:
String header;

// Переменная для хранения информации о том,
// какая кнопка была последней нажата на веб-странице:
String lastState;

// Создаем объект для сервера с портом «80»:
WiFiServer server(80);

void setup() {
  irsend.begin();

  Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
  delay(10);

  // Подготавливаем контакт GPIO4:
  pinMode(4, OUTPUT);
  digitalWrite(4, 0);

  // Подключаемся к WiFi-сети:
  Serial.print("Connecting to ");  //  "Подключаемся к "
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");  //  "Подключились к WiFi"

  // Запускаем сервер:
  server.begin();
  Serial.println("Server started");  //  "Сервер запущен"

  // Печатаем IP-адрес:
  Serial.println(WiFi.localIP());
}


void loop() {
  // начинаем прослушивать входящих клиентов:
  WiFiClient client = server.available();

  if (client) {                     // если подключился новый клиент,
    Serial.println("New Client.");  // печатаем сообщение
    // «Новый клиент.»
    // в мониторе порта;
    String currentLine = "";        // создаем строку для хранения
    // входящих данных от клиента;
    while (client.connected()) {    // цикл while() будет работать
      // все то время, пока клиент
      // будет подключен к серверу;
      if (client.available()) {     // если у клиента есть данные,
        // которые можно прочесть,
        char c = client.read();     // считываем байт, а затем
        Serial.write(c);            // печатаем его в мониторе порта
        header += c;
        if (c == '\n') {            // если этим байтом является
          // символ новой строки
          // если получили два символа новой строки подряд,
          // то это значит, что текущая строчка пуста;
          // это конец HTTP-запроса клиента,
          // а значит – пора отправлять ответ:
          if (currentLine.length() == 0) {
            // HTTP-заголовки всегда начинаются
            // с кода ответа (например, «HTTP/1.1 200 OK»)
            // и информации о типе контента
            // (чтобы клиент понимал, что получает);
            // в конце пишем пустую строчку:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            //  "Соединение: отключено"
            client.println();



            int irCode;

            if (header.indexOf("GET /lamp/on") >= 0) {
              irCode = 0xFFE01F;
              lastState = "on";
            }
            else if (header.indexOf("GET /lamp/off") != -1) {
              irCode = 0xFF609F;
              lastState = "off";
            }
            else if (header.indexOf("GET /lamp/brighten") != -1) {
              irCode = 0xFFA05F;
              lastState = "brighten";
            }
            else if (header.indexOf("GET /lamp/dim") != -1) {
              irCode = 0xFF20DF;
              lastState = "dim";
            }
            else if (header.indexOf("GET /lamp/red") != -1) {
              irCode = 0xFF906F;
              lastState = "red";
            }
            else if (header.indexOf("GET /lamp/green") != -1) {
              irCode = 0xFF10EF;
              lastState = "green";
            }
            else if (header.indexOf("GET /lamp/blue") != -1) {
              irCode = 0xFF50AF;
              lastState = "blue";
            }
            else if (header.indexOf("GET /lamp/yellow") != -1) {
              irCode = 0xFF8877;
              lastState = "yellow";
            }
            else if (header.indexOf("GET /lamp/cyan") != -1) {
              irCode = 0xFF708F;
              lastState = "cyan";
            }
            else if (header.indexOf("GET /lamp/purple") != -1) {
              irCode = 0xFF6897;
              lastState = "purple";
            }
            else {
              lastState = "none";
              Serial.println("Invalid request - IR signal not sent");
              //  "Неправильный запрос – ИК -сигнал не отправлен"
            }
            // Отправляем ИК-сигнал RGB-светодиодной лампочке:
            Serial.print("NEC: ");
            Serial.println(irCode, HEX);
            irsend.sendNEC(irCode, 32);
            client.flush();

            // показываем веб-страницу:
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");

            // веб-страница:
            client.println("</head><body><h1>RGB Lamp</h1>Last state:" + lastState);
            client.println("<p><a href=\"/lamp/on\"><button>ON</button></a><a href=\"/lamp/off\"><button>OFF</button></a></p>");
            client.println("<p><a href=\"/lamp/brighten\"><button>BRIGHTEN</button></a><a href=\"/lamp/dim\"><button>DIM</button></a></p>");
            client.println("<p><a href=\"/lamp/red\"><button>RED</button></a><a href=\"/lamp/green\"><button>GREEN</button></a><a href=\"/lamp/blue\"><button>BLUE</button></a></p>");
            client.println("<p><a href=\"/lamp/yellow\"><button>YELLOW</button></a><a href=\"/lamp/cyan\"><button>CYAN</button></a><a href=\"/lamp/purple\"><button>PURPLE</button></a></p>");

            client.println("</body></html>");

            // конец HTTP-ответа задается
            // с помощью дополнительной пустой строки:
            client.println();
            // выходим из цикла while():
            break;
          } else { // если получили символ новой строки,
            // очищаем текущую строку «currentLine»:
            currentLine = "";
          }
        } else if (c != '\r') {  // если получили любые данные,
          // кроме символа возврата каретки,
          currentLine += c;      // добавляем эти данные
          // в конец строки «currentLine»
        }
      }
    }
    // очищаем переменную «header»:
    header = "";
    // отключаем соединение:
    client.stop();
    Serial.println("Client disconnected.");
    //  "Клиент отключился."
    Serial.println("");
  }
}

Итого

Итак, с помощью этого проекта мы научились встраивать ИК-передатчик и ИК-приемник в ESP8266-проект. Это лишь шаблон, но возможности применения этих ИК-устройств – безграничны. К примеру, вместо ИК-лампочки можно управлять телевизором, кондиционером воздуха, термостатом, лентой RGB-светодиодов и любым другим устройством, управляемым с помощью пульта дистанционного управления.

Так же вы можете по аналогии с вот этим примером сделать управление лампой не через браузер,а с помощью Android-виджетов.

См.также

Внешние ссылки