Во многих приложениях интернета вещей (IoT) достаточно распространенной является ситуация, когда необходимо непрерывно, в режиме реального времени, производить мониторинг показаний с какого либо датчика. Самый простой способ сделать это – использовать модуль ESP8266 в качестве веб-сервера, на котором будет формироваться необходимая нам HTML страница с показаниями датчика (пример подобного проекта вы можете посмотреть в этой статье).
Но проблема при таком подходе заключается в том, что веб-браузеру необходимо будет периодически обновлять данную веб-страницу чтобы получать обновленные показания с датчика. Это не только неэффективно, но и требует значительного числа временных циклов работы процессора, во время которых он мог бы выполнять другие полезные задачи. Решение данной проблемы известно – это “Asynchronous JavaScript and XML” или, сокращенно, AJAX. С помощью технологии AJAX мы можем производить мониторинг данных в режиме реального времени без обновления всей веб-страницы, что значительно экономит ресурсы работы процессора. В данной статье мы рассмотрим использование веб-сервера с поддержкой AJAX в модуле ESP8266.
Что такое AJAX
Как мы уже выяснили ранее, AJAX расшифровывается как for “Asynchronous JavaScript and XML” и эта технология может быть использована для обновления содержимого части веб-страницы без полной перезагрузки всей страницы. Это достигается с помощью "стихийного/спонтанного" процесса запроса и приема данных. Функции AJAX’а позволяют производить обновления веб-контента асинхронно. Это означает, что браузеру нет необходимости обновлять веб-страницу целиком и он может обновить только часть контента страницы.
Примером работы технологии AJAX может служить ввод поискового запроса в поисковике Google. Как только мы в строке поиска Google начинаем вводить свой запрос, то Google сразу начинает показывать нам связанные с ним запросы. Во время этого процесса не происходит обновления содержимого всей страницы, но необходимая для обновления часть страницы обновляется в фоновом режиме с использованием AJAX.
Как работает AJAX
AJAX является комбинацией следующих элементов:
- XML (Extensible Markup Language – расширяемый язык разметки).
- JavaScript и HTML
XML – это расширяемый язык разметки. XML в основном используется для получения данных сервера в определенном формате. Хотя он может получать данные и в виде обычного текста. Когда пользователь посещает веб-страницу и происходит событие, в нашем случае это “Нажатие кнопки”, JavaScript создает объект XMLHttpRequest, который затем передает информацию в формате XML между веб-браузером и веб-сервером. Объект XMLHttpRequest отправляет запрос на обновленные данные страницы на веб-сервер, сервер обрабатывает запрос, ответ создается на стороне сервера и отправляется обратно в браузер, который затем использует JavaScript для обработки ответа и отображения его на веб-странице.
JavaScript выполняет процесс обновления в AJAX. Запрос на обновленное содержимое отформатирован в XML, чтобы сделать его понятным, а JavaScript обновляет содержимое для пользователя, просматривающего обновленную страницу.
Принцип работы AJAX наглядно показан на следующем рисунке.
Как показано на приведенном рисунке, на запрос AJAX браузер отправляет запрос XMLHttpRequest на сервер с помощью javascript. Этот запрос (объект) включает дата, содержащие сведения о том, что конкретно было запрошено. Сервер в ответ передает только те данные, которые были запрошены клиентской стороной. После этого браузер получает данные и обновляет только ту часть веб-страницы, которую необходимо обновить вместо перезагрузки всей веб-страницы.
Необходимые компоненты
- Модуль NodeMCU ESP8266 (купить на AliExpress).
- Датчик температуры LM35 (купить на AliExpress).
- Светодиод (купить на AliExpress).
- Резистор 150 Ом (купить на AliExpress).
- Макетная плата.
- Соединительные провода.
- Кабель для программирования.
Схема проекта
Схема веб-сервера AJAX на основе NodeMCU ESP8266 представлена на следующем рисунке.
Как видите, схема очень проста. Мы подключили светодиод через токоограничивающий резистор к контакту D0 платы NodeMCU ESP8266, мы будем управлять его переключениями с нашего веб-сервера. С датчика LM35 мы будем считывать показания температуры и соответствующим образом обновлять содержимое нашей веб-страницы. Датчик LM35 запитывается от контакта 3.3V платы NodeMCU ESP8266. Поскольку LM35 – это аналоговый датчик температуры, то его выходной контакт подключен к контакту A0 платы. Более подробно о подключении датчика LM35 к плате NodeMCU ESP8266 можно прочитать в этой статье.
Объяснение программы веб-сервера AJAX для модуля ESP8266
Подготовка NodeMCU ESP8266 для загрузки кода программы
Если до этого вы еще не загружали программы в вашу плату NodeMCU ESP8266, то ее необходимо подготовить для загрузки кода. Для этого выполните следующую последовательность шагов.
1. В Arduino IDE выберите пункт меню File–>Preferences–>Settings.
2. Введите строку https://arduino.esp8266.com/stable/package_esp8266com_index.json в поле ‘Additional Board Manager URL’ и нажмите ‘Ok’.
3. После этого откройте Tools > Board > Boards Manager (менеджер плат) и в поле поиска введите ESP8266, затем в открывшихся результатах поиска выберите самую последнюю версию платы и нажмите на кнопку install (установить).
4. После того как установка будет закончена, выберите пункт меню Tools ->Board -> и в нем выберите NodeMCU 1.0 (ESP-12E Module). После этого вы сможете программировать вашу NodeMCU с помощью Arduino IDE.
Создание заголовочного файла для HTML страницы
Первоначально, HTML код нашей веб-страницы, на котором мы будем показывать считываемые с датчика lm35 значения и кнопку для управления светодиодом, мы должны конвертировать в заголовочный файл (.h файл), который будет подключаться к основному коду нашей программы. Так удобнее. HTML код для всей веб-страницы будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
const char webpage[] PROGMEM = R"=====( <!DOCTYPE html> <html> <style type="text/css"> .button { background-color: #4CAF50; /* Green */ border: none; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; } </style> <body style="background-color: #f9e79f "> <center> <div> <h1>AJAX BASED ESP8266 WEBSERVER</h1> <button class="button" onclick="send(1)">LED ON</button> <button class="button" onclick="send(0)">LED OFF</button><BR> </div> <br> <div><h2> Temp(C): <span id="adc_val">0</span><br><br> LED State: <span id="state">NA</span> </h2> </div> <script> function send(led_sts) { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById("state").innerHTML = this.responseText; } }; xhttp.open("GET", "led_set?state="+led_sts, true); xhttp.send(); } setInterval(function() { getData(); }, 2000); function getData() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById("adc_val").innerHTML = this.responseText; } }; xhttp.open("GET", "adcread", true); xhttp.send(); } </script> </center> </body> </html> )====="; |
Сначала создайте новый файл в блокноте и сохраните его с расширением .h – это расширение для заголовочных файлов языка C. Назовите этот файл “index.h” и скопируйте в него представленный HTML код веб-страницы. Представленный код ответственен за визуальную часть нашей веб-страницы.
Примечание: не забывайте поместить этот заголовочный файл в каталог, в котором будет располагаться код нашей основной программы для NodeMCU.
Объяснение кода программы
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Первым делом в программе мы подключим заголовочные файлы используемых библиотек: “ESP8266WiFi.h”, “WiFiClient.h” и “ESP8266WebServer.h”.
1 2 3 |
#include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> |
Далее мы подключим заголовочный файл нашей веб-страницы, который мы создали ранее. Не забудьте его поместить в тот же самый каталог, где будет находиться файл с кодом основной программы для ESP8266.
1 |
#include "index.h" |
После этого укажите идентификатор сети (SSID) и пароль для вашей Wi-Fi сети – они будут необходимы чтобы ваша NodeMCU могла подключаться к сети Интернет.
1 2 |
const char* ssid = "admin"; const char* password = "12345678"; |
Далее создадим объект ESP8266WebServer с именем "сервер" и портом по умолчанию 80.
1 |
ESP8266WebServer server(80); |
Затем запрограммируем функцию handleRoot для обработки веб-страницы и передачи всей веб-страницы на клиентскую сторону когда это будет необходимо.
1 2 3 4 5 |
void handleRoot() { String s = webpage; server.send(200, "text/html", s); } |
Также запрограммируем функцию sensor_data для чтения данных температуры и передачи их на веб-страницу после необходимых преобразований.
1 2 3 4 5 6 7 |
void sensor_data() { int a = analogRead(A0); int temp= a/4.35; String sensor_value = String(temp); server.send(200, "text/plane", sensor_value); } |
Внутри функции led_control информация, поступающая от веб-страницы, будет приниматься и сравниваться с определенными значениями чтобы управлять состоянием светодиода.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void led_control() { String state = "OFF"; String act_state = server.arg("state"); if(act_state == "1") { digitalWrite(LED,HIGH); //LED ON state = "ON"; } else { digitalWrite(LED,LOW); //LED OFF state = "OFF"; } server.send(200, "text/plane", state); } |
Далее, для подключения NodeMCU ESP8266 к сети Интернет, мы будем вызывать функцию WiFi.begin() с идентификатором сети и паролем в качестве аргументов. Затем мы будем проверять установлено ли соединение с сетью с помощью функции WiFi.status() и затем после успешного соединения печатать в окне монитора последовательной связи IP адрес нашей платы.
1 2 3 4 5 6 7 8 9 10 |
WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { Serial.print("Connecting..."); } Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); |
Затем мы будем вызывать функции “handleRoot”, “led_control” и “sensor_data” когда клиент будет запрашивать URL с индексами "/", “/led_set” и “/adcread”.
1 2 3 4 |
server.on("/", handleRoot); server.on("/led_set", led_control); server.on("/adcread", sensor_data); server.begin(); |
Тестирование работы веб-сервера AJAX
Перед тестированием работы проекта убедитесь в том, что ваш роутер включен. Подайте питанию на схему проекта. В нашем случае мы используем питание через кабель USB, но можно использовать и внешний источник питания.
Затем нам необходимо определить IP адрес платы NodeMCU ESP8266 – в нашем проекте он будет печататься в окне монитора последовательной связи Arduino IDE. Откройте окно монитора последовательной связи и нажмите кнопку сброса (Reset button) на NodeMCU один раз, после этого ее IP адрес должен напечататься в окне монитора последовательной связи. Скопируйте этот IP адрес в адресную строку вашего браузера.
Убедитесь в том, что ваш компьютер и NodeMCU ESP8266 подключены к одной и той же Wi-Fi сети. После того, как вы введете IP адрес в адресную строку вашего браузера у вас в браузере должна открыться веб-страница, формируемая нашим веб-сервером на основе NodeMCU ESP8266, с которой вы сможете управлять состоянием светодиода и наблюдать показания температуры, считываемые с датчика lm35.
Исходный код программы (скетча)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
/*Arduino code for ESP8266 AJAX Webserver www.circuitdigest.com */ #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include "index.h" #define LED D0 const char* ssid = "admin"; const char* password = "12345678"; ESP8266WebServer server(80); void handleRoot() { String s = webpage; server.send(200, "text/html", s); } void sensor_data() { int a = analogRead(A0); int temp= a/4.35; String sensor_value = String(temp); server.send(200, "text/plane", sensor_value); } void led_control() { String state = "OFF"; String act_state = server.arg("state"); if(act_state == "1") { digitalWrite(LED,HIGH); //LED ON (включение светодиода) state = "ON"; } else { digitalWrite(LED,LOW); //LED OFF (выключение светодиода) state = "OFF"; } server.send(200, "text/plane", state); } void setup(void) { Serial.begin(115200); WiFi.begin(ssid, password); Serial.println(""); pinMode(LED,OUTPUT); while (WiFi.status() != WL_CONNECTED) { Serial.print("Connecting..."); } Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); server.on("/", handleRoot); server.on("/led_set", led_control); server.on("/adcread", sensor_data); server.begin(); } void loop(void) { server.handleClient(); } |
Добрый день.
Подскажите пож-ста, а как обновлять несколько переменных (допустим:
Temp1(C): 0
Temp2(C): 0
Temp3(C): 0)
без перезагрузки страницы?
Добрый вечер. Да точно также, по аналогии. А в чем у вас проблема?
HTML:
...
Temp(C): 0
Temp_1(C): 0
Temp_2(C): 0
LED State: NA
...
// вот в этом коде так?
function getData() {
var xhttp = new XMLHttpRequest(); //Temp(C)
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("adc_val").innerHTML =
this.responseText;
}
};
xhttp.open("GET", "adcread", true);
xhttp.send();
var xhttp = new XMLHttpRequest(); // Temp_1(C)
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("adc_val_1").innerHTML =
this.responseText;
}
};
xhttp.open("GET", "adcread_1", true);
xhttp.send();
var xhttp = new XMLHttpRequest(); // // Temp_2(C)
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("adc_val_2").innerHTML =
this.responseText;
}
};
xhttp.open("GET", "adcread_2", true);
xhttp.send();
}
или по другому как то?
А вы в html коде связали Temp(C), Temp_1(C) и Temp_2(C) с обработчиками adc_val, adc_val_1 и adc_val_2? И в скетч нужно внести изменения, вам же теперь три числа надо передавать, а не одно
HTML:
......
Temp(C): 0
Temp_1(C): 0
Temp_2(C): 0
LED State: NA
.....
скетч - изменения
void sensor_data()
{
int a = analogRead(A0);
int temp= a/4.35;
String sensor_value = String(temp);
server.send(200, "text/plane", sensor_value);
}
void sensor_data_1()
{
int a = analogRead_1(A0);
int temp= a/4.35;
String sensor_value = String(temp);
server.send(200, "text/plane", sensor_value);
}
void sensor_data_2()
{
int a = analogRead_2(A0);
int temp= a/4.35;
String sensor_value = String(temp);
server.send(200, "text/plane", sensor_value);
}
....
server.on("/adcread", sensor_data);
server.on("/adcread_1", sensor_data_1);
server.on("/adcread_2", sensor_data_2);
получается так?
HTML:
......
""
Temp(C): "0"
Temp_1(C): "0"
Temp_2(C): "0"
LED State: "NA"
""
.....
без ковычек " в сообщении запись не отображается
HTML:
......
*div**h2*
Temp(C): *span id="adc_val"*0*/span*br*
Temp_1(C): *span id="adc_val_1"*0*/span**br*
Temp_2(C): *span id="adc_val_2"*0*/span**br**br*
LED State: *span id="state"*NA*/span*
*/h2*
.....
произвел замену знака < на * иначе нет отображения в сообщении
Я понял. Это ограничения на вредоносный код в комментариях так работают, к сожалению. Я вижу что в html файле вы добавили поддержку трех переменных, а вот в скетче вы считываете всего одно значение температуры. Как вы этим одним значением собираетесь менять три ваших значения на экране?
Благодарю за урок, код работает (с учетом изменений в скетче)!
Да, на каждую переменную идет свой запрос.
А можно ли одним запросом передавать значения N переменных и затем их отображать со своими id в HTML странице. Может это JSON - но как?
От админа: я рад что у вас получилось, но, к сожалению, на ваш очередной вопрос ответа не знаю
А как изменить цвет фона и цвет текста в коде html
body style="background-color: #f9e79f - цвет фона
А для изменения цвета текста нужно дописывать код, например, можно так:
h1 style="color:#ff0000">AJAX BASED ESP8266 WEBSERVER
То есть дописать нужно к существующему коду style="color:#ff0000"