ESP8266:Примеры/Веб-переключатель, управляемый при помощи EasyIoT Cloud

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

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


Pixel Art Mini Meow Animated.gif Черновик


Веб-переключатель, управляемый при помощи EasyIoT Cloud[1]

Эта статья рассказывает, как собрать веб-переключатель, управляемый через WiFi при помощи веб-сервиса EasyIoT Cloud. Для этого проекта нам понадобятся модуль с чипом ESP8266, твердотельное реле и программа IDE Arduino. Данные для точки доступа, к которой будет подключаться веб-переключатель, можно задать через браузерный интерфейс.

Управлять веб-переключателем можно, во-первых, дистанционно – через браузерный веб-интерфейс (с компьютера, планшета или мобильного телефона) или через официальное приложение.

Веб-переключатель подключен к интернету через WiFi. Кроме того, переключателем можно управлять локально, при помощи кнопки.

Данное руководство написано на примере последней версии MQTT-брокера EasyIoT Cloud. Эта версия имеет два улучшения по сравнению с предыдущей. Во-первых, для обновления состояния в интерфейсе используется информация от переключателя. То есть, если соединение с переключателем разорвано, состояние в пользовательском интерфейсе обновляться не будет. Во-вторых, данные для точки доступа, к которой будет подключаться веб-переключатель, теперь можно задать через браузер, открытый на смартфоне или компьютере.

Этот веб-переключатель работает по принципу «подключай и работай» – с настройками EasyIoT Cloud возиться не нужно, т.к. они выставляются автоматически.

ВНИМАНИЕ! Работая с этим проектом, вы будете иметь дело с СЕТЕВЫМ напряжением. ЭТО ОЧЕНЬ ОПАСНО!

Если у вас недостаточно опыта или квалификации для работы с СЕТЕВЫМ напряжением, настоятельно рекомендую не работать с этим проектом!

Не беритесь за этот проект, не имея ДОСТАТОЧНО ЗНАНИЙ о том, как работают цепи, использующие СЕТЕВОЕ напряжение!

Не беритесь за этот проект, если на линии с СЕТЕВЫМ напряжением не стоит правильный ПРЕДОХРАНИТЕЛЬ!

Максимальная сила тока для твердотельного реле в этом проекте – 2 ампера. Подходит только для комнатного освещения.

Введение

[Видео]

Цепь веб-переключателя в законченном виде

Необходимые компоненты

  • Один модуль с ESP8266
  • Одно твердотельное реле (2 ампера, 240 вольт)
  • Один понижающий преобразователь (на входе переменный ток, на выходе – постоянный; на выходе 3.3 вольта; 600 мА)
  • Одна кнопка
  • Один транзистор типа NPN (TO-92, 2N2222)
  • Один электролитический конденсатор на 1000 мкФ
  • Один резистор на 1 кОм
  • Один резистор на 47 кОм

Настройка EasyIoT Cloud

Настраивать EasyIoT Cloud не нужно, просто зарегистрируйтесь в сервисе EasyIoT Cloud. Чтобы получить доступ к веб-переключателю (будь то Android-приложение или браузерный интерфейс), понадобятся имя пользователя и пароль. Веб-переключатель будет автоматически добавлен в EasyIoT Cloud и появитя в браузерном интерфейсе или интерфейсе Android-приложения после подключения к питанию.

Программирование

Программа для веб-переключателя написана в IDE Arduino. Вам нужно будет установить в нее аддон для ESP8266, и о том, как это сделать, можно прочесть тут.

Дальнейшие действия зависят от того, какой модуль ESP8266 вы используете. Если ваш модуль не оснащен адаптером USB-Serial, то его нужно будет подключить. Схему подключения к адаптеру смотрите на картинке ниже. Не забудьте настроить его на 3,3 вольта. Иногда адаптеры не обеспечивают ESP8266 достаточным напряжением. В этом случае ESP8266 нужно питать только от внешнего источника питания (т.е. нужно отключить от адаптера линию Vcc).

Esp8266-reflash-firmware.jpg

Если вы используете адаптерную плату вроде той, что выпускает команда NodeMCU, просто подключите ее к компьютеру через USB-кабель. Этот способ рекомендуется для новичков.

ESP8266 internet connected switch (EasyIoT Cloud MQTT API V1) - improved 1.jpg

Саму программу можно скачать с GitHub. Вам также понадобится библиотека для клиента MQTT; она называется «esp-mqtt», и ее можно скачать отсюда.

