Starting Electronics: руководство по веб-серверам на Arduino. Часть 7. Отображение DI и AI входов при помощи AJAX

от автора

От переводчика. Автор руководства медленно, но неуклонно ведёт заинтересованного читателя к пониманию работы веб-серверов на Arduino и всему из этого вытекающему: поняв как это работает, можно самостоятельно создавать очень интересные IoT проекты, функционал которых ограничен только вашей фантазией.

На этом уроке рассматривается динамическое отображение на веб-странице состояния кнопок и аналогового входа при помощи AJAX. Скетч из этого урока можно легко модифицировать для отображения любого (в разумных пределах, конечно) количества кнопок и аналоговых входов, а также доработать код для отображения других параметров контроллера Arduino.

На этом уроке мы рассмотрим отображение на веб-странице текущего состояния двух кнопок и одного аналогового входа контроллера Arduino.

JavaScript используется для выполнения периодических AJAX запросов к веб-серверу о состоянии кнопок и аналогового входа.

В следующем видео показана работа системы: с помощью AJAX обновляются данные о состоянии кнопки и аналогового входа, без перезагрузки всей страницы и каких-либо видимых артефактов.

Принципиальная электрическая схема

На нижеприведенной схеме показано подключение кнопок (на пины D7 и D8) к контроллеру Arduino с платой Ethernet Shield. Потенциометр подключен к аналоговому входу A2 и при его помощи можно изменять напряжение на этом входе и отображать его текущее значение на веб-странице.

Принципиальная схема подключений кнопок и потенциометра к плате Arduino:

Скетч

Скетч для этого урока — это модифицированная версия скетча из предыдущей статьи.

/*--------------------------------------------------------------   Скетч:      eth_websrv_AJAX_IN    Описание:  Arduino веб-сервер, отображающий состояние двух кнопок и аналогового входа на веб-странице при помощи AJAX.      Оборудование: контроллер Arduino Uno, плата Ethernet Shield, 2 кнопки, потенциометр.                    Программное обеспечение: среда разработки Arduino IDE      Ссылки:     - WebServer example by David A. Mellis and modified by Tom Igoe     - Ethernet library documentation: http://arduino.cc/en/Reference/Ethernet     - Learning PHP, MySQL & JavaScript by Robin Nixon, O'Reilly publishers    Дата создания:         20 февраля 2013     Автор:       W.A. Smith, http://startingelectronics.org --------------------------------------------------------------*/  #include <SPI.h> #include <Ethernet.h>  byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(10, 0, 0, 20); // IP-адрес (нужно изменить на актуальный для вашей сети) EthernetServer server(80);  String HTTP_req; // для хранения HTTP запроса  void setup() {     Ethernet.begin(mac, ip);     server.begin();     Serial.begin(115200);     pinMode(7, INPUT); // кнопка подключена к плате Arduino на пин D7     pinMode(8, INPUT); // кнопка подключена к плате Arduino на пин D8 }  void loop() {     EthernetClient client = server.available();  // try to get client      if (client) {         boolean currentLineIsBlank = true;         while (client.connected()) {             if (client.available()) {                 char c = client.read(); // получаем очередной байт (символ) от клиента                 HTTP_req += c; // сохраняем символ HTTP запроса                 if (c == '\n' && currentLineIsBlank) {                     // Посылаем http заголовок                     client.println("HTTP/1.1 200 OK");                     client.println("Content-Type: text/html");                     client.println("Connection: keep-alive");                     client.println();                      if (HTTP_req.indexOf("ajax_switch") > -1) {                         // AJAX запрос                         GetAjaxData(client);                     } else {                         // Посылка веб-страницы, содержащей JavaScript код и AJAX вызовы                         client.println("<!DOCTYPE html>");                         client.println("<html>");                         client.println("<head>");                         client.println("<title>Arduino Web Page</title>");                         client.println("<script>");                         client.println("function GetSwitchAnalogData() {");                         client.println(                             "nocache = \"&nocache=\" + Math.random() * 1000000;");                         client.println("var request = new XMLHttpRequest();");                         client.println("request.onreadystatechange = function() {");                         client.println("if (this.readyState == 4) {");                         client.println("if (this.status == 200) {");                         client.println("if (this.responseText != null) {");                         client.println("document.getElementById(\"sw_an_data\")\ .innerHTML = this.responseText;");                         client.println("}}}}");                         client.println(                         "request.open(\"GET\", \"ajax_switch\" + nocache, true);");                         client.println("request.send(null);");                         client.println("setTimeout('GetSwitchAnalogData()', 1000);");                         client.println("}");                         client.println("</script>");                         client.println("</head>");                          // Тело веб-страницы                         client.println("<body onload=\"GetSwitchAnalogData()\">");                         client.println("<h1>Arduino AJAX Input</h1>");                         client.println("<div id=\"sw_an_data\">");                         client.println("</div>");                         client.println("</body>");                         client.println("</html>");                     }                     // Выводим принятый HTTP запрос в Serial                     Serial.print(HTTP_req);                     HTTP_req = "";                     break;                 }                 if (c == '\n') {                     currentLineIsBlank = true;                 } else if (c != '\r') {                     currentLineIsBlank = false;                 }             } // end if (client.available())         } // end while (client.connected())         delay(1);         client.stop();     } // end if (client) }  // Определение состояния цифровых и аналогового входов и посылка данных о них в браузер  void GetAjaxData(EthernetClient cl) {     int analog_val;          if (digitalRead(7)) {         cl.println("<p>Switch 7 state: ON</p>");     } else {         cl.println("<p>Switch 7 state: OFF</p>");     }      if (digitalRead(8)) {         cl.println("<p>Switch 8 state: ON</p>");     } else {         cl.println("<p>Switch 8 state: OFF</p>");     }      // Работа с аналоговым входом A2      analog_val = analogRead(2);     cl.print("<p>Analog A2: ");     cl.print(analog_val);     cl.println("</p>"); } 

