ARDUINO – programovanie mikrokontrolérov Atmel

0

(web server na SD karte – zobrazenie analógových hodnôt v grafickej podobe - prax)

V predchádzajúcej časti sme si vysvetlili teóriu, ako možno na webovej stránke zobrazovať iba meniace  sa hodnoty bez potreby „prekresľovania“ celej webovej stránky. Už vieme, že na to slúži systém AJAX, čo je asynchrónny JavaScrip a XML. V dnešnej časti tieto teoretické znalosti využijeme pri stavbe webového servera na Arduine, ktorý bude snímať analógové hodnoty pomocou potenciometra, pripojeného k analógovému pinu Arduina a tieto zobrazovať na obrazovke webového prehliadača v tvare grafického merača.

Hardvérové zapojenie

Zapojenie sme si popísali v predchádzajúcej časti, ale pre zopakovanie si uvedieme pripojovaciu schému (obr.č.1). Potenciometer je pripojený krajnými bodmi k napájaciemu napätiu (5 Voltov a GND) a stredný vývod k analógovému pinu A2. V prípade, že stred potenciometra pripojíme k inému analógovému pinu, musíme upraviť aj skeč Arduina! Inak sa hodnoty nebudú zobrazovať správne.

HTML  stránka

Teraz si vytvoríme HTML stránku s názvom index.htm, ktorá bude obsahovať prvky AJAXu.

Jej neúplný obsah je na výpise č.1:

<!DOCTYPE html>
<html>
    <head>
        <title>Arduino Ajax Meter</title>
        <script>
             var data_val = 0;
 
< --- tu patrí kód merača --->
      
       function GetArduinoInputs()
             {
                    nocache = "&nocache=" + Math.random() * 1000000;
                    var request = new XMLHttpRequest();
                    request.onreadystatechange = function()
                    {
                           if (this.readyState == 4) {
                                  if (this.status == 200) {
                                        if (this.responseXML != null) {
                                               document.getElementById("input3").innerHTML =
                                                      this.responseXML.getElementsByTagName('analog')[0].childNodes[0].nodeValue;
                                                      data_val = this.responseXML.getElementsByTagName('analog')[0].childNodes[0].nodeValue;
                                        }
                                  }
                           }
                    }
                    request.open("GET", "ajax_inputs" + nocache, true);
                    request.send(null);
                    setTimeout('GetArduinoInputs()', 200);
                   
             }
       </script>
    </head>
    <body onload="GetArduinoInputs()">
        <h1>Arduino Ajax Meter</h1>
        <p>Analógový vstup (A2): <span id="input3">...</span></p>
        <canvas id="an_gauge_1" data-major-ticks="0 100 200 300 400 500 600 700 800 900 1000 1023" data-type="canv-gauge" data-min-value="0" data-max-value="1023" data-onready="setInterval( function() { Gauge.Collection.get('an_gauge_1').setValue(data_val);}, 200);"></canvas>
    </body>
</html>

Poznámka:

Na uvedenom výpise je neúplný obsah HTML stránky. Slúži na vysvetlenie činnosti kódu a tak neobsahuje definíciu merača. Úplný HTML kód si môžete stiahnuť zo servera redakcie.

Všimnime si, že sa jedná o klasický HTML kód, ktorý obsahuje hlavičku (párový tag <head>) a telo (párový tag <body>). Hlavička však navyše obsahuje novú sekciu <script>. Tu sa nachádza definícia funkcie AJAXu (JavaScriptu) s názvom GETArduinoInputs(). Pravidelné volanie tejto funkcie je zabezpečené v tele stránky tagom <body onload="GetArduinoInputs()">. Keď je táto funkcia JavaScriptu zavolaná, pošle Arduinu http požiadavku GET s textom ajax_inputs. Arduino príjme túto požiadavku a prehliadaču vráti hlavičku s hodnotou analógového portu. Keď JavaScript v prehliadači príjme túto odpoveď, spustí funkciu request.onreadystatechange = function(). Táto funkcia sa spustí zakaždým, ako Arduino zašle odpoveď prehliadaču. Jej úlohou je pomocou príkazu document.getElementById("input3").innerHTML   zmeniť  hodnotu analógového portu namiesto textu „input3“.