Скачав, установите ее в папку «libraries» IDE Arduino. Программа использует MQTT-брокер, встроенный в EasyIoT Cloud.

В программе нужно будет поменять две строчки, показанные ниже. Это логин и пароль к аккаунту в EasyIoT Cloud. Вместо "usrname" и "pssw" впишите собственные логин и пароль.

#define EIOTCLOUD_USERNAME "usrname"
#define EIOTCLOUD_PASSWORD "pssw"

Кроме того, прямо в программе можно поменять данные для точки доступа (SSID и пароль), но это необязательно, потому что их также можно поменять в веб-интерфейсе EasyIoT Cloud.

Вначале программа считывает настройки EEPROM. Затем пытается подключиться к точке доступа. Если подключиться к точке доступа не удается, через 10 секунд программа переключает ESP8266 в режим точки доступа, чтобы вы могли указать данные для точки доступа, к которой должен подключиться ESP8266. Если подключение к точке доступа прошло успешно, программа проверяет ID модуля. Если ID модуля равно «0», это значит, что переключатель не настроен на EasyIoT Cloud. В этом случае программа добавляет модуль в EasyIoT Cloud и сохраняет ID модуля в настройки EEPROM. После успешной настройки модуль подписывается на топик, содержащий данные о статусе переключателя.

  1 #include <ESP8266WiFi.h>
  2 #include <MQTT.h>
  3 #include <EEPROM.h>
  4 
  5 //#define DEBUG
  6 
  7 #define AP_SSID     "Geek"
  8 #define AP_PASSWORD "G33k"  
  9 
 10 
 11 #define EIOTCLOUD_USERNAME "usrname"
 12 #define EIOTCLOUD_PASSWORD "psswd"
 13 
 14 
 15 // создаем экземпляр класса MQTT:
 16 #define EIOT_CLOUD_ADDRESS "cloud.iot-playground.com"
 17 MQTT myMqtt("", EIOT_CLOUD_ADDRESS, 1883);
 18 
 19 #define CONFIG_START 0
 20 #define CONFIG_VERSION "v01"
 21 
 22 #define AP_CONNECT_TIME 10 //s
 23 
 24 char ap_ssid[16];
 25 char ap_pass[16];
 26 
 27 WiFiServer server(80);
 28 
 29 
 30 struct StoreStruct {
 31   // это для проверки того, ваши ли это настройки:
 32   char version[4];
 33   // переменные для настроек:
 34   uint moduleId;  // ID модуля
 35   bool state;     // состояние
 36   char ssid[20];
 37   char pwd[20];
 38 } storage = {
 39   CONFIG_VERSION,
 40   // модуль по умолчанию - «0»
 41   0,
 42   0, // выкл.
 43   AP_SSID,
 44   AP_PASSWORD
 45 };
 46 
 47 bool stepOk = false;
 48 int buttonState;
 49 
 50 boolean result;
 51 String topic("");
 52 String valueStr("");
 53 
 54 boolean switchState;
 55 const int buttonPin = 0;  
 56 const int outPin = 2;  
 57 int lastButtonState = LOW; 
 58 int cnt = 0;
 59 
 60 void setup() 
 61 {  
 62 #ifdef DEBUG
 63   Serial.begin(115200);
 64 #endif
 65   delay(1000);
 66 
 67   pinMode(buttonPin, INPUT);
 68   digitalWrite(buttonPin, HIGH);
 69   
 70   EEPROM.begin(512);
 71 
 72   switchState = false;  
 73 
 74   loadConfig();
 75   
 76   pinMode(BUILTIN_LED, OUTPUT); 
 77   digitalWrite(BUILTIN_LED, !switchState); 
 78 #ifndef DEBUG
 79   pinMode(outPin, OUTPUT); 
 80   digitalWrite(outPin, switchState);
 81 #endif  
 82 
 83 
 84 #ifdef DEBUG
 85   Serial.println();
 86   Serial.println();
 87   Serial.print("Connecting to ");  //  "Подключение к "
 88   Serial.println(AP_SSID);
 89 #endif  
 90   WiFi.begin(storage.ssid, storage.pwd);
 91 
 92   int i = 0;
 93   
 94   while (WiFi.status() != WL_CONNECTED && i++ < (AP_CONNECT_TIME*2) ) {
 95     delay(500);
 96 #ifdef DEBUG
 97     Serial.print(".");
 98 #endif
 99   }
100 
101   if (!(i < (AP_CONNECT_TIME*2)))
102   {
103     AP_Setup();
104     AP_Loop();
105     ESP.reset();
106   }
107 
108 #ifdef DEBUG
109   Serial.println("");
110   Serial.println("WiFi connected");  //  "Подключились к WiFi"
111   Serial.println("IP address: ");    //  "IP-адрес: "
112   Serial.println(WiFi.localIP());
113 
114   Serial.println("Connecting to MQTT server");  
115              //  "Подключение к MQTT-серверу"
116 #endif
117   // задаем ID клиента;
118   // генерируем название клиента на основе MAC-адреса 
119   // и последних 8 бит счетчика микросекунд;
120   String clientName;
121   uint8_t mac[6];
122   WiFi.macAddress(mac);
123   clientName += macToStr(mac);
124   clientName += "-";
125   clientName += String(micros() & 0xff, 16);
126   myMqtt.setClientId((char*) clientName.c_str());
127 
128 #ifdef DEBUG
129   Serial.print("MQTT client id:");  //  "ID MQTT-клиента"
130   Serial.println(clientName);
131 #endif
132   // настраиваем функции обратного вызова:
133   myMqtt.onConnected(myConnectedCb);
134   myMqtt.onDisconnected(myDisconnectedCb);
135   myMqtt.onPublished(myPublishedCb);
136   myMqtt.onData(myDataCb);
137   
138   //////Serial.println("connect mqtt...");
139                    //  "Подключение к MQTT..."
140   myMqtt.setUserPwd(EIOTCLOUD_USERNAME, EIOTCLOUD_PASSWORD);  
141   myMqtt.connect();
142 
143   delay(500);
144 
145 #ifdef DEBUG
146   Serial.print("ModuleId: ");  //  "ID модуля: "
147   Serial.println(storage.moduleId);
148 #endif
149 
150   // если нужно, создаем модуль: 
151   if (storage.moduleId == 0)
152   {
153     // создаем модуль:
154 #ifdef DEBUG
155     Serial.println("create module: /NewModule");
156                //  "Создание модуля: /NewModule"
157 #endif
158     myMqtt.subscribe("/NewModule");
159     waitOk();
160       
161     // создаем параметр Sensor.Parameter1:
162 #ifdef DEBUG    
163     Serial.println("/"+String(storage.moduleId)+ "/Sensor.Parameter1/NewParameter");    
164 #endif
165     myMqtt.subscribe("/"+String(storage.moduleId)+ "/Sensor.Parameter1/NewParameter");
166     waitOk();
167 
168     // задаем параметр IsCommand:
169 #ifdef DEBUG    
170     Serial.println("/"+String(storage.moduleId)+ "/Sensor.Parameter1/IsCommand");    
171 #endif
172     valueStr = "true";
173     topic  = "/"+String(storage.moduleId)+ "/Sensor.Parameter1/IsCommand";
174     result = myMqtt.publish(topic, valueStr);
175     delay(100);
176 
177     // задаем тип модуля:
178 #ifdef DEBUG        
179     Serial.println("Set module type"); 
180                //  "Настройка типа модуля: /NewModule"
181 
182 #endif
183     valueStr = "MT_DIGITAL_OUTPUT";
184     topic  = "/" + String(storage.moduleId) + "/ModuleType";
185     result = myMqtt.publish(topic, valueStr);
186     delay(100);
187 
188     // сохраняем ID нового модуля:
189     saveConfig();
190   }
191 
192   //switchState = storage.state;
193   //storage.state = !storage.state;
194 
195   storage.state = switchState;
196 
197   Serial.println("Suscribe: /"+String(storage.moduleId)+ "/Sensor.Parameter1"); 
198   myMqtt.subscribe("/"+String(storage.moduleId)+ "/Sensor.Parameter1");
199 }
200 
201 void loop() {
202   while (WiFi.status() != WL_CONNECTED) {
203     delay(500);
204 #ifdef DEBUG        
205     Serial.print(".");
206 #endif
207   }
208 
209 
210   int reading = digitalRead(buttonPin);
211 
212   if (reading != lastButtonState) {
213     if (reading == LOW)
214     {
215       switchState = !switchState;            
216 #ifdef DEBUG
217       Serial.println("button pressed");
218                  //  "Кнопка нажата"
219 #endif
220     }
221 
222 #ifdef DEBUG
223       Serial.println("set state: ");
224                  //  "Задано состояние: "
225       Serial.println(valueStr);
226 #endif
227 
228     lastButtonState = reading;
229   }
230   
231   digitalWrite(BUILTIN_LED, !switchState); 
232 #ifndef DEBUG  
233   digitalWrite(outPin, switchState); 
234 #endif
235   if (switchState != storage.state)
236   {
237     storage.state = switchState;
238     // сохраняем состояние кнопки:
239     saveConfig();
240 
241     valueStr = String(switchState);
242     topic  = "/"+String(storage.moduleId)+ "/Sensor.Parameter1";
243     result = myMqtt.publish(topic, valueStr, 0, 1);    
244 #ifdef DEBUG
245       Serial.print("Publish ");  //  "Публикация "
246 
247       Serial.print(topic);
248       Serial.print(" ");
249       Serial.println(valueStr);
250 #endif  
251     delay(200);
252   }  
253 }
254 
255 void waitOk()
256 {
257   while(!stepOk)
258     delay(100);
259  
260   stepOk = false;
261 }
262 
263 String macToStr(const uint8_t* mac)
264 {
265   String result;
266   for (int i = 0; i < 6; ++i) {
267     result += String(mac[i], 16);
268     if (i < 5)
269       result += ':';
270   }
271   return result;
272 }
273 
274 
275 /*
276  * 
277  */
278 void myConnectedCb() {
279 #ifdef DEBUG
280   Serial.println("connected to MQTT server");
281              //  "Подключение к MQTT-серверу"
282 #endif
283   if (storage.moduleId != 0)
284     myMqtt.subscribe("/" + String(storage.moduleId) + "/Sensor.Parameter1");
285 }
286 
287 void myDisconnectedCb() {
288 #ifdef DEBUG
289   Serial.println("disconnected. try to reconnect...");
290              //  "Соединение разорвано. Пытаемся переподключиться..."
291 #endif
292   delay(500);
293   myMqtt.connect();
294 }
295 
296 void myPublishedCb() {
297 #ifdef DEBUG  
298   Serial.println("published.");  //  "Опубликовано."
299 
300 #endif
301 }
302 
303 void myDataCb(String& topic, String& data) {  
304 #ifdef DEBUG  
305   Serial.print("Received topic:");  //  "Полученный топик:"
306   Serial.print(topic);
307   Serial.print(": ");
308   Serial.println(data);
309 #endif
310   if (topic ==  String("/NewModule"))
311   {
312     storage.moduleId = data.toInt();
313     stepOk = true;
314   }
315   else if (topic == String("/"+String(storage.moduleId)+ "/Sensor.Parameter1/NewParameter"))
316   {
317     stepOk = true;
318   }
319   else if (topic == String("/"+String(storage.moduleId)+ "/Sensor.Parameter1"))
320   {
321     switchState = (data == String("1"))? true: false;
322 #ifdef DEBUG      
323     Serial.print("switch state received: ");
324              //  "Полученное состояние переключателя: "
325     Serial.println(switchState);
326 #endif
327   }
328 }
329 
330 void loadConfig() {
331   // чтобы убедиться, что настройки есть, и это ВАШИ настройки!
332   // если настройки найдены не будут, будут использованы 
333   // настройки по умолчанию:
334   if (EEPROM.read(CONFIG_START + 0) == CONFIG_VERSION[0] &&
335       EEPROM.read(CONFIG_START + 1) == CONFIG_VERSION[1] &&
336       EEPROM.read(CONFIG_START + 2) == CONFIG_VERSION[2])
337     for (unsigned int t=0; t<sizeof(storage); t++)
338       *((char*)&storage + t) = EEPROM.read(CONFIG_START + t);
339 }
340 
341 void saveConfig() {
342   for (unsigned int t=0; t<sizeof(storage); t++)
343     EEPROM.write(CONFIG_START + t, *((char*)&storage + t));
344 
345   EEPROM.commit();
346 }
347 
348 
349 
350 void AP_Setup(void){
351   Serial.println("setting mode");  //  "Режим настройки"
352 
353   WiFi.mode(WIFI_AP);
354 
355   String clientName;
356   clientName += "Thing-";
357   uint8_t mac[6];
358   WiFi.macAddress(mac);
359   clientName += macToStr(mac);
360   
361   Serial.println("starting ap");  //  "Запуск точки доступа"
362   WiFi.softAP((char*) clientName.c_str(), "");
363   Serial.println("running server");  //  "Запуск сервера"
364   server.begin();
365 }
366 
367 void AP_Loop(void){
368 
369   bool  inf_loop = true;
370   int  val = 0;
371   WiFiClient client;
372 
373   Serial.println("AP loop");
374 
375   while(inf_loop){
376     while (!client){
377       Serial.print(".");
378       delay(100);
379       client = server.available();
380     }
381     String ssid;
382     String passwd;
383     // считываем первую строчку запроса:
384     String req = client.readStringUntil('\r');
385     client.flush();
386 
387     // подготавливаем ответ, начинаем со стандартного заголовка:
388     String s = "HTTP/1.1 200 OK\r\n";
389     s += "Content-Type: text/html\r\n\r\n";
390     s += "<!DOCTYPE HTML>\r\n<html>\r\n";
391 
392     if (req.indexOf("&") != -1){
393       int ptr1 = req.indexOf("ssid=", 0);
394       int ptr2 = req.indexOf("&", ptr1);
395       int ptr3 = req.indexOf(" HTTP/",ptr2);
396       ssid = req.substring(ptr1+5, ptr2);
397       passwd = req.substring(ptr2+10, ptr3);    
398       val = -1;
399     }
400 
401     if (val == -1){
402       strcpy(storage.ssid, ssid.c_str());
403       strcpy(storage.pwd, passwd.c_str());
404       
405       saveConfig();
406       //storeAPinfo(ssid, passwd);
407       s += "Setting OK";
408       s += "<br>"; // переходим к следующей строчке
409       s += "Continue / reboot";
410       inf_loop = false;
411     }
412 
413     else{
414       String content="";
415       // показываем значения всех входных аналоговых контактов:
416       content += "<form method=get>";
417       content += "<label>SSID</label><br>";
418       content += "<input  type='text' name='ssid' maxlength='19' size='15' value='"+ String(storage.ssid) +"'><br>";
419       content += "<label>Password</label><br>";
420       content += "<input  type='password' name='password' maxlength='19' size='15' value='"+ String(storage.pwd) +"'><br><br>";
421       content += "<input  type='submit' value='Submit' >";
422       content += "</form>";
423       s += content;
424     }
425     
426     s += "</html>\n";
427     // отправляем ответ клиенту:
428     client.print(s);
429     delay(1);
430     client.stop();
431   }
432 }

Самый простой способ проверить программу – воспользоваться адаптерной платой NodeMCU ESP8266. Для нее не нужно никаких предварительных настроек – просто подключите ее к USB-порту компьютера. Если вы используете эту плату, вы также можете убрать знак комментария у строчки #define DEBUG, расположенной в начале программы; это позволит видеть отладочные сообщения. Кнопка FLASH на ESP8266 будет служить кнопкой ручного изменения состояния переключателя. Состояние светодиода будет отображаться при помощи встроенного светодиода.

[Видео]

Схема

В нашем случае используется модуль ESP-01, но вы можете использовать любой другой модуль с чипом ESP8266. Контакт GPIO2 подключен к транзистору NPN, чтобы управлять твердотельным реле. Максимальная сила тока для нашего твердотельного реле – это 2 ампера. Этого хватит для комнатного освещения, но для проектов, где требуется больше питания (к примеру, для нагревателей) – вряд ли.

Для подачи питания используется понижающий преобразователь, снижающий напряжение до 3,3 вольт. Очень важно поставить на 3,3-вольтовую линию конденсатор на 1000 мкФ. Если забыть о нем, то цепь работать не будет.

Контакт GPIO0 подключен к кнопке – для локального управления веб-переключателем.

Web-switch-scheme.jpg

После включения питания переключатель будет автоматически добавлен в EasyIoT Cloud, а также отобразится в браузерном интерфейсе и Android-приложении.

ESP8266 internet connected switch (EasyIoT Cloud MQTT API V1) - improved 1 1.png


ESP8266 internet connected switch (EasyIoT Cloud MQTT API V1) - improved 1 2.png

Если нужно, переключатель можно переименовать и поместить в нужную группу.

Настройка веб-переключателя

Будучи включенным, веб-переключатель пытается подключиться к точке доступа. Если спустя 10 секунд подключиться к точке доступа так и не удается, веб-переключатель переходит в режим точки доступа. Возьмите мобильный телефон и поищите точку доступа с названием «Thing-xx-xx-xx-xx», где «xx-xx-xx-xx» – это набор случайных цифр. Подключитесь к этой точке доступа, а затем введите в браузере IP-адрес «192.168.4.1».

Откроется страница, где вы сможете настроить название SSID и пароль к ней. Указав нужные данные, кликните по кнопке «Submit». После этого веб-переключатель перезагрузится и подключится к указанной точке доступа.

ESP8266 internet connected switch (EasyIoT Cloud MQTT API V1) - improved 2 1.png

См.также

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