ESP8266:Примеры/Доступ к веб-серверу из любой точки мира
Доступ к веб-серверу из любой точки мира
В этом руководстве мы сделаем так, чтобы веб-сервер из этого примера стал доступен из любой точки мира. В процессе мы воспользуемся бесплатным сервисом ngrok для создания безопасного туннеля к ESP8266, подключенной к вашей локальной сети.
Необходимое оборудование
- Плата ESP8266 - 1шт.;
- Светодиод - 2шт.;
- Резистор 470 Ом- 2 шт.
- Макетная плата - 1шт.;
- Провода перемычки.
Создаем аккаунт в ngrok
Пройдите по этой ссылке и кликните на оранжевую кнопку «SIGN UP» в правом верхнем углу страницы.
Откроется новая страница. Введите свои данные и зарегистрируйте аккаунт.
Создав аккаунт, залогиньтесь и перейдите на вкладку «Auth». Скопируйте строчку из поля «Your Tunnel Authtoken» (англ. «аутентификационный токен тоннеля») в безопасное место (позже она нам пригодится).
У меня получился такой токен: 69X1SnMSGqAz6qo9qDtHo_8tCo6V5q8WeC5YtDrZqy
Перейдите на вкладку «Download» в навигационной панели.
Выберите версию ngrok, соответствующую вашей ОС, и скачайте ее.
Распакуйте скачанную папку и запустите ngrok.
Запускаем безопасный туннель ngrok
Впишите в терминал команду ниже, заменив красный текст на свой IP-адрес и аутентификационный токен туннеля ngrok.
ngrok tcp 192.168.1.22:8888 --authtoken 3V1MfHcNMD9rhBizf8TRs_2whamY91tqX4
Нажмите ↵ Enter , чтобы запустить эту команду.
Если все в порядке, то статус туннеля должен смениться на «online», а в графе «Forwarding» должна появиться перенаправляющая ссылка.
Вписав эту ссылку (в моем случае это «http://0.tcp.ngrok.io:54626/») в браузер, вы сможете получить доступ к веб-серверу из любой точки мира.
При открытии веб-сервера вас всегда будут просить ввести имя пользователя и пароль.
Как работает код
В самом начале скетча мы подключаем библиотеку «ESP8266WiFi», а в следующих двух строчках задаем SSID и пароль для своей WiFi-сети, чтобы ESP8266 мог к ней подключиться.
// Подключаем библиотеку «ESP8266WiFi»:
#include <ESP8266WiFi.h>
// Пишем SSID и пароль для своей WiFi-сети:
const char* ssid = "YOUR_NETWORK_NAME";
const char* password = "YOUR_NETWORK_PASSWORD";
Далее создаем объект веб-сервера и задаем ему порт «8888»:
// Создаем объект веб-сервера с номером порта «8888»:
WiFiServer server(8888);
Создаем переменную «header» для хранения заголовка ответа на запрос, переменные «gpio5_state» и «gpio4_state» для хранения текущего состояния этих GPIO-контактов, а также переменные «gpio5_pin» и «gpio4_pin» для хранения номеров контактов GPIO4 и GPIO5.
// Создаем несколько переменных:
String header;
String gpio5_state = "Off";
String gpio4_state = "Off";
int gpio5_pin = 5;
int gpio4_pin = 4;
Далее создаем блок setup(), код в котором будет запущен только раз – при запуске ESP8266.
// Код в этом блоке будет запущен только один раз:
void setup() {
}
Запускаем последовательную коммуникацию на скорости 115200 бод (для отладки). Переключаем оба GPIO-контакта в режим вывода данных (OUTPUT) и задаем им значение «LOW».
// Этот код будет запущен только один раз:
void setup() {
// Инициализируем последовательную коммуникацию (для отладки):
Serial.begin(115200);
delay(10);
// Подготавливаем GPIO-контакты:
pinMode(gpio5_pin, OUTPUT);
digitalWrite(gpio5_pin, LOW);
pinMode(gpio4_pin, OUTPUT);
digitalWrite(gpio4_pin, LOW);
В следующем фрагменте кода запускаем подключение к WiFi-сети, ждем успешного подключения и печатаем в мониторе порта IDE Arduino IP-адрес ESP8266.
// Подключаемся к WiFi-сети:
Serial.println();
Serial.print("Connecting to ");
// "Подключаемся к "
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
// "Подключились к WiFi"
// Запускаем веб-сервер:
server.begin();
Serial.println("Web server running. Waiting for the ESP IP...");
// "Веб-сервер запущен.
// "Ждем получения IP-адреса ESP8266..."
delay(1000);
// Печатаем IP-адрес ESP8266:
Serial.println(WiFi.localIP());
}
Блок loop() отвечает за то, что будет происходить, когда новый клиент установит соединение с веб-сервером. То есть код в loop() будет постоянно прослушивать новых клиентов. Соответственно, если к веб-серверу подключится новый клиент, он начнет подключаться к нему.
// Код в блоке loop() будет постоянно повторяться:
void loop() {
// Прослушиваем новых клиентов:
WiFiClient client = server.available();
Булева переменная «blank_line» ниже служит для того, чтобы определить конец HTTP-запроса. Кроме того, в этом фрагменте кода используется цикл while(), который будет работать все то время, пока клиент будет подключен к веб-серверу.
if (client) {
Serial.println("New client"); // "Новый клиент"
// Булева переменная, предназначенная
// для определения конца HTTP-запроса:
boolean blank_line = true;
while (client.connected()) {
if (client.available()) {
Теперь давайте добавим в веб-сервер механизм аутентификации, чтобы сделать его более безопасным. После этого любому, кто попытается получить доступ к веб-серверу, придется ввести имя пользователя и пароль.
По умолчанию именем пользователя будет «user», а паролем – «pass». Чуть ниже я расскажу о том, как их поменять. Если пользователь введет правильные имя пользователя и пароль, то получит доступ к веб-странице для управления GPIO-контактами ESP8266.
// Проверяем, правильные ли введены имя пользователя и пароль:
if(header.indexOf("dXNlcjpwYXNz") >= 0) {
В следующем фрагменте кода проверяется, на какую кнопку веб-страницы нажал пользователь. По сути, в нем проверяется, какой вы хотите открыть URL.
К примеру, кликая на кнопку «OFF» для контакта GPIO5, вы открываете ссылку «http://192.168.1.22:8888/gpio5off». Код смотрит на эту ссылку и при помощи нескольких else if() сверяется с тем, что ему нужно сделать. В случае со ссылкой «http://192.168.1.22:8888/gpio5off» он переключит контакт GPIO5 («gpio5_pin») в состояние «LOW».
// Проверяем, правильные ли введены
// имя пользователя и пароль:
if(header.indexOf("dXNlcjpwYXNz") >= 0) {
// Залогинивание прошло успешно:
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
// "Соединение: отключено"
client.println();
// Включаем/выключаем GPIO-контакты:
if(header.indexOf("GET / HTTP/1.1") >= 0) {
Serial.println("Main Web Page");
// "Главная веб-страница"
}
else if(header.indexOf("GET /gpio5on HTTP/1.1") >= 0){
Serial.println("GPIO 5 On");
// "Контакт GPIO5 включен"
gpio5_state = "On";
digitalWrite(gpio5_pin, HIGH);
}
else if(header.indexOf("GET /gpio5off HTTP/1.1") >= 0){
Serial.println("GPIO 5 Off");
// "Контакт GPIO5 выключен"
gpio5_state = "Off";
digitalWrite(gpio5_pin, LOW);
}
else if(header.indexOf("GET /gpio4on HTTP/1.1") >= 0){
Serial.println("GPIO 4 On");
// "Контакт GPIO4 включен"
gpio4_state = "On";
digitalWrite(gpio4_pin, HIGH);
}
else if(header.indexOf("GET /gpio4off HTTP/1.1") >= 0){
Serial.println("GPIO 4 Off");
// "Контакт GPIO4 выключен"
gpio4_state = "Off";
digitalWrite(gpio4_pin, LOW);
}
Страница отправляется клиенту при помощи метода client.println(). Это очень простая веб-страница, использующая фреймворк Bootstrap (см. код ниже).
Более подробно о Bootstrap можно почитать по этой ссылке.
На веб-странице будет 4 кнопки для включения/выключения (переключения между состояниями «HIGH» и «LOW») двух светодиодов, подключенных к контактам GPIO5 и GPIO4.
Кнопки – это HTML-теги <a href=””></a> с CSS-классом, который придает им вид кнопки. Когда вы нажимаете на кнопку, вам открывается другая страница с URL, соответствующим этой кнопке. Именно так ESP8266 и узнает, что ей нужно сделать (т.е. включить или выключить светодиод, и какой именно).
// Ваша веб-страница:
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("<head>");
client.println("<meta name=\"viewport\" content=\"width=device-width, initialscale=1\">");
client.println("<link rel=\"stylesheet\"
href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css\">");
client.println("</head><div class=\"container\">");
client.println("<h1>Web Server</h1>");
client.println("<h2>GPIO 5 - Current State: " + gpio5_state);
client.println("<div class=\"row\">");
client.println("<div class=\"col-md-2\"><a href=\"/gpio5on\" class=\"btn btn-block
btn-lg btn-success\" role=\"button\">ON</a></div>");
client.println("<div class=\"col-md-2\"><a href=\"/gpio5off\" class=\"btn btn-block
btn-lg btn-danger\" role=\"button\">OFF</a></div>");
client.println("</div>");
client.println("<h2>GPIO 4 - Current State: " + gpio4_state);
client.println("<div class=\"row\">");
client.println("<div class=\"col-md-2\"><a href=\"/gpio4on\" class=\"btn btn-block
btn-lg btn-success\" role=\"button\">ON</a></div>");
client.println("<div class=\"col-md-2\"><a href=\"/gpio4off\" class=\"btn btn-block
btn-lg btn-danger\" role=\"button\">OFF</a></div>");
client.println("</div></div></html>");
Если вы введете неправильные имя пользователя и пароль, в браузере будет напечатано сообщение «Authentication failed» («Аутентификация не удалась»).
// Неправильные имя пользователя и пароль,
// поэтому HTTP-запрос завершился неудачей...
else {
client.println("HTTP/1.1 401 Unauthorized");
client.println("WWW-Authenticate: Basic realm=\"Secure\"");
client.println("Content-Type: text/html");
client.println();
client.println("<html>Authentication failed</html>");
}
Строчки кода в конце скетча очищают переменную «header», завершают цикл loop() и завершают подключение.
header = "";
break;
}
if (c == '\n') {
// Когда нужно перейти к следующей строчке:
blank_line = true;
}
else if (c != '\r') {
// Когда в текущей строке найден символ:
blank_line = false;
}
}
}
// Завершаем подключение к клиенту:
delay(1);
client.stop();
Serial.println("Client disconnected.");
// "Клиент отключился."
}
}
Как задать собственные имя пользователя и пароль
В скетче, который мы разобрали выше, именем пользователя является «user», а паролем – «pass». Уверен, вы захотите задать вместо них собственные имя пользователя и пароль. Пройдите по этой ссылке. В первом поле впишите следующее:
your_username:your_password
В моем случае это «user:pass».
Затем нажмите на зеленую кнопку «Encode» – это сгенерирует строку в формате Base64. В моем случае это «dXNlcjpwYXNz».
Скопируйте ее и вставьте в это место скетча:
// Проверяем, правильные ли введены имя пользователя и пароль:
if(header.indexOf("dXNlcjpwYXNz") >= 0) {
Загружаем код
Подключите ESP8266 к ПК и откройте IDE Arduino. Кликните по «Инструменты» > «Плата» (Tools > Board) и выберите подключенную плату.
Выберите COM-порт, к которому подключена ваша плата.
Проверив все настройки, нажмите на кнопку «Загрузка» (Upload):
Подождите несколько секунд, пока не увидите сообщение «Загрузка завершена» (Done uploading) в левом нижнем углу IDE Arduino.
Копируем IP-адрес ESP8266
Загрузив веб-серверный скетч на ESP8266, кликните в IDE Arduino на «Инструменты» > «Монитор порта» (Tools > Serial Monitor). Монитор порта – это окно, где ESP8266, загрузившись, напечатает свой IP-адрес.
Мой IP-адрес – это «192.168.1.22» (см. на скриншоте ниже). Ваш IP-адрес, скорее всего, будет другим. Сохраните его, т.к. позднее вы с его помощью перейдете на страницу веб-сервера.
Проблема – В мониторе порта не печатается IP-адрес
Если у вас возникла такая проблема, можно попробовать сделать следующее:
- Оставьте монитор порта открытым;
- ESP-12E (или FTDI-программатор) должен оставаться подключенным к ПК;
- Перезапустите ESP-12E, нажав на встроенную в него кнопку.
Спустя секунду ESP8266 должна напечатать в мониторе порта свой IP-адрес.
Устанавливаем программу для сканирования IP-адресов
Если IP-адрес в мониторе порта по-прежнему не появляется, то вам нужно будет установить на свой ПК программу для сканирования IP-адресов. Она будет искать все девайсы в вашей сети.
- Загрузите ее (это бесплатно). Можете выбрать между Advanced IP Scanner (Windows) и Angry IP Scanner (MAC OS X, Windows или Linux);
- Установите программу (в это время ESP8266 должен быть включен, и на нем должен работать скетч, написанный нами выше);
- Откройте программу для сканирования IP-адресов и нажмите на кнопку «Сканировать» (Scan);
Дайте процессу завершиться (это должно занять несколько минут).
Заходим на веб-сервер
Перед заходом на веб-сервер нужно сделать следующее:
- Перезапустите ESP8266;
- Откройте браузер;
- Впишите в его адресную строку IP-адрес, скопированный ранее из монитора порта IDE Arduino, и добавьте в конце «8888» (в моем случае – «192.168.1.22:8888»);
- Нажмите на ↵ Enter .
После этого в браузере должно выскочить окно с запросом ввести имя пользователя и пароль:
- Введите имя пользователя и пароль
- Залогиньтесь
В результате в браузере должна загрузиться примерно такая страница:
Схема
Загрузив код на ESP8266, подключите компоненты друг к другу, как показано на схеме ниже (для подключения светодиодов можно воспользоваться резисторами номиналом между 270 Ом и 470 Ом).
Код
/*********
Rui Santos
Complete project details at http://randomnerdtutorials.com
*********/
// Including the ESP8266 WiFi library
#include <ESP8266WiFi.h>
// Replace with your network details
const char* ssid = "YOUR_NETWORK_NAME";
const char* password = "YOUR_NETWORK_PASSWORD";
// Web Server on port 8888
WiFiServer server(8888);
// variables
String header;
String gpio5_state = "Off";
String gpio4_state = "Off";
int gpio5_pin = 5;
int gpio4_pin = 4;
// only runs once
void setup() {
// Initializing serial port for debugging purposes
Serial.begin(115200);
delay(10);
// preparing GPIOs
pinMode(gpio5_pin, OUTPUT);
digitalWrite(gpio5_pin, LOW);
pinMode(gpio4_pin, OUTPUT);
digitalWrite(gpio4_pin, LOW);
// Connecting to WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
// Starting the web server
server.begin();
Serial.println("Web server running. Waiting for the ESP IP...");
delay(1000);
// Printing the ESP IP address
Serial.println(WiFi.localIP());
}
// runs over and over again
void loop() {
// Listenning for new clients
WiFiClient client = server.available();
if (client) {
Serial.println("New client");
// boolean to locate when the http request ends
boolean blank_line = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
header += c;
if (c == '\n' && blank_line) {
// checking if header is valid
// dXNlcjpwYXNz = 'user:pass' (user:pass) base64 encode
Serial.print(header);
// Finding the right credential string
if(header.indexOf("dXNlcjpwYXNz") >= 0) {
//successful login
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
// turns the GPIOs on and off
if(header.indexOf("GET / HTTP/1.1") >= 0) {
Serial.println("Main Web Page");
}
else if(header.indexOf("GET /gpio5on HTTP/1.1") >= 0){
Serial.println("GPIO 5 On");
gpio5_state = "On";
digitalWrite(gpio5_pin, HIGH);
}
else if(header.indexOf("GET /gpio5off HTTP/1.1") >= 0){
Serial.println("GPIO 5 Off");
gpio5_state = "Off";
digitalWrite(gpio5_pin, LOW);
}
else if(header.indexOf("GET /gpio4on HTTP/1.1") >= 0){
Serial.println("GPIO 4 On");
gpio4_state = "On";
digitalWrite(gpio4_pin, HIGH);
}
else if(header.indexOf("GET /gpio4off HTTP/1.1") >= 0){
Serial.println("GPIO 4 Off");
gpio4_state = "Off";
digitalWrite(gpio4_pin, LOW);
}
// your web page
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("<head>");
client.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css\">");
client.println("</head><div class=\"container\">");
client.println("<h1>Web Server</h1>");
client.println("<h2>GPIO 5 - Current State: " + gpio5_state);
client.println("<div class=\"row\">");
client.println("<div class=\"col-md-2\"><a href=\"/gpio5on\" class=\"btn btn-block btn-lg btn-success\" role=\"button\">ON</a></div>");
client.println("<div class=\"col-md-2\"><a href=\"/gpio5off\" class=\"btn btn-block btn-lg btn-danger\" role=\"button\">OFF</a></div>");
client.println("</div>");
client.println("<h2>GPIO 4 - Current State: " + gpio4_state);
client.println("<div class=\"row\">");
client.println("<div class=\"col-md-2\"><a href=\"/gpio4on\" class=\"btn btn-block btn-lg btn-success\" role=\"button\">ON</a></div>");
client.println("<div class=\"col-md-2\"><a href=\"/gpio4off\" class=\"btn btn-block btn-lg btn-danger\" role=\"button\">OFF</a></div>");
client.println("</div></div></html>");
}
// wrong user or passm, so http request fails...
else {
client.println("HTTP/1.1 401 Unauthorized");
client.println("WWW-Authenticate: Basic realm=\"Secure\"");
client.println("Content-Type: text/html");
client.println();
client.println("<html>Authentication failed</html>");
}
header = "";
break;
}
if (c == '\n') {
// when starts reading a new line
blank_line = true;
}
else if (c != '\r') {
// when finds a character on the current line
blank_line = false;
}
}
}
// closing the client connection
delay(1);
client.stop();
Serial.println("Client disconnected.");
}
}
См.также
Внешние ссылки
ESP8266 AT-команды | |
---|---|
Список AT-команд | |
Базовые команды |
|
Команды для WiFi |
|
Команды для TCP/IP |
|