Всегда грезил идеей умного дома. Хоп-хоп и все, что угодно можно включать-выключать не отрывая попы от дивана. Мечта ленивца! А ведь не зря программисты считаются самой ленивой профессией, а? Давайте приблизимся к заветной мечте и сделаем управление RGB лентой в нашей квартире с телефона Android.

Я программист и я ленив. Повторяться о том как прошивать ESP с помощью Arduino IDE не буду, читайте в статье ESP + MQTT как основа умного дома там все подробно описано. Теперь перейдем к сути.
Зажигать RGB ленту будет плата NodeMCU, на ней стоит ESP8266-12 плюс стабилизатор и удобные выводы - идеальный вариант для отладки. ESP8266-01 лучше не использовать для таких задач т.к. она не имеет выводов, которые при старте не "моргнут" единицей. Управление осуществляется по протоколу MQTT с мобильного телефона на android. MQTT - открытый протокол поверх http веб-сокет соединения. Мы зарегистрировали аккаунт на cloudmqtt и все наши устройства объединены этим сервером-брокером.
Подключаем нашу ESP8266 к USB-UART шнурку и из Arduino IDE грузим скетч в контроллер:
#include <ESP8266WiFi.h> #include <PubSubClient.h> #include <EEPROM.h> // Подключение к wifi const char *ssid = "xxxx"; // Имя wifi точки доступа const char *pass = "xxxx"; // Пароль wifi точки доступа // Подключение к mqtt брокеру const char *mqtt_server = "m13.cloudmqtt.com"; // Имя сервера MQTT const int mqtt_port = 14483; // Порт для подключения к серверу MQTT const char *mqtt_user = "xxxx"; // Логи к серверу MQTT const char *mqtt_pass = "xxxx"; // Пароль к серверу MQTT const char *topic_name = "light/rgb1"; // Название mqtt топика для передачи цвета // Конфигурация портов const int r = 2; // GIPO управления каналом цвета R const int g = 4; // GIPO управления каналом цвета R const int b = 5; // GIPO управления каналом цвета B // конфигурация eprom памяти const int eprom_r = 0; // Область в памяти для R const int eprom_g = 1; // Область в памяти для G const int eprom_b = 2; // Область в памяти для B #define BUFFER_SIZE 100 int tm = 300; float temp = 0; int c_r = 0; // Текущий цвет R int c_g = 0; // Текущий цвет G int c_b = 0; // Текущий цвет B WiFiClient wclient; PubSubClient client(wclient); void setup_wifi() { delay(10); // Подключаемся к WiFi Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void reconnect() { // Повторять пока нет MQTT соединения while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Попытка подключения к MQTT if (client.connect("arduinoClient", mqtt_user, mqtt_pass)) { Serial.println("connected"); // Подписываемся по топики (которые мы будем слушать от брокера) client.subscribe(topic_name); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } void setup() { Serial.begin(115200); delay(10); // Конфигурируем GPIO при старте pinMode(r, OUTPUT); pinMode(g, OUTPUT); pinMode(b, OUTPUT); // Читаем последний заданный цвет из энергонезависимой памяти EEPROM // Необходимо вызвать функцию EEPROM.begin(size) каждый раз перед началом чтения или записи. // Размер (указывается в байтах) соответствует размеру данных, которые вы намереваетесь использовать в EEPROM. // Размер данных должен быть в диапазоне от 4 до 4096 байт. EEPROM.begin(4); c_r = EEPROM.read(eprom_r); c_g = EEPROM.read(eprom_g); c_b = EEPROM.read(eprom_b); EEPROM.commit(); setColor(); setup_wifi(); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); } // Функция получения данных от MQTT сервера void callback(char* topic, byte* payload, unsigned int length) { // Присланное значение цвета String value = ""; for (int i=0; i<length; i++) { value = value + ((char)payload[i]); } Serial.print("The topic: "); Serial.println(topic); // Проверяем из нужного ли нам топика пришли данные if (String(topic) == String(topic_name)) { if (value != "") { Serial.print("HEX color: "); Serial.println(value); // Конвертируем пришедшие данные из hex #00AAFF в отдельные integer цвета long number = (long) strtol( &value[1], NULL, 16); c_r = number >> 16; c_g = number >> 8 & 0xFF; c_b = number & 0xFF; setColor(); // Запишем новые цвета в память, что бы при следующем включении цвет был такой же EEPROM.begin(4); EEPROM.write(eprom_r, c_r); EEPROM.write(eprom_g, c_g); EEPROM.write(eprom_b, c_b); EEPROM.commit(); } } } void setColor() { // Передадим значения на GPIO analogWrite(r, c_r); analogWrite(g, c_g); analogWrite(b, c_b); Serial.print("Set color RGB("); Serial.print(c_r); Serial.print(", "); Serial.print(c_g); Serial.print(", "); Serial.print(c_b); Serial.println(")"); }
Детально описывать код не буду т.к. в нем много комментариев. Принцип работы такой:
- Подключиться к роутеру;
- Подключиться к брокеру через интернет;
- Сообщить брокеру, что мы слушаем топики "light/rgb1". Они нам будут задавать цвет.
- Прочитать из памяти последний сохраненный цвет;
- Запустить цикл в котором мы задаем текущий цвет на GPIO через встроенный шим и ожидаем поступления топика. Если он будет, то конвертируем цвет в нужным нам формат, сохраняем его в память и меняем шим модуляцию на GPIO.
В коде используется библиотека "PubSubClient.h". Прикладываю ее ниже, что бы не возникло проблем с совместимостью версий.
Схема подключения
Для управления RGB лентой нам потребуется три MOSFET транзистора IRLB8721.

