[Перевод] Starting Electronics: руководство по веб-серверам на Arduino. Часть 13. Графический индикатор на веб-странице

ypqerquj-ucdpilkargiejd7irg.jpeg

От переводчика. В этой части руководства рассказывается как подключать к страницам Arduino сервера и использовать Javascript компоненты, в данном случае компонент стрелочного индикатора.

Это открывает поистине безграничные возможности для проектирования веб-страниц: вам доступны тысячи готовых Javascript компонентов для реализации различных функций и дизайна веб-интерфейсов для ваших проектов.
В этой части руководства рассматривается создание графического индикатора на веб-странице для отображения состояния одного из аналоговых входов Arduino. Значения индикатора обновляются при помощи Ajax.

Сам индикатор написан на JavaScript и использует HTML5 canvas. Индикатор используется как компонент (без изменений) и просто настраивается для отображения значения одного из аналоговых входов Arduino. Значения индикатора обновляются каждые 200 мс.

В этом видео показана работа сервера Arduino и индикатора, размещённого на веб-странице.

Компонент индикатора


Компонент индикатора «HTML Canvas Gauges» можно скачать на Github. Можно также ознакомиться со статьёй (на английском) в которой даны подробности использования компонента.

В этом уроке используется Javascript код из файла Gauge.min.js (загруженный по вышеприведенной ссылке).

Arduino скетч и веб-страница

Скетч Ардуино


Скетч Arduino для этого урока:

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

  Описание:  Arduino сервер с SD картой, динамически отображающий на графическом индикаторе состояние аналогового входа при помощи Ajax.

  Оборудование: контроллер Arduino Uno, плата Ethernet Shield, microSD карта памяти 2 ГБ, потенциометр, подключённый на пин A2.
                
  Программное обеспечение: среда разработки Arduino IDE, microSD карта с файлом index.htm
  
  Ссылки:
    - 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
    - SD Card library documentation: http://arduino.cc/en/Reference/SD
    - Gauge from: https://github.com/Mikhus/canv-gauge
  
  Дата создания: 27 марта 2013
  Изменения: 19 июня 2013
 
  Author:       W.A. Smith, http://startingelectronics.org
--------------------------------------------------------------*/

#include 
#include 
#include 

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 0, 20); // IP-адрес (нужно изменить на актуальный для вашей сети)
EthernetServer server(80);

#define REQ_BUF_SZ   50
char HTTP_req[REQ_BUF_SZ] = {0}; // Хранится как null terminated string
char req_index = 0; // индекс буфера

File webFile;

