====== ESP12: Управление RGB-светодиодом (Веб-сервер) ======
===== Описание =====
В данном проекте производится управление RGB-светодиодом. Модуль RGB-светодиода подключен к контролеру. Мы сможем с сайта управлять цветом его свечения, либо отключать светодиод полностью. Таким образом WiFi модуль будет являться веб-сервером для управления модулем.

В дальнейшем, Вы можете подключать к контроллеру любые устройства, которыми требуется управлять и влиять на них с сайта.

Изначально WiFi модуль настроен на работу с помощью AT-команд. Но вручную писать AT-команды очень неудобно, по нескольким причинам:
  * Очень велика вероятность ошибиться и ввести неверную AT-команду
  * Разбор ответа от контроллера будет довольно ёмким
  * Скетч будет трудно-читаемым из-за огромного количества кода
  * Сложно выявить ошибки

Поэтому, чтобы общение с модулем было наиболее эффективным, простым и быстрым, мы воспользуемся библиотекой WiFiEsp
  * [[http://files.smartelements.ru/know_libs/WiFiEsp.zip|Скачать библиотеку WiFiEsp]]

Скачайте и установите данную библиотеку в среду разработки Arduino IDE.

<WRAP center round tip 80%>
Перед началом работы настройте модуль на скорость обмена в 9600 бод. О том как это сделать, рассказано [[main:comm:esp12#Установка скорости обмена данными|на этой странице]].
</WRAP>

==== Шаг 1. Вам потребуется ====
Возьмите необходимые для проекта элементы, представленные в таблице ниже.

^ Что нужно                                                                                                               ^  Кол-во, шт  ^
| [[https://smartelements.ru/collection/kontrollery/product/kontroller-smart-uno|Контроллер Smart Uno]]                   |  1           |
| [[https://smartelements.ru/collection/svyaz/product/wifi-modul-esp-12|Wi-Fi модуль ESP-12]]                      |  1           |
| [[https://smartelements.ru/collection/shildy/product/sensor-shield|Sensor Shield]]                      |  1           |
| [[http://smartelements.ru/collection/provoda-i-kabeli/product/nabor-provodov-20-sht-20sm-mama-mama|Провода мама-мама]]  |  8           |
| [[https://smartelements.ru/collection/moduli/product/modul-rgb-svetodioda-3|Модуль RGB-светодиода]]             |  1           |



==== Шаг 2. Подключите Wifi модуль к шилду ====
Выполните подключение модуля в соответствии со схемой ниже.


==== Шаг 3. Загрузите скетч на контроллер ====
Загрузите скетч, представленный ниже, на контроллер. Предварительно измените значения переменных:
  * char ssid[] - здесь должно быть Ваше имя WiFi сети
  * char pass[] - здесь должен быть пароль от Вашей WiFi сети
  * rgb_pins[] - вывод контроллера, к которым подключены цвета светодиода: красный, зелёный и синий

<file Arduino>
#include "WiFiEsp.h" //подключение библиотеки WiFiEsp

// Эмуляция Serial соединения
#ifndef HAVE_HWSERIAL1
#include "SoftwareSerial.h"
SoftwareSerial wifiSerial(6, 7); // RX, TX
#endif

char ssid[] = "SmartElements"; //имя сети wifi
char pass[] = "1234567890"; //пароль от сети wifi
int status = WL_IDLE_STATUS; //статус сети wifi
const int rgb_pins[] = {3, 4, 5}; //выводы RGB светодиода
boolean rgb_states[] = {false, false, false}; //состояние выводов

WiFiEspServer server(80); //пересенная для создания объекта сервера

//использование кольцевого буфера для увеличения скорости
RingBuffer buf(8);

void setup()
{
  for (int i = 0; i < 3; i++) { //установка режимов выводов 
    pinMode(rgb_pins[i], OUTPUT);  
    digitalWrite(rgb_pins[i], rgb_states[i]);
  }

  //инициализация Serial-соединения для отладки
  Serial.begin(9600);
  //инициализация Serial-соединения для ESP-модуля
  wifiSerial.begin(9600);
  //инициализация ESP модуля
  WiFi.init(&wifiSerial);

  //проверка существования модуля
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi module not present");
    //если модуля нет, то не продолжать работу далее
    while (true);
  }

  //попытка подключения к wifi сети
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: "); //вывод надписи о попытке подключения
    Serial.println(ssid); //вывод названия сети
    //подключение к сети, используя имя и пароль
    status = WiFi.begin(ssid, pass);
  }

  Serial.println("You're connected to the network");
  printWifiStatus(); //вывод статуса подключения

  //инициализация веб-сервера на порту № 80
  server.begin();
}


void loop()
{
  WiFiEspClient client = server.available();  //ожидание входящих клиентов

  if (client) {                               //если получен клиент,
    Serial.println("New client");             //вывести сообщение в Serial-порт
    buf.init();                               //инициализация буфера
    while (client.connected()) {              //цикл, пока клиент подключен
      if (client.available()) {               //если от клиента есть байты для чтения,
        char c = client.read();               //прочитать байт, а затем
        buf.push(c);                          //добавить его в буфер

        //если в строке получены два символа завершения строки
        //значит - это конец HTTP запроса, так что нужно отправить ответ
        if (buf.endsWith("\r\n\r\n")) {
          sendHttpResponse(client);
          break;
        }

        //проверить если запрос был одним из: "GET /R", "GET /G" или "GET /B":
        if (buf.endsWith("GET /R")) { //запрос изменения состояния красного цвета
          Serial.println("Change red state"); //вывод отладочной информации
          changeStateRGB(0); //изменение статуса цвета
        } else if (buf.endsWith("GET /G")) { //запрос изменения состояния зелёного цвета
          Serial.println("Change green state"); //вывод отладочной информации
          changeStateRGB(1); //изменение статуса цвета
        } else if (buf.endsWith("GET /B")) { //запрос изменения состояния синего цвета
          Serial.println("Change blue state"); //вывод отладочной информации
          changeStateRGB(2); //изменение статуса цвета
        }
      }
    }

    //закрытие соединения
    client.stop();
    Serial.println("Client disconnected");
  }
}

/* Отправка и формирование страницы - ответа
 *  на сервер 
 */
void sendHttpResponse(WiFiEspClient& client)
{
  //Обычно, HTTP заголовки начинаются с кода ответа (напр. HTTP/1.1 200 OK)
  // и типа страницы чтобы клиент знал, что приходит, затем пустая строка
  client.print(
    "HTTP/1.1 200 OK\r\n"
    "Content-Type: text/html\r\n"
    "Connection: close\r\n"  //соединение будет закрыто после завершения ответа
    "\r\n");
  client.print("<!DOCTYPE HTML>\r\n");
  client.print("<html>\r\n");

  //заголовок страницы
  client.print("<head>\r\n");
  client.print("<meta charset=\"utf-8\">\r\n");
  client.print("<title>Данные модуля</title>\r\n");
  client.print("</head>\r\n");

  //тело страницы
  client.print("<h1>Привет от SmartElements!</h1>\r\n");
  client.print("<p>Данные получены с помощью ");
  client.print("<a href=\"https://smartelements.ru/collection/svyaz/product/wifi-modul-esp-12\">");
  client.print("крутого Wi-Fi модуля</a></p>\r\n");
  client.println();

  //затем определение текущего цвета RGB светодиода и вывод его
  printCurrentColor(client);
  client.println("<br>");
  client.println("<br>");

  //вывод состояния цвета в строку со ссылкой для изменения
  printLinkState(client, 0);
  printLinkState(client, 1);
  printLinkState(client, 2); 

  //HTTP ответы заканчиваются с пустой строки
  client.println();
}

/* Вывод состояния сети и адреса для подключения к моджулю  
 */
void printWifiStatus()
{
  //вывод названия сети
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  //вывод полученного модулем IP-адреса
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  //вывод информации о адресе в браузере
  Serial.println();
  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);
  Serial.println();
}

/* Изменение статуса и зажигание светодиода
 */
void changeStateRGB(const int& i)
{
  rgb_states[i] = !rgb_states[i]; //Изменение состояния светодиода
  digitalWrite(rgb_pins[i], rgb_states[i]); //Зажечь/погасить светодиод (цвет)
}

/* Формирование строки-ссылки для управления текущим цветом.
 *  В зависимости от номера вывода опредяется цвет и 
 *  выводится строка запроса.
 *  В зависимости от текущего состояния цвета (включен он или нет),
 *  меняется надпись ссылки. Если цвет выключен, то надпись будет "Включить цвет"
 *  и наоборот.
 */
void printLinkState(WiFiEspClient& client, const int& i)
{
  client.print("<a href=\"");
  if (i == 0) {
    client.print("/R\" style=\"color:red");
  } else if (i == 1) {
    client.print("/G\" style=\"color:green");
  } else {
    client.print("/B\" style=\"color:blue");
  }
  client.print("\">");
  if (rgb_states[i]) {
    client.print("Выключить");
  } else {
    client.print("Включить");
  }
  client.println(" цвет</a><br>");
}

/* Определение текущего цвета и светодиода и вывод надписи об этом
 *  соответствующим цветом.
 */
void printCurrentColor(WiFiEspClient& client) 
{
  client.print("<h2 style=\"background-color:");
  if (rgb_states[0] == true && rgb_states[1] == false && rgb_states[2] == false) {
    client.print("red");
  } else if (rgb_states[0] == false && rgb_states[1] == true && rgb_states[2] == false) {
    client.print("green");
  } else if (rgb_states[0] == false && rgb_states[1] == false && rgb_states[2] == true) {
    client.print("blue");
  } else if (rgb_states[0] == true && rgb_states[1] == true && rgb_states[2] == false) {
    client.print("yellow");
  } else if (rgb_states[0] == true && rgb_states[1] == false && rgb_states[2] == true) {
    client.print("purple");
  } else if (rgb_states[0] == false && rgb_states[1] == true && rgb_states[2] == true) {
    client.print("aqua");
  } else if (rgb_states[0] == true && rgb_states[1] == true && rgb_states[2] == true) {
    client.print("silver");
  } else if (rgb_states[0] == false && rgb_states[1] == false && rgb_states[2] == false) {
    client.print("black");
  } 
  client.print("\">Текущий цвет RGB светодиода</h2>");
}
</file>

==== Шаг 4. Где запущен сервер ====
Далее, откройте монитор Serial-порта в Arduino IDE, и посмотрите параметры подключения. Вы увидите информацию вида:

<file>
[WiFiEsp] Initializing ESP module
[WiFiEsp] Initilization successful - 1.5.4
Attempting to connect to WPA SSID: SmartElements
[WiFiEsp] Connected to SmartElements
You're connected to the network
SSID: SmartElements
IP Address: 192.168.1.18

To see this page in action, open a browser to http://192.168.1.18

[WiFiEsp] Server started on port 80
</file>

Где:
  * SSID - имя сети, к которой подключился модуль
  * IP Address - IP адрес модуля

Скопируйте адрес сайта, в нашем случае он выглядит так:
<file> http://192.168.1.18 </file>

<WRAP center round info 80%>
Данный IP-адрес действует только внутри Вашей домашней WiFi сети.
</WRAP>


==== Шаг 5. Результат ====

Откройте браузер, который Вы используете для выхода в интернет. И в строке ввода адреса сайта, вставьте адрес, скопированный из шага 4. После этого загрузится страница вида:

{{ :главная:связь:wifi_rgb_1.jpg?nolink |}}

Где:
  - Адрес Вашего сайта
  - Текущий цвет RGB-светодиода (черный - означает что светодиод выключен)
  - Ссылка для включения красного цвета свечения
  - Ссылка для включения зелёного цвета свечения
  - Ссылка для включения синего цвета свечения

==== Шаг 6. Красный цвет ====

На сайте, кликните по ссылке красного цвета "Включить цвет".

//Технически, после выполнения клика по ссылке на наш веб сервер будет отправлен запрос "GET /R" (получить красный). Этот запрос обрабатывается контроллером. Обработку запроса мы описали в скетче, где указали что нам нужно инвертировать (сделать обратным) значение цвета. То есть, если сейчас был выключен красный, его включить. И наоборот.//

{{ :главная:связь:wifi_rgb_2.jpg?nolink |}}

Где:
  - Адрес и выполненный запрос
  - Значение текущего цвета светодиода
  - Обратите внимание, что ссылка красного цвета изменилась с команды "Включить" на команду "Выключить"

После выполнения этого действия, Вы должны увидеть что светодиод загорелся красным цветом.

<WRAP center round info 80%>
Всегда дожидайтесь пока страница сайта загрузится окончательно. Потому как её обновление занимает некоторое время. В эти моменты ссылки могут не отрабатывать.
</WRAP>

==== Шаг 7. Фиолетовый цвет ====

Далее, после того как страница окончательно обновилась, давайте сделаем клик по ссылке синего цвета "Включить цвет"

{{ :главная:связь:wifi_rgb_3.jpg?nolink |}}

Обратите внимание:
  - Снова поменялся запрос на сервер
  - Текущий цвет изменился на фиолетовый (сиреневый)
  - Ссылка синего цвета изменилась с команды "Включить" на команду "Выключить"

Светодиод стал светиться фиолетовым (сиреневым) цветом

==== Шаг 8. Белый цвет ====

Ну и, наконец, добавим ещё не включенный зелёный цвет, кликнув по ссылке зелёного цвета.

{{ :главная:связь:wifi_rgb_4.jpg?nolink |}}

И увидим следующее:
  - Изменение запроса
  - Изменение цвета текущего свечения (отображаем серым, так как у нас белый фон сайта и белый цвет на нём будет не виден)
  - Ссылка зелёного цвета изменилась с команды "Включить" на команду "Выключить"

Теперь светодиод светит белым цветом - сочетание свечения красного, зелёного и синего.

==== Шаг 9. Выключение свечения ====

Далее, будем отключать включенные цвета. Нажмём на ссылку красного цвета, тем самым отключив свечение красного цвета. После выключения красного, светодиод будет светиться голубым цветом (сочетание синего и зелёного).

{{ :главная:связь:wifi_rgb_5.jpg?nolink |}}

Ссылка красного цвета поменялась с "Выключить" на "Включить".

//Технически, кликая на ссылку одного цвета, мы посылаем один и тот же запрос "GET /R" (получить красный). А далее в скетче, в зависимости от текущего состояния свечения цвета, мы его меняем на противоположное. Это гораздо проще, чем создавать отдельные кнопки (ссылки) для включения и отключения цвета. //

==== Шаг 10. Управляйте цветом ====

Посмотрите, какой цвет свечения получится в сочетании красного и зелёного цветов.

Теперь Вы можете управлять цветом свечения, включая и отключая различные цвета. Не забывайте, что серверу требуется время на получение запроса и обновление веб страницы.

Таким же образом, Вы можете подключить любые устройства и управлять их работой через интернет.