HTML kód potom tagom <p>Analógový vstup (A2): <span id="input3">...</span></p>  prepíše na obrazovke prehliadača text „input3“skutočnou hodnotou analógového vstupu. Takto sa prepisuje iba časť kódu HTML stránky a preto nie je nutné načítať a zobraziť celú stránku znova.

Toto využijeme pri vykresľovaní grafu na obrazovke. Použijeme na to prvok Gauge (merač), ktorý je dostupný pomocou tagu <canvas> od verzie 5 jazyka HTML.

Definícia prvku Gauge je vložená práve v sekcii <script> a na výpise nie je uvedená (ale ak si stiahnete kompletný kód stránky index.htm a pozriete sa na miesto vo výpise označené červeným textom, uvidíte definíciu prvku Gauge). Na jeho zobrazenie použijeme tag <canvas> (plátno), ktorý sa zapíše do sekcie <body>. 

Aktualizácia merača

JavaScript analógovú hodnotu, ktorú pošle Arduino prehliadaču, uloží do premennej data_val. Na to slúži príkaz JavaScriptu data_val = this.responseXML.getElementsByTagName('analog')[0].childNodes[0].nodeValue;

Potom sa hodnota uložená v data_val v merači zobrazí týmito parametrami v tagu <canvas>:

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

Hodnota 200 na konci parametra značí, že sa merač bude aktualizovať každých 200 milisekúnd. Aby sa hodnoty korektne zobrazovali, je potrebné načasovať aj pravidelnú požiadavku AJAXu každých 200 milisekúnd. To zabezpečí príkaz JavaScriptu na v kóde HTML stránky:
setTimeout('GetArduinoInputs()', 200);
V prípade, že sieť je veľmi pomalá alebo preťažená, skúsme pozmeniť aktualizáciu na hodnotu 1000, čo značí refreš údajov každú sekundu. Hodnotu zvážime podľa potreby aplikácie, niektoré údaje ako teplota okolia či tlak stačí refrešovať aj každých 5 minút. 

Skeč Arduina

Zatiaľ sme sa venovali kódu HTML stránky index.htm. Ale ako to funguje na strane Arduina? Odpoveď nájdeme v skeči na výpise č.2:

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>
 
// velkost bufferu pre požiadavky
#define REQ_BUF_SZ   20
 
 
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 100, 203); // IP adresa, zmeniť podľa seba!!!
EthernetServer server(80);  // server na port 80
File webFile;
char HTTP_req[REQ_BUF_SZ] = {0}; // bufferovaná požiadavka
char req_index = 0;              // index na HTTP_req buffer
 