Код веб-страницы

Вышеприведенный Arduino скетч создает и отправляет в браузер следующий HTML код:

Изменения в скетче (относительно версии из предыдущего урока)

Пины D7 и D8 контроллера Arduino настраиваются как входы в разделе setup() скетча.

    pinMode(7, INPUT); // кнопка подключена к плате Arduino на пин D7     pinMode(8, INPUT); // кнопка подключена к плате Arduino на пин D8 

Была переименована JavaScript функция, которая обрабатывает AJAX вызовы.

GetSwitchAnalogData() 

Также была переименована Arduino функция, отвечающая на AJAX запросы.

void GetAjaxData(EthernetClient cl) { 

Под заголовком H1 в HTML коде создан элемент div с идентификатором «sw_an_data». Сам тег div не отображается на странице, но служит местом для размещения JavaScript информации (данных о состоянии кнопок и аналогового входа, получаемых от Arduino).

Отправка запроса из браузера

JavaScript функция GetSwitchAnalogData() вызывается каждую секунду и каждую секунду отправляет GET запросы на Arduino веб-сервер.

Получение и обработка AJAX запроса на Arduino

Когда Arduino сервер определяет, что получил AJAX запрос, то вызывает функцию GetAjaxData(). Эта функция считывает состояние двух кнопок и отправляет их состояние (ON или OFF) в ответ браузеру. Эта функция также считывает значение аналогового входа A2 и отправляет эти данные браузеру.

Отображение данных в веб-браузере

Когда веб-браузер получает данные, запрошенные им у Arduino сервера, он просто вставляет их в тег div с идентификатором sw_an_data.

От переводчика о 7-й части

Начинающим трудно понять работу этой системы потому, что она довольно «хитро» устроена — Javascript функция вызывает сама себя и при этом имеет участки кода, которые вступают в работу отложенно, только после после поступления ответа от сервера Arduino. А сервер Arduino, в свою очередь, не просто посылает HTML код веб-страницы, а «синтезирует» ещё и сам Javascript код, который потом будет взаимодействовать с самим Arduino сервером.

У начинающих, с непривычки, ум может зайти за разум от таких «наворотов». Для начала я бы посоветовал обратить внимание на следующий участок Arduino кода:

                    if (HTTP_req.indexOf("ajax_switch") > -1) {                         // AJAX запрос                         GetAjaxData(client);                     } else {                         // Посылка веб-страницы, содержащей JavaScript код и AJAX вызовы                         client.println("<!DOCTYPE html>"); 

Это один из «узловых» моментов всей логики работы системы, который нужно хорошо понимать: здесь веб-сервер определяет какой запрос к нему пришёл — если это «простой» GET запрос, то он выдаёт браузеру веб-страницу, а если этот AJAX запрос, то он выдаёт только небольшой ответ с AJAX данными о состоянии кнопок и аналогового входа.

Важно понимать: веб-страницу сервер выдаёт один раз в начале «сеанса», а затем (массово и беспрерывно) посылает только AJAX ответы на запросы им же сгенерированного Javascript кода внутри этой веб-страницы.

Лучше понять логику сетевого взаимодействия можно, если нажать (в браузере Firefox) клавишу F12 и выбрать вкладку «Network».

Часть 1, часть 2, часть 3, часть 4, часть 5, часть 6.


ссылка на оригинал статьи https://habr.com/ru/company/timeweb/blog/718518/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *