От переводчика. На шестом уроке мы добрались до практически полезного кода — с его помощью можно создать работающий пример «бесшовного» автоматического взаимодействия между клиентом (страницей в веб-браузере) и Arduino веб-сервером, который передаёт данные о состоянии подключённого оборудования и внутренних переменных.

Тут нужно помнить, что это только «концептуальный» и базовый пример такого взаимодействия, максимально упрощённый для лучшего понимания начинающими сути самой технологии. Когда вы поймёте принцип, то далее сможете как угодно улучшать и модернизировать этот код. Например, уделить внимание его быстродействию и безопасности.


При помощи небольшой модификации HTML и JavaScript кода в скетче Arduino из предыдущей части этого руководства, можно заставить веб-сервер автоматически обновлять статус кнопки на веб-странице. Ручное инициирование AJAX запросов, которое использовалось в предыдущей части, больше не нужно.

Прежде чем продолжить знакомство с этой частью, вам нужно пройти предыдущую часть руководства и понять как работают AJAX запросы.

В следующем видеоролике показано, как веб-сервер Arduino автоматически обновляет состояние кнопки с помощью AJAX.


Скетч AJAX веб-сервера Arduino


Используйте то же оборудование, что и в предыдущей части этого руководства:
  • Контроллер Arduino Uno
  • Плата Ethernet Shield
  • Кнопка
  • Резистор 10 кОм
  • Соединительные провода

Кнопка подключается к плате Arduino/Ethernet Shield так, как показано на принципиальной схеме ниже. В изначальном состоянии вывод D3 контроллера подтянут к земле при помощи резистора 10 кОм (низкий потенциал, LOW или «0»), а после нажатия кнопки на вывод D3 подаётся высокий потенциал (HIGH или «1»).



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

Модифицированный скетч:

/*--------------------------------------------------------------
  Скетч:      eth_websrv_AJAX_switch_auto

  Описание:  Arduino веб-сервер, отображающий состояние кнопки на веб-странице при помощи AJAX. Состояние кнопки обновляется автоматически.
  
  Оборудование: контроллер Arduino Uno, плата Ethernet Shield, кнопка.
                
  Программное обеспечение: среда разработки 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

  Дата создания:         13 февраля 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(3, INPUT); // кнопка подключена к плате Arduino на пин D3
}

void loop() {
    EthernetClient client = server.available();

    if (client) {  // got 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 запрос состояния кнопки
                        GetSwitchState(client);
                    }
                    else {
                        // Посылка веб-страницы, содержащей JavaScript код и AJAX вызовы
                        client.println("<!DOCTYPE html>");
                        client.println("<html>");
                        
                        // Заголовок веб-страницы и встроенный в неё код JavaScript
                        client.println("<head>");
                        client.println("<title>Arduino Web Page</title>");
                        client.println("<script>");
                        client.println("function GetSwitchState() {");
                        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(\"switch_txt\")\
.innerHTML = this.responseText;");
                        client.println("}}}}");
                        client.println(
                        "request.open(\"GET\", \"ajax_switch\" + nocache, true);");
                        client.println("request.send(null);");
                        client.println("setTimeout('GetSwitchState()', 1000);");
                        client.println("}");
                        client.println("</script>");
                        client.println("</head>");

                        // Тело веб-страницы
                        client.println("<body onload=\"GetSwitchState()\">");
                        client.println("<h1>Arduino AJAX Switch Status</h1>");
                        client.println(
                        "<p id=\"switch_txt\">Switch state: Not requested...</p>");
                        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)
} // loop

// Посылка данных о состоянии кнопки браузеру

void GetSwitchState(EthernetClient cl) {
    if (digitalRead(3)) {
        cl.println("Switch state: ON");
    } else {
        cl.println("Switch state: OFF");
    }
}


Изменения в HTML и JavaScript коде


Ниже показаны изменения, внесенные в HTML файл, который скетч Arduino отправляет в веб-браузер (этот файл отправляется построчно с помощью функции client.println() в скетче).



Модифицированный код веб-страницы для работы автоматических AJAX запросов.

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


Во-первых, удаляется код, который создает кнопку на веб-странице, поскольку эта кнопка больше не нужна. На изображении выше он закомментирован.

Вызов функции GetSwitchState()


Функция GetSwitchState(), которая раньше вызывалась при нажатии кнопки на веб-странице, теперь автоматически вызывается при загрузке страницы. Это достигается добавлением её вызова в тег body (<body onload=«GetSwitchState()»>).

Примечание переводчика: здесь речь идёт о JavaScript функции GetSwitchState(), а не об одноимённой Arduino функции, которая занимается определением состояния кнопки, подключённой к серверу.

Эта строка добавляется в HTML код веб-страницы при помощи вызова следующей функции:

client.println("<body onload=\"GetSwitchState()\">");

Ежесекундные AJAX вызовы


Если мы не предпримем специальных действий и не изменим код для осуществления периодических вызовов, функция GetSwitchState() будет вызвана только один раз при загрузке веб-страницы.

Следующая строка кода добавляется в конец функции GetSwitchState(), чтобы эта функция автоматически вызывалась каждую секунду:

setTimeout('GetSwitchState()', 1000);

Эта строка JavaScript кода вызывает функцию GetSwitchState() (саму себя) каждые 1000 миллисекунд (каждую секунду). Таким образом каждую секунду выполняется AJAX запрос, который получает информацию о состоянии кнопки и обновляет её на веб-странице.

Этот код добавляется на веб-страницу при помощи следующей строки в Arduino скетче:

client.println("setTimeout('GetSwitchState()', 1000);");


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


Если в двух словах резюмировать произведённые автором руководства улучшения предыдущего скетча, то он просто добавил на страницу JavaScript код, который автоматически раз в секунду вызывает функцию GetSwitchState(), которая, в свою очередь, автоматически, без участия пользователя, производит AJAX запросы к Arduino серверу.

В результате мы получаем динамическую систему, которая «сама» обновляет данные на веб-странице и делает это без каких-либо видимых артефактов.

Этот пример реализует однонаправленную передачу информации (о состоянии кнопки) сервер — клиент, если сюда добавить ещё и передачу (пользовательских) данных от клиента к серверу, то мы получим полностью интерактивную и двунаправленную систему.

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


Комментарии (1)


  1. fiego
    00.00.0000 00:00

    if (this.readyState == 4) {
      if (this.status == 200) {
        if (this.responseText != null) {
          document.getElementById("sw_an_data").innerHTML = this.responseText;
        }
      }
    }
    

    Мне одному этот код глаз резанул?

    if (this.readyState == 4 && this.status == 200 && this.responseText != null) {
      document.getElementById("sw_an_data").innerHTML = this.responseText;
    }

    Полный эквивалент и не режет глаз.

    Ещё можно было б использвать уж fetch.