Они выдерживают хорошие токи и напряжение до 30 вольт, но не стоит их крепить к одному радиатору - корпус - средний вывод. Для стабильности работы подтягиваем управляющие GPIO и RST, и GPIO0 к земле через резисторы 10 кОм.

В схему включен стабилизатор напряжения на 3.2 вольта т.к. плата тут ESP8266-12. Она рассчитана на 3.2 Вольта.
Приложение MQTT для телефона
Телефон станет таким же клиентом брокера как и все наши устройства "Умного дома", Скачиваем один из множества бесплатных клиентов под ваш Android - IoT MQTT Dashboard. Вводим данные для доступа к брокеру.

Создаем новый элемент управления "Select color", указываем топик с именем "light/rgb1" - на него будет подписано наше устройство. Формат передаваемого цвета - HEX (#abcdef).

Готово! Теперь можно на телефоне задать цвет RGB подсветки и он будет послушно меняться на нашем устройстве. Аналогичным образом с небольшими изменениями в коде можно сделать датчик потопа с электро кранами, систему управления котлом и контролем температуры, охранную систему и многое другое. Лишь бы воображения хватило!
Для любителей анимации предлагаю версию с плавным изменением цвета:
#include <ESP8266WiFi.h> #include <PubSubClient.h> #include <EEPROM.h> // Подключение к wifi const char *ssid = "xxxx"; // Имя wifi точки доступа const char *pass = "xxxx"; // Пароль wifi точки доступа // Подключение к mqtt брокеру const char *mqtt_server = "m13.cloudmqtt.com"; // Имя сервера MQTT const int mqtt_port = 14483; // Порт для подключения к серверу MQTT const char *mqtt_user = "xxxx"; // Логи к серверу MQTT const char *mqtt_pass = "xxxx"; // Пароль к серверу MQTT const char *topic_name = "light/rgb1"; // Название mqtt топика для передачи цвета // Конфигурация портов const int r = 2; // GIPO управления каналом цвета R const int g = 4; // GIPO управления каналом цвета R const int b = 5; // GIPO управления каналом цвета B // Конфигурация eprom памяти const int eprom_r = 0; // Область в памяти для r const int eprom_g = 1; // Область в памяти для g const int eprom_b = 2; // Область в памяти для b // Анимация const int duration = 200; // продолжительность анимации unsigned long previous_millis = 0; unsigned long current_millis = 0; const long interval = 10; // Временное значения float ca_r = 0; float ca_g = 0; float ca_b = 0; // Коэффициенты для анимации float ck_r = 0; float ck_g = 0; float ck_b = 0; int animate_duration = duration; // Триггер запуска анимации #define BUFFER_SIZE 100 int tm = 300; float temp = 0; int c_r = 0; // Текущий цвет R int c_g = 0; // Текущий цвет G int c_b = 0; // Текущий цвет B WiFiClient wclient; PubSubClient client(wclient); void setup_wifi() { // Подключаемся к WiFi Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); animate(); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void reconnect() { // Повторять пока нет MQTT соединения while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Попытка подключения к MQTT if (client.connect("arduinoClient", mqtt_user, mqtt_pass)) { Serial.println("connected"); // Подписываемся по топики (которые мы будем слушать от брокера) client.subscribe(topic_name); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } void setup() { Serial.begin(115200); delay(10); // Конфигурируем GPIO при старте pinMode(r, OUTPUT); pinMode(g, OUTPUT); pinMode(b, OUTPUT); // Читаем последний заданный цвет из энергонезависимой памяти EEPROM // Необходимо вызвать функцию EEPROM.begin(size) каждый раз перед началом чтения или записи. // Размер (указывается в байтах) соответствует размеру данных, которые вы намереваетесь использовать в EEPROM. // Размер данных должен быть в диапазоне от 4 до 4096 байт. EEPROM.begin(4); c_r = EEPROM.read(eprom_r); c_g = EEPROM.read(eprom_g); c_b = EEPROM.read(eprom_b); EEPROM.commit(); setColor(); previous_millis = millis(); current_millis = millis(); setup_wifi(); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); } void loop() { animate(); if (!client.connected()) { reconnect(); } client.loop(); } // Функция получения данных от MQTT сервера void callback(char* topic, byte* payload, unsigned int length) { // Присланное значение цвета String value = ""; for (int i=0; i<length; i++) { value = value + ((char)payload[i]); } Serial.print("The topic: "); Serial.println(topic); // Проверяем из нужного ли нам топика пришли данные if (String(topic) == String(topic_name)) { if (value != "") { Serial.print("HEX color: "); Serial.println(value); // Конвертируем пришедшие данные из hex #00AAFF в отдельные integer цвета long number = (long) strtol( &value[1], NULL, 16); c_r = number >> 16; c_g = number >> 8 & 0xFF; c_b = number & 0xFF; setColor(); // Запишем новые цвета в память, что бы при следующем включении цвет был такой же EEPROM.begin(4); EEPROM.write(eprom_r, c_r); EEPROM.write(eprom_g, c_g); EEPROM.write(eprom_b, c_b); EEPROM.commit(); } } } void animate() { current_millis = millis(); if (animate_duration > 0 && current_millis - previous_millis >= interval) { previous_millis = current_millis; ca_r = ca_r + ck_r; ca_g = ca_g + ck_g; ca_b = ca_b + ck_b; if (ca_r < 0) ca_r = 0; if (ca_g < 0) ca_g = 0; if (ca_b < 0) ca_b = 0; animate_duration = animate_duration - 1; // Передадим значения на GPIO analogWrite(r, ca_r); analogWrite(g, ca_g); analogWrite(b, ca_b); Serial.print("Animate RGB("); Serial.print(ca_r); Serial.print(", "); Serial.print(ca_g); Serial.print(", "); Serial.print(ca_b); Serial.println(")"); } } void setColor() { animate_duration = duration; // Триггер запуска анимации ck_r = ((c_r - ca_r) / duration); ck_g = ((c_g - ca_g) / duration); ck_b = ((c_b - ca_b) / duration); Serial.print("Set color RGB("); Serial.print(c_r); Serial.print(", "); Serial.print(c_g); Serial.print(", "); Serial.print(c_b); Serial.println(")"); }
Отладку удобнее всего проводить на NodeMCU. При подключении обратите внимание на распиновку вашей платы. Не все контакты поддерживают цифровой выход. Используйте каналы с индексом D.


Печатная плата, если есть желание сорбрать в железе.

ca_r = ca_r + ck_r;
ca_g = ca_r + ck_r;
ca_b = ca_r + ck_r;
Спасибо
Может быть версия библиотек разная. Попробуйте скачать библиотеку publicent отсюда http://diytimes.ru/post/23 (файл в конце статьи). В обновленной версии изменился синтаксис.
Стоит проверить настройки ArduinoIDE, может быть выбран не тот тип процессора или еще что-то. По настройкам смотрите статью http://diytimes.ru/post/19 в ней все подробно расписано.