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

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

Перевод: Максим Кузьмин (Cubewriter)
Перевел 1850 статей для сайта.

Контакты:

Проверка/Оформление/Редактирование: Мякишев Е.А.


Ambox content.png Черновик


Веб-переключатель, управляемый при помощи 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

См.также

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

  1. iot-playground.com -