void setup()
{
    // vypnutie Ethernet čipu
    pinMode(10, OUTPUT);
    digitalWrite(10, HIGH);
   
    Serial.begin(9600);       // zapnutie sériového portu pre ladenie
   
    // inicializácia SD karty
    Serial.println("Inicializacia SD karty...");
    if (!SD.begin(4)) {
        Serial.println("Chyba - SD karta nie je inicializovana");
        return;   
    }
    Serial.println("OK - SD karta inicializovana");
    // hľadáme súbor index.htm
    if (!SD.exists("index.htm")) {
        Serial.println("Chyba - nemozem najst subor index.htm");
        return;  // návrat, ak nemôže nájsť súbor index.htm
    }
    Serial.println("OK - subor index.htm najdeny.");
   
    Ethernet.begin(mac, ip); 
    server.begin();          
}
 
 
void loop()
{
    EthernetClient client = server.available(); 
 
    if (client) { 
        boolean prazdnyRiadok = true;
        while (client.connected()) {
            if (client.available()) {   // ak klient zaslal požiadavku
                char c = client.read(); // prečítaj 1 bajt od klienta
                // do bufferu vloží HTTP požiadavku
               
                if (req_index < (REQ_BUF_SZ - 1)) {
                    HTTP_req[req_index] = c;          // ulož znak HTTP požiadavky
                    req_index++;
                }
                // vypíš HTTP požiadavku na serial monitor
                Serial.print(c);
                // posledný riadok klientskej požiadavky je prázdny riadok a končí znakom \n
 
                if (c == '\n' && prazdnyRiadok) {
                   
                    client.println("HTTP/1.1 200 OK");// zašle klasickú hlavičku
                    // táto časť hlavičky nižšie v závislosti na tom, či
                    // web stránka alebo XML stránka je požadovaná
                    // Ajax požiadavka - zaslanie XML súboru
                    if (StrContains(HTTP_req, "ajax_inputs")) {
                        // zaslanie zvyšku hlavičky
                        client.println("Content-Type: text/xml");
                        client.println("Connection: keep-alive");
                        client.println();
                        // odoslanie XML súboru obsahujúceho vstupné stavy
                        XML_response(client);
                    }
                    else {  // odoslanie klasickej hlavičky
                       
                        client.println("Content-Type: text/html");
                        client.println("Connection: keep-alive");
                        client.println();
                       
                        webFile = SD.open("index.htm");        // otvor súbor index.htm
                        if (webFile) {
                            while(webFile.available()) {
                                client.write(webFile.read()); // zašli stránku klientovi
                            }
                            webFile.close();
                        }
                    }
                    // vypíš prijatú HTTP požiadavku na sériový port
                    Serial.print(HTTP_req);
                    // vymaž index bufferu a obsah bufferu nastav na nulu
                    req_index = 0;
                    StrClear(HTTP_req, REQ_BUF_SZ);
                    break;
                }
                // každý riadok textu prijatý od klienta končí znakmi \r\n
                if (c == '\n') {
                    // posledný znak na riadku prijatého textu
                  
                    prazdnyRiadok = true;
                }
                else if (c != '\r') {
                   
                    prazdnyRiadok = false;
                }
            } // koniec if (client.available())
        } // koniec if (client.available())
        delay(1);      // dajme čas klientovi prijať dáta
        client.stop(); // ukonči spojenie s klientom
    } // koniec if (client)
}
 
// zaslanie XML súboru obsahujúceho analógové hodnoty
void XML_response(EthernetClient cl)
{
    int analog_val;
   
    cl.print("<?xml version = \"1.0\" ?>");
    cl.print("<inputs>");
    // čítaj analóg pin A2
    analog_val = analogRead(2);
    cl.print("<analog>");
    cl.print(analog_val);
    cl.print("</analog>");
    cl.print("</inputs>");
}
 
// nastavuje každý element reťazca na nulu (vymaže pole)
void StrClear(char *str, char length)
{
    for (int i = 0; i < length; i++) {
        str[i] = 0;
    }
}
 
// hľadá znaky sfind v reťazci str
// vráti hodnotu 1 ak znaky našiel
// vráti hodnotu 0 ak znaky nenašiel
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;
}

My už značnú časť kódu skeče poznáme. Novou pre nás je sekcia označená červenou farbou, ktorá hovorí – ak sa v požiadavke nachádza text „ajax_inputs“, tak odošli časť hlavičky a zavolaj funkciu XML_response(). Tá je vo výpise zvýraznená modrou farbou. Jej úlohou nie je nič iné, ako prečítať hodnotu na vstupe analógového pinu A2 Arduina a vytvoriť XML kód tak, ako sme si to vysvetlili v teoretickej časti. Takto vytvorený XML kód bude Arduinom odoslaný HTML stránke, kde JavaScript uloží hodnotu do premennej data_val a pozmení text input3.

Skeč obsahuje rutiny na ladenie, ktoré vypisujú hlásenia na sériový port. Tie po odladení činnosti môžeme spokojne vymazať, čím ušetríme miesto v pamäti Arduina.

Skeč spolu s HTML stránkou si stiahneme zo serveru redakcie. HTML stránku nahráme pod názvom index.htm na SD kartu, skeč vložíme do IDE prostredia Arduina, preložíme a nahráme do Arduina. Zasunieme ethernet shield, na prehliadači nastavíme správnu IP adresu a na obrazovke uvidíme grafický merač podobný tomu na obrázku č.2.

Za domácu úlohu sa pohrajme s parametrami tagu <canvas>, budeme prekvapení, čo všetko sa dá nastaviť a ako sa zmení výzor merača. Nabudúce si ukážeme, ako vytvoriť dva merače, ktoré budú ukazovať už reálne hodnoty teploty a tlaku.

Zobrazit Galériu

Miroslav Oravec

Všetky autorove články

Pridať komentár