В связи с чрезвычайной распространенностью и популярностью разного рода «умных» жопожужжателей, решил закупить себе на пробу Wi-Fi розетку. Само устройство выполнено довольно неплохо (если не считать одного монтажного косяка, который будет показан ниже), корпус из качественного пластика. Внутри корпуса на печатной плате расположен честный AC/DC преобразователь (а не конденсаторное барахло), силовое реле, кнопка и два светодиода для ее подсветки. Разрыв нагрузки однополюсный (т.е. разрывается только один провод), что нехорошо, т.к. мы можем легко получить ситуацию, когда на нагрузку в выключенном состоянии подведена фаза.
С обратной стороны расположена сама микросхема ESP8266, линейный стабилизатор и внешняя flash память, подключенная по SPI. Не обошлось и без монтажных косяков — наши дорогие китайские друзья так и не научились откусывать лишнюю длину выводов у выводных элементов, поэтому один из электролитических конденсаторов оказался практически замкнут загнутыми выводами с обратной стороны платы. Также был замечен криво установленный керамический SMD конденсатор. Такое часто бывает после печки, но то, что он остался в таком положении означает, что либо контроля после печки совсем нет (даже визуального), либо это считается допустимым.
В штатном варианте работает это все следующим образом: при включении в розетку устройство ищет у себя в памяти настройки для подключения к wifi сети (SSID и ключ), если находит — подключается, если нет — создает свою точку доступа (скорее всего скрытую, т.к. обнаружить ее не штатными средствами винды не удалось) и ждет когда его сконфигурируют. Конфигурация происходит при помощи смартфона с установленным приложением производителя. После конфигурации и нехитрой процедуры регистрации устройства начинается собственно работа. Управление устройством происходит по следующей схеме: при нажатии кнопки «включить» в приложении отправляется команда на сервер, далее сервер отправляет команду устройству и наша розетка замыкает контакты реле, после чего отправляет обратно свое состояние.
Ущербность данной схемы видна невооруженным глазом — она не работает без интернета. Нет, включить розетку вручную вы сможете — для этого на ней есть кнопка. Но вот дистанционное управление и какие-то сценарии автоматизации работать не будут, несмотря на то, что вы с розеткой находитесь в одной локальной сети. Еще один существенный минус подобной схемы — она не позволяет встроить эту розетку в существующую систему автоматизации (отличную от того убогого суррогата, который предлагает производитель).
Вывод напрашивается сам собой — нужно написать свою прошивку с блэкджеком и шлюхами! Благо, материалов на эту тему в интернете — вагон и маленькая тележка. В качестве отправной точки был взят проект вот отсюда.
Исправляем адрес MQTT сервера (брокера), а также названия топиков (тем), в которые мы будем отдавать данные о состоянии (pubTopic) и на которые мы будем подписаны (controlTopic), с тем чтобы получать оттуда команды управления:
#include <DNSServer.h>;
#include <ESP8266WebServer.h>;
#include <WiFiManager.h>;
//https://github.com/tzapu/WiFiManager
//
WiFiClient espClient;
IPAddress MQTTserver(192, 168, 0, 101); // mosquitto address
PubSubClient client(espClient, MQTTserver);
char* pubTopic = "/ESP8266/STATUS/GPIO12";
char* controlTopic = "/ESP8266/CONTROL/#";
Далее переписываем Callback — это функция, которая вызывается при получении данных. Нас (пока что) интересует только команда управления выводом GPIO12, который управляет реле. Соответственно, обрабатывать будем только его — при получении команды «ON» или «OFF» переводим GPIO12 в соответствующее состояние. Никаких данных мы в топик статуса здесь не посылаем — это удобнее делать в другом месте кода.
void callback(const MQTT::Publish& sub) {
if (sub.topic() == "/ESP8266/CONTROL/GPIO12") {
if (sub.payload_string() == "ON") {
//Serial.println("Control request to GPIO12 [ON]");
digitalWrite(12, HIGH);
}
if (sub.payload_string() == "OFF") {
//Serial.println("Control request to GPIO12 [OFF]");
digitalWrite(12, LOW);
}
}
}
Ну и основной цикл — здесь наша задача определять переключения состояния GPIO12 и отправлять сообщения с фактическим состоянием пина в соответствующий топик. Такой подход позволяет нам отслеживать как включения по команде от MQTT сервера так и ручные включения/выключения, произведенные кнопкой на самом устройстве. Кстати, о кнопке — ее мы тоже должны обрабатывать, опрос кнопки осуществляется каждые 200 мс (на самом деле это несколько многовато, для комфортного использования лучше ставить 150 мс) и при изменении состояния мы переключаем GPIO12.
int ms2;
if ( ( micros() / 1000 ) - ms > 1000 || ms > micros() / 1000 ) {
if ( heap > ESP.getFreeHeap() ) {
Serial.println("Detect mem leak!");
heap = ESP.getFreeHeap();
}
ms = micros() / 1000;
seconds++;
if(seconds > 3599) seconds = 0;
if ( WiFi.status() != WL_CONNECTED ) {
digitalWrite(13, 1);
}
else {
digitalWrite(13, 0);
}
}
ms2 = micros() / 1000;
if ( ms2 % 200 == 0 ) {
pinOldState = pinState; // must be global or static
pinState = digitalRead(12); // must be global or static
if(pinState != pinOldState) {
//Публикуем GPIO в топике
if (client.publish("/ESP8266/STATUS/GPIO12", String(pinState))) {
Serial.println(" Publish status [ok]. PIN [" + String(pinState) + "] OLD [" + String(pinOldState) + "]");
}
else Serial.println(" Publish status [failed]");
}
ButtonState = digitalRead(0);
//Если нажали кнопку (нашли передний фронт)
if(ButtonState == 0 && ButtonOldState == 1) {
//Переключаем реле
if(digitalRead(12) == 0) digitalWrite(12, 1);
else digitalWrite(12, 0);
}
ButtonOldState = ButtonState;
delay(10);
}
if ( !client.loop() ) { restart();}
yield();
}
Допиленную прошивку можно скачать вот тут (~1 Мб). Кроме самой прошивки в архиве также лежат нужные библиотеки и даташит на ESP8266. В результате, теперь устройство работает по протоколу MQTT в нашей сети и не лезет ни к каким внешним серверам.
Зашиваем получившееся ПО в устройство, предварительно припаяв 4 штырька на плату.
Если в энергонезависимой памяти остались данные для авторизации (SSID и ключ), то устройство автоматически подключится к указанной сети, если по каким-то причинам это не получится, то розетка создаст свою точку доступа с именем, которое мы задали в коде. И по адресу 192.168.4.1 выводит страницу где можно сконфигурировать наш ESP8266. В этой версии точка доступа создается незащищенная! Это косяк (мой) и косяк крайне опасный! В следующий раз расскажу и покажу почему.
Теперь устанавливаем MQTT сервер
sudo apt-get update
sudo apt-get install mosquitto
sudo apt-get install mosquitto-clients