ESP8266:Примеры/Веб-переключатель, управляемый при помощи EasyIoT Cloud
Черновик |
Веб-переключатель, управляемый при помощи 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).
Если вы используете адаптерную плату вроде той, что выпускает команда NodeMCU, просто подключите ее к компьютеру через USB-кабель. Этот способ рекомендуется для новичков.
Саму программу можно скачать с 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. После успешной настройки модуль подписывается на топик, содержащий данные о статусе переключателя.
#include <ESP8266WiFi.h>
#include <MQTT.h>
#include <EEPROM.h>
//#define DEBUG
#define AP_SSID "Geek"
#define AP_PASSWORD "G33k"
#define EIOTCLOUD_USERNAME "usrname"
#define EIOTCLOUD_PASSWORD "psswd"
// создаем экземпляр класса MQTT:
#define EIOT_CLOUD_ADDRESS "cloud.iot-playground.com"
MQTT myMqtt("", EIOT_CLOUD_ADDRESS, 1883);
#define CONFIG_START 0
#define CONFIG_VERSION "v01"
#define AP_CONNECT_TIME 10 //s
char ap_ssid[16];
char ap_pass[16];
WiFiServer server(80);
struct StoreStruct {
// это для проверки того, ваши ли это настройки:
char version[4];
// переменные для настроек:
uint moduleId; // ID модуля
bool state; // состояние
char ssid[20];
char pwd[20];
} storage = {
CONFIG_VERSION,
// модуль по умолчанию - «0»
0,
0, // выкл.
AP_SSID,
AP_PASSWORD
};
bool stepOk = false;
int buttonState;
boolean result;
String topic("");
String valueStr("");
boolean switchState;
const int buttonPin = 0;
const int outPin = 2;
int lastButtonState = LOW;
int cnt = 0;
void setup()
{
#ifdef DEBUG
Serial.begin(115200);
#endif
delay(1000);
pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, HIGH);
EEPROM.begin(512);
switchState = false;
loadConfig();
pinMode(BUILTIN_LED, OUTPUT);
digitalWrite(BUILTIN_LED, !switchState);
#ifndef DEBUG
pinMode(outPin, OUTPUT);
digitalWrite(outPin, switchState);
#endif
#ifdef DEBUG
Serial.println();
Serial.println();
Serial.print("Connecting to "); // "Подключение к "
Serial.println(AP_SSID);
#endif
WiFi.begin(storage.ssid, storage.pwd);
int i = 0;
while (WiFi.status() != WL_CONNECTED && i++ < (AP_CONNECT_TIME*2) ) {
delay(500);
#ifdef DEBUG
Serial.print(".");
#endif
}
if (!(i < (AP_CONNECT_TIME*2)))
{
AP_Setup();
AP_Loop();
ESP.reset();
}
#ifdef DEBUG
Serial.println("");
Serial.println("WiFi connected"); // "Подключились к WiFi"
Serial.println("IP address: "); // "IP-адрес: "
Serial.println(WiFi.localIP());
Serial.println("Connecting to MQTT server");
// "Подключение к MQTT-серверу"
#endif
// задаем ID клиента;
// генерируем название клиента на основе MAC-адреса
// и последних 8 бит счетчика микросекунд;
String clientName;
uint8_t mac[6];
WiFi.macAddress(mac);
clientName += macToStr(mac);
clientName += "-";
clientName += String(micros() & 0xff, 16);
myMqtt.setClientId((char*) clientName.c_str());
#ifdef DEBUG
Serial.print("MQTT client id:"); // "ID MQTT-клиента"
Serial.println(clientName);
#endif
// настраиваем функции обратного вызова:
myMqtt.onConnected(myConnectedCb);
myMqtt.onDisconnected(myDisconnectedCb);
myMqtt.onPublished(myPublishedCb);
myMqtt.onData(myDataCb);
//////Serial.println("connect mqtt...");
// "Подключение к MQTT..."
myMqtt.setUserPwd(EIOTCLOUD_USERNAME, EIOTCLOUD_PASSWORD);
myMqtt.connect();
delay(500);
#ifdef DEBUG
Serial.print("ModuleId: "); // "ID модуля: "
Serial.println(storage.moduleId);
#endif
// если нужно, создаем модуль:
if (storage.moduleId == 0)
{
// создаем модуль:
#ifdef DEBUG
Serial.println("create module: /NewModule");
// "Создание модуля: /NewModule"
#endif
myMqtt.subscribe("/NewModule");
waitOk();
// создаем параметр Sensor.Parameter1:
#ifdef DEBUG
Serial.println("/"+String(storage.moduleId)+ "/Sensor.Parameter1/NewParameter");
#endif
myMqtt.subscribe("/"+String(storage.moduleId)+ "/Sensor.Parameter1/NewParameter");
waitOk();
// задаем параметр IsCommand:
#ifdef DEBUG
Serial.println("/"+String(storage.moduleId)+ "/Sensor.Parameter1/IsCommand");
#endif
valueStr = "true";
topic = "/"+String(storage.moduleId)+ "/Sensor.Parameter1/IsCommand";
result = myMqtt.publish(topic, valueStr);
delay(100);
// задаем тип модуля:
#ifdef DEBUG
Serial.println("Set module type");
// "Настройка типа модуля: /NewModule"
#endif
valueStr = "MT_DIGITAL_OUTPUT";
topic = "/" + String(storage.moduleId) + "/ModuleType";
result = myMqtt.publish(topic, valueStr);
delay(100);
// сохраняем ID нового модуля:
saveConfig();
}
//switchState = storage.state;
//storage.state = !storage.state;
storage.state = switchState;
Serial.println("Suscribe: /"+String(storage.moduleId)+ "/Sensor.Parameter1");
myMqtt.subscribe("/"+String(storage.moduleId)+ "/Sensor.Parameter1");
}
void loop() {
while (WiFi.status() != WL_CONNECTED) {
delay(500);
#ifdef DEBUG
Serial.print(".");
#endif
}
int reading = digitalRead(buttonPin);
if (reading != lastButtonState) {
if (reading == LOW)
{
switchState = !switchState;
#ifdef DEBUG
Serial.println("button pressed");
// "Кнопка нажата"
#endif
}
#ifdef DEBUG
Serial.println("set state: ");
// "Задано состояние: "
Serial.println(valueStr);
#endif
lastButtonState = reading;
}
digitalWrite(BUILTIN_LED, !switchState);
#ifndef DEBUG
digitalWrite(outPin, switchState);
#endif
if (switchState != storage.state)
{
storage.state = switchState;
// сохраняем состояние кнопки:
saveConfig();
valueStr = String(switchState);
topic = "/"+String(storage.moduleId)+ "/Sensor.Parameter1";
result = myMqtt.publish(topic, valueStr, 0, 1);
#ifdef DEBUG
Serial.print("Publish "); // "Публикация "
Serial.print(topic);
Serial.print(" ");
Serial.println(valueStr);
#endif
delay(200);
}
}
void waitOk()
{
while(!stepOk)
delay(100);
stepOk = false;
}
String macToStr(const uint8_t* mac)
{
String result;
for (int i = 0; i < 6; ++i) {
result += String(mac[i], 16);
if (i < 5)
result += ':';
}
return result;
}
/*
*
*/
void myConnectedCb() {
#ifdef DEBUG
Serial.println("connected to MQTT server");
// "Подключение к MQTT-серверу"
#endif
if (storage.moduleId != 0)
myMqtt.subscribe("/" + String(storage.moduleId) + "/Sensor.Parameter1");
}
void myDisconnectedCb() {
#ifdef DEBUG
Serial.println("disconnected. try to reconnect...");
// "Соединение разорвано. Пытаемся переподключиться..."
#endif
delay(500);
myMqtt.connect();
}
void myPublishedCb() {
#ifdef DEBUG
Serial.println("published."); // "Опубликовано."
#endif
}
void myDataCb(String& topic, String& data) {
#ifdef DEBUG
Serial.print("Received topic:"); // "Полученный топик:"
Serial.print(topic);
Serial.print(": ");
Serial.println(data);
#endif
if (topic == String("/NewModule"))
{
storage.moduleId = data.toInt();
stepOk = true;
}
else if (topic == String("/"+String(storage.moduleId)+ "/Sensor.Parameter1/NewParameter"))
{
stepOk = true;
}
else if (topic == String("/"+String(storage.moduleId)+ "/Sensor.Parameter1"))
{
switchState = (data == String("1"))? true: false;
#ifdef DEBUG
Serial.print("switch state received: ");
// "Полученное состояние переключателя: "
Serial.println(switchState);
#endif
}
}
void loadConfig() {
// чтобы убедиться, что настройки есть, и это ВАШИ настройки!
// если настройки найдены не будут, будут использованы
// настройки по умолчанию:
if (EEPROM.read(CONFIG_START + 0) == CONFIG_VERSION[0] &&
EEPROM.read(CONFIG_START + 1) == CONFIG_VERSION[1] &&
EEPROM.read(CONFIG_START + 2) == CONFIG_VERSION[2])
for (unsigned int t=0; t<sizeof(storage); t++)
*((char*)&storage + t) = EEPROM.read(CONFIG_START + t);
}
void saveConfig() {
for (unsigned int t=0; t<sizeof(storage); t++)
EEPROM.write(CONFIG_START + t, *((char*)&storage + t));
EEPROM.commit();
}
void AP_Setup(void){
Serial.println("setting mode"); // "Режим настройки"
WiFi.mode(WIFI_AP);
String clientName;
clientName += "Thing-";
uint8_t mac[6];
WiFi.macAddress(mac);
clientName += macToStr(mac);
Serial.println("starting ap"); // "Запуск точки доступа"
WiFi.softAP((char*) clientName.c_str(), "");
Serial.println("running server"); // "Запуск сервера"
server.begin();
}
void AP_Loop(void){
bool inf_loop = true;
int val = 0;
WiFiClient client;
Serial.println("AP loop");
while(inf_loop){
while (!client){
Serial.print(".");
delay(100);
client = server.available();
}
String ssid;
String passwd;
// считываем первую строчку запроса:
String req = client.readStringUntil('\r');
client.flush();
// подготавливаем ответ, начинаем со стандартного заголовка:
String s = "HTTP/1.1 200 OK\r\n";
s += "Content-Type: text/html\r\n\r\n";
s += "<!DOCTYPE HTML>\r\n<html>\r\n";
if (req.indexOf("&") != -1){
int ptr1 = req.indexOf("ssid=", 0);
int ptr2 = req.indexOf("&", ptr1);
int ptr3 = req.indexOf(" HTTP/",ptr2);
ssid = req.substring(ptr1+5, ptr2);
passwd = req.substring(ptr2+10, ptr3);
val = -1;
}
if (val == -1){
strcpy(storage.ssid, ssid.c_str());
strcpy(storage.pwd, passwd.c_str());
saveConfig();
//storeAPinfo(ssid, passwd);
s += "Setting OK";
s += "<br>"; // переходим к следующей строчке
s += "Continue / reboot";
inf_loop = false;
}
else{
String content="";
// показываем значения всех входных аналоговых контактов:
content += "<form method=get>";
content += "<label>SSID</label><br>";
content += "<input type='text' name='ssid' maxlength='19' size='15' value='"+ String(storage.ssid) +"'><br>";
content += "<label>Password</label><br>";
content += "<input type='password' name='password' maxlength='19' size='15' value='"+ String(storage.pwd) +"'><br><br>";
content += "<input type='submit' value='Submit' >";
content += "</form>";
s += content;
}
s += "</html>\n";
// отправляем ответ клиенту:
client.print(s);
delay(1);
client.stop();
}
}
Самый простой способ проверить программу – воспользоваться адаптерной платой NodeMCU ESP8266. Для нее не нужно никаких предварительных настроек – просто подключите ее к USB-порту компьютера. Если вы используете эту плату, вы также можете убрать знак комментария у строчки #define DEBUG, расположенной в начале программы; это позволит видеть отладочные сообщения. Кнопка FLASH на ESP8266 будет служить кнопкой ручного изменения состояния переключателя. Состояние светодиода будет отображаться при помощи встроенного светодиода.
[Видео]
Схема
В нашем случае используется модуль ESP-01, но вы можете использовать любой другой модуль с чипом ESP8266. Контакт GPIO2 подключен к транзистору NPN, чтобы управлять твердотельным реле. Максимальная сила тока для нашего твердотельного реле – это 2 ампера. Этого хватит для комнатного освещения, но для проектов, где требуется больше питания (к примеру, для нагревателей) – вряд ли.
Для подачи питания используется понижающий преобразователь, снижающий напряжение до 3,3 вольт. Очень важно поставить на 3,3-вольтовую линию конденсатор на 1000 мкФ. Если забыть о нем, то цепь работать не будет.
Контакт GPIO0 подключен к кнопке – для локального управления веб-переключателем.
После включения питания переключатель будет автоматически добавлен в EasyIoT Cloud, а также отобразится в браузерном интерфейсе и Android-приложении.
Если нужно, переключатель можно переименовать и поместить в нужную группу.
Настройка веб-переключателя
Будучи включенным, веб-переключатель пытается подключиться к точке доступа. Если спустя 10 секунд подключиться к точке доступа так и не удается, веб-переключатель переходит в режим точки доступа. Возьмите мобильный телефон и поищите точку доступа с названием «Thing-xx-xx-xx-xx», где «xx-xx-xx-xx» – это набор случайных цифр. Подключитесь к этой точке доступа, а затем введите в браузере IP-адрес «192.168.4.1».
Откроется страница, где вы сможете настроить название SSID и пароль к ней. Указав нужные данные, кликните по кнопке «Submit». После этого веб-переключатель перезагрузится и подключится к указанной точке доступа.