void setup() {
    // отключение Ethernet
    pinMode(10, OUTPUT);
    digitalWrite(10, HIGH);

    Serial.begin(115200);
    
    Serial.println("Initializing SD card...");
    if (!SD.begin(4)) {
        Serial.println("ERROR - SD card initialization failed!");
        return;
    }
    Serial.println("SUCCESS - SD card initialized.");

    if (!SD.exists("index.htm")) {
        Serial.println("ERROR - Can't find index.htm file!");
        return;
    }
    Serial.println("SUCCESS - Found index.htm file.");
    
    Ethernet.begin(mac, ip);
    server.begin();
}

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

    if (client) {
        boolean currentLineIsBlank = true;
        while (client.connected()) {
            if (client.available()) {
                char c = client.read();  // получаем очередной байт (символ) от клиента
                // сохраняем последний элемент массива 0 (null terminate string)
                if (req_index < (REQ_BUF_SZ - 1)) {
                    HTTP_req[req_index] = c; // сохраняем символ HTTP запроса
                    req_index++;
                }

                if (c == '\n' && currentLineIsBlank) {
                    // Посылаем http заголовок
                    client.println("HTTP/1.1 200 OK");
                    // HTML или XML запрос
                    if (StrContains(HTTP_req, "ajax_inputs")) {
                        client.println("Content-Type: text/xml");
                        client.println("Connection: keep-alive");
                        client.println();
                        XML_response(client);
                    } else {
                        client.println("Content-Type: text/html");
                        client.println("Connection: keep-alive");
                        client.println();
                        webFile = SD.open("index.htm");
                        if (webFile) {
                            while(webFile.available()) {
                                client.write(webFile.read());
                            }
                            webFile.close();
                        }
                    }
                    Serial.print(HTTP_req);
                    // Обнуляем массив (буфер)
                    req_index = 0;
                    StrClear(HTTP_req, REQ_BUF_SZ);
                    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)
}

// Посылаем XML файл
void XML_response(EthernetClient cl) {
    int analog_val;
    
    cl.print("");
    cl.print("");
    // read analog pin A2
    analog_val = analogRead(2);
    cl.print("");
    cl.print(analog_val);
    cl.print("");
    cl.print("");
}

// Обнуление массива
void StrClear(char *str, char length) {
    for (int i = 0; i < length; i++) {
        str[i] = 0;
    }
}

// Поиск подстроки
// 1, если найдена
// 0, если не найдена
char StrContains(char *str, char *sfind) {
    char found = 0;
    char index = 0;
    char len;

    len = strlen(str);
    
    if (strlen(sfind) > len) {
        return 0;
    }
    while (index < len) {
        if (str[index] == sfind[found]) {
            found++;
            if (strlen(sfind) == found) {
                return 1;
            }
        }
        else {
            found = 0;
        }
        index++;
    }

    return 0;
}


Этот скетч представляет собой немного измененную версию скетча из предыдущего урока.

Скетч отправляет значение аналогового входа A2 в браузер, которое затем отображается на индикаторе на веб-странице. Состояние индикатора обновляется с помощью Ajax, а значение аналогового входа A2 передаётся как часть XML файла (от Arduino сервера к браузеру).

Веб-страница, размещенная на сервере Arduino, хранится на microSD карте памяти, подключённой к плате Arduino Ethernet Shield.

Веб-страница


Ниже показано содержание веб-страницы этого примера (без кода самого индикатора).

i7ufkwnqzuxtxsti7emrznu_4xe.png

Эта веб-страница — это модифицированная версия страницы, использованной в предыдущем уроке.

Примечание переводчика: на скриншоте выше отсутствует код самого компонента индикатора, видна только одна строка от него (eval (function…), остальное автор просто обрезал, что может вызвать непонимание читателя. В общем, можно представлять себе, что на месте этой строки находится полный код компонента индикатора.

Индикатор


Индикатор подключается к веб-странице путем вставки его кода между тегами script в заголовке страницы.

Индикатор отображается в теле страницы (body) с помощью HTML5 тега canvas.

Обновление индикатора


Значение входа A2 Arduino принимается браузером и отображается на веб-странице так же, как и в предыдущей части этого руководства. Также значение входа A2 сохраняется в Javascript переменной data_val, чтобы индикатор мог его использовать.

Значение в data_val передается индикатору с помощью Javascript строки в свойстве data-onready тега canvas:

setInterval( function() {Gauge.Collection.get('an_gauge_1').setValue(data_val);}, 200);


Цифра 200 в вышеприведенной строке предписывает индикатору обновляться каждые 200 миллисекунд.

Интервалы


Индикатор обновляется каждые 200 мс, и периодичность Ajax запросов также установлена на 200 мс.

Интервал для Ajax запросов задаётся в следующей строке Javascript кода:

setTimeout('GetArduinoInputs()', 200);


Такая частота следования Ajax запросов может вызвать проблемы в загруженной или медленной сети. Если у вас появятся какие-то проблемы с этим, попробуйте изменить это значение на 1000, чтобы запросы производились реже, один раз в секунду.

Запуск скетча


Подключите потенциометр, как показано на принципиальной схеме в 7-й части этого руководства. Кнопки, показанные на схеме, не будут использоваться в этом уроке.

Скопируйте веб-страницу index.htm (см. ниже) на microSD карту памяти и вставьте ее в слот на плате Arduino Ethernet Shield.

Загрузите вышеприведенный скетч на плату Arduino сервера.

Исходный код веб-страницы


Загрузите исходный код веб-страницы (файл index.htm) здесь:

Arduino_web_gauge.zip (6,3 КБ)

Лицензия на индикатор (MIT) включена в архив в виде текстового файла.

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


В этой части всё описано достаточно подробно и понятно, от себя добавлю только несколько слов по поводу компонента индикатора.

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

Ну и должно быть понятно, что по такому (или подобному) принципу можно подключить к веб-странице множество других Javascript компонентов.

Часть 1, часть 2, часть 3, часть 4, часть 5, часть 6, часть 7, часть 8, часть 9, часть 10, часть 11, часть 12.

mxuanbovcusqgmqdgugvpnql8vq.jpeg

© Habrahabr.ru