Pull to refresh
1891.1
Timeweb Cloud
То самое облако

Starting Electronics: руководство по веб-серверам на Arduino. Часть 11. SD веб-сервер c Ajax

Level of difficultyMedium
Reading time5 min
Views2.8K
Original author: Starting Electronics


От переводчика. Примеры руководства становятся всё более и более практичными — на этом уроке автор «скрестил» Arduino веб-сервер с SD картой памяти и динамическое отображение состояния кнопки (входов/выходов) на веб-странице.

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


Исходные данные для этого урока: Arduino веб-сервер с платой Ethernet Shield и microSD картой памяти, на которой размещается веб-страница. На этой веб-странице с помощью Ajax динамически отображается состояние кнопки, подключённой к Arduino.

В предыдущих частях этого руководства веб-страница, размещенная на SD карте, никогда не использовалась для отображения состояния входов Arduino — все веб-страницы, отображающие ввод/вывод, были частью скетча Arduino.

В этом уроке веб-страница, отображающая состояние кнопки, размещается на SD карте памяти.

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


В этом примере кнопка подключается к контакту D3 Arduino по следующей схеме:



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


Код (скетч Arduino) и веб-страница этого урока представляют собой комбинацию 2-й части («Веб-сервер с SD картой») и 7-й части («Отображение DI и AI входов при помощи AJAX») из этого руководства.

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

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


Веб-страница, состоящая из HTML и JavaScript кода (для реализации Ajax), показана ниже:


Веб-страница, размещающаяся на SD карте памяти (файл index.htm)

Эта страница сохраняется на microSD карту в виде файла index.htm — это в основном тот же HTML/JavaScript код, который создавался (генерировался) Arduino скетчем в 7-й части («Отображение DI и AI входов при помощи AJAX»), но с измененным текстом заголовка.

Скопируйте нижеприведённый код и вставьте в файл index.htm.

<!DOCTYPE html>
<html>
    <head>
        <title>Arduino SD Card Web Page using Ajax</title>
        <script>
        function GetSwitchState()  {
            nocache = "&nocache=" + Math.random() * 1000000;
            var request = new XMLHttpRequest();
            request.onreadystatechange = function()  {
                if (this.readyState == 4) {
                    if (this.status == 200) {
                        if (this.responseText != null) {
                            document.getElementById("switch_txt").innerHTML = this.responseText;
                        }
                    }
                }
            }
            request.open("GET", "ajax_switch" + nocache, true);
            request.send(null);
            setTimeout('GetSwitchState()', 1000);
        }
      </script>
    </head>

    <body onload="GetSwitchState()">
        <h1>Arduino Switch State from SD Card Web Page using Ajax</h1>
        <p id="switch_txt">Switch state: Not requested...</p>
    </body>
</html>

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


Arduino скетч показан ниже. Для работы системы требуется, чтобы указанный выше HTML/JavaScript код был доступен на microSD карте в виде файла index.htm.

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

  Описание:  Arduino веб-сервер с SD картой, динамически (раз в секунду) отображающий текущее состояние кнопки на веб-странице при помощи Ajax.
  
  Оборудование: контроллер Arduino Uno, плата Ethernet Shield, microSD карта памяти 2 ГБ, кнопка, подключённая на пин D3 Arduino

  Программное обеспечение: среда разработки 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

  Дата создания: 25 марта 2013
  Изменения: 17 июня 2013
 
  Автор: W.A. Smith, http://startingelectronics.org
--------------------------------------------------------------*/

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>

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

// Буфер для HTTP запросов
#define REQ_BUF_SZ   40
char HTTP_req[REQ_BUF_SZ] = {0}; // Хранится как null terminated string
char req_index = 0; // индекс буфера

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.");

    pinMode(3, INPUT); // Кнопка на D3
    
    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");
                    client.println("Content-Type: text/html");
                    client.println("Connection: keep-alive");
                    client.println();
                    
                    // Ajax запрос
                    if (StrContains(HTTP_req, "ajax_switch")) {
                        // получаем состояние кнопки и посылаем соответствующий текст
                        GetSwitchState(client);
                    }
                    else {  // запрос веб-страницы
                        // посылаем страницу
                        webFile = SD.open("index.htm");
                        if (webFile) {
                            while(webFile.available()) {
                                client.write(webFile.read());
                            }
                            webFile.close();
                        }
                    }
                    // дублируем HTTP запрос в Serial
                    Serial.println(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)
}

// Посылаем состояние кнопки браузеру
void GetSwitchState(EthernetClient cl) {
    if (digitalRead(3)) {
        cl.println("Switch state: ON");
    } else {
        cl.println("Switch state: OFF");
    }
}

// Обнуляем массив (буфер)
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;
}

Как работает скетч


Этот скетч работает так же, как и скетч из 7-й части этого руководства, за исключением того, что вместо отправки веб-страницы построчно из кода скетча Arduino, веб-страница посылается браузеру из файла index.htm на SD карте.

Поскольку мы используем Ajax, веб-страница (при загрузке в веб-браузере) отправляет тот же запрос текущего состояния кнопки, что и код в 7-й части. Если бы мы не использовали Ajax, то Arduino нужно было бы прочитать файл index.htm с SD карты и изменить часть, показывающую текущее состояние кнопки, а затем отправить обратно всю изменённую веб-страницу. Использование Ajax делает это процесс более простым и эффективным.

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


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

И вообще, для лучшего понимания рекомендуется проходить все уроки последовательно и проводить практические эксперименты с кодом из этих статей.

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


Tags:
Hubs:
Total votes 12: ↑12 and ↓0+12
Comments0

Articles

Information

Website
timeweb.cloud
Registered
Founded
Employees
201–500 employees
Location
Россия
Representative
Timeweb Cloud