Решил сделать себе сенсорный выключатель на кухню.
Обычно такие штуки делаются на Ардуине, однако с учетом падения цен на ESP8266, и более жирными ресурсами, захотелось сделать это на ESP8266, и заодно убить этим выстрелом второго зайца - сделать выключатель WiFi управляемым.
Вот список того что я запланировал получить в итоге:
1. При включении (точнее первом подаче электричества вообще) выключатель должен подключаться к WiFi, а потом слать на домашний сервер свой IP и набор поддерживаемых фич. Хочу так сделать, чтобы унифицировать управление устройствами из одного центрального узла. Грубо говоря, выключатель сам добавляет себя в базу данных как выключатель, а потом центральная программа видит новое устройство "выключатель" и начинает с ним работать исходя из параметров которые этот выключатель прислал;
2. Выключатель должен уметь выключать и включать свет в автономном режиме, без наличия или отсутствия WiFi;
3. Выключатель так же должен уметь включать и выключать свет по сети, при чем без использования хитрожопых протоколов;
4. Соответственно выключатель должен уметь запоминать свое состояние и возвращать его по запросу из сети;
5. У выключателя должна быть подсветка в режиме ночника, на одном светодиоде;
6. Эта подсветка так же должна уметь включаться и выключаться по сети;
7. Разумеется это все должно иметь возможность быть запихнутым в бокс.
Самое проблемное и казалось сперва, и оказалось в итоге - это питание.
Все дело в том, что ESP8266 - питается от 3.3в, и ладно бы этим все ограничилось, но нет, кроме ESP-шки там должно принимать участие как минимум два модуля, приемный и исполнительный, которые непонятно как себя поведут с 3.3в.
В качестве сенсоров у меня был выбор, выбрать подешевле и поменьше, или подороже и побольше. Оба были у меня куплены для других проектов.
Разумеется выбрал я красненький, из-за двух факторов: меньшего размера, и отсутствия на стороне сенсора шероховатостей в виде деталей и разъемов. Честно говоря, производитель правого сенсора головой видимо не думал. Ну да ладно.
Исполнительным механизмом я вначале хотел выбрать классическое реле, но потом отказался из-за щелчка, и из-за надежды на то, что не придется ставить дополнительные транзисторы, чтобы подружить ESP-шные 3.3 вольта с релюшными пятью. В итоге выбрал твердотельный Omron с питанием в 5в. Как оказалось дальше, не зря.
В качестве элемента питания использовал обычную пятивольтовую зарядку от мобильника, куда навесом напаял LM1117 (стабилизатор на 3.3в).
Родные кишки от выключателя за 30 гривен, я вытащил (видно на первой фотке), вместо этого приклеил сенсор силиконом на внутреннюю сторону того элемента, на который обычно нажимают.
В общем вся готовая конструкция на кухне выглядела вот так...
На удивление, и сенсор, и твердотельное реле - отлично работают от 3.3В.
Что касается программной части, то для нее я использовал Arduino IDE (в Арче лежит в стандартных репах) + плагин к ней. Язык отдаленно напоминает C++, а то и PHP. Специально перепрошивать ESP8266 ни под что не нужно, все шьется на лету, с закороченным GPIO0 (об этом лучше почитать в интернете, цель статьи - не научить вас шить ESP8266).
За прошивку я взял скетч веб-сервера из примеров, добавив туда необходимые фичи.
С гордостью представляю его вам..
#include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <ESP8266mDNS.h> #include <ESP8266HTTPClient.h> const char* ssid = "u39_2.4G"; const char* password = ""; String ID="0001"; int status=0; ESP8266WebServer server(80); int n=0; void handleNotFound(){ String message = "File Not Found\n\n"; message += "URI: "; message += server.uri(); message += "\nMethod: "; message += (server.method() == HTTP_GET)?"GET":"POST"; message += "\nArguments: "; message += server.args(); message += "\n"; for (uint8_t i=0; i<server.args(); i++){ message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; } server.send(404, "text/plain", message); } void setup(void){ Serial.begin(115200); Serial.println("Starting Jarvis module: switcher..."); // На пин 13 у нас подключен сенсор pinMode(13, INPUT); // На пин 13 у нас подключено реле pinMode(5, OUTPUT); // На пин 4 у нас подключена подсветка pinMode(4, OUTPUT); digitalWrite(5, 0); digitalWrite(4, 1); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.print("Connecting to WiFi: "); // Wait for connection while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); n++; // Если за 60 сек (0.5 * 120) нет wifi-подключения, ребутаем девайс. Потенциальный баг, в случае отсутствия WiFi девайс упадет в бесконечный ребут, пофиксю в след ревизии if (n==120) {Serial.println("[ FAIL ]");Serial.println("1 minute is gone, no connection, restarting...");ESP.reset();} } Serial.println(" [OK] "); HTTPClient http; String IP=WiFi.localIP().toString().c_str(); // Посылаем на сервер умного дома GET запрос с нашим IP и списком поддерживаемых фич // lightswitch означает что к этой IP можно будет обращаться http://192.168.1.X/lightswitch=1 или http://192.168.1.X/lightswitch=0 и это будет включать и выключать свет // backlight по сути означает то же самое, но для низкожрущей подсветки // remote означает то, что этим устройством можно управлять снаружи // alert - означает то, что это устройство поддерживает индикацию. Это можно использовать например для бесшумной индикации звонка в дверной звонок http.begin("http://192.168.1.2/init.php?ip="+IP+"&device=switcher&features=lightswitch,backlight,remote,alert&id="+ID); int httpCode = http.GET(); http.end(); Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); } // http://192.168.1.X/status вернет нам 0 или 1 в зависимости от того включен свет или нет server.on("/status", [](){ String st=String(status); server.send(200, "text/plain", st); }); // http://192.168.1.X/backlight=1 включит подсветку server.on("/backlight=1", [](){ digitalWrite(4, 1); server.send(200, "text/plain", "OK"); }); // http://192.168.1.X/backlight=1 выключит подсветку server.on("/backlight=0", [](){ digitalWrite(4, 0); server.send(200, "text/plain", "OK"); }); // http://192.168.1.X/lightswitch=1 включит свет server.on("/lightswitch=1", [](){ digitalWrite(5, 1); status=1; server.send(200, "text/plain", "OK"); }); // http://192.168.1.X/lightswitch=0 выключит свет server.on("/lightswitch=0", [](){ digitalWrite(5, 0); status=0; server.send(200, "text/plain", "OK"); }); server.onNotFound(handleNotFound); server.begin(); Serial.println("HTTP server started"); } void loop(void){ server.handleClient(); int value=digitalRead(13); // Если нажат сенсор, и свет выключен, то... if ((value==1)and(status==0)) { // Включаем свет digitalWrite(5, 1); // Меняем статус status=1; // Сбрасываем значение переменной сенсора. Если этого не сделать, то проверка ниже снова выключит свет value=0; delay(1000); } // Если нажат сенсор, и свет включен, то... if ((value==1)and(status==1)) { // Выключаем свет digitalWrite(5, 0); // Меняем статус status=0; // Сбрасываем значение переменной сенсора. Если этого не сделать, то проверка выше снова включит свет value=0; delay(1000); } delay(100); }Единственное что, светодиод подсветки я пока не припаял, выключатель работает в пилотном режиме, в связи с чем предоставляю вам видео, благодарю за чтение статьи, и готов ответить на вопросы, если такие появятся.