Zde můžete vidět rozdíly mezi vybranou verzí a aktuální verzí dané stránky.
| Obě strany předchozí revize Předchozí verze Následující verze | Předchozí verze | ||
|
2014:mbed-http [2015/01/19 04:27] Maximilián Tydor [Webová stránka a periferie] |
2014:mbed-http [2015/01/19 06:02] (aktuální) Maximilián Tydor [vykreslení webové stránky] |
||
|---|---|---|---|
| Řádek 33: | Řádek 33: | ||
| ===== REALIZACE ===== | ===== REALIZACE ===== | ||
| - | Základem byl výše zmíněný projekt. Důležité bylo **neaktualizovat** žádnou knihovnu, jinak se aplikace kompletně rozpadla. | + | Postup prací: |
| + | - Importovat dříve zmíněný projekt a **neaktualizovat** žádnou knihovnu! (jinak se aplikace kompletně rozpadne) | ||
| + | - Odebrat obsluhu SD karty (nebylo už možné tímto strácet čas) | ||
| + | - Implementovat HTTP autorizaci | ||
| + | - Implementovat POST metodu | ||
| + | - Nakonfigurovat periferie | ||
| + | - Vytovřit webovou stránku a obslužné metody | ||
| + | |||
| + | ==== HTTP Autorizace ==== | ||
| + | Vytvořený skript hledá v HTTP hlavičce odeslané klientem tento řádek: //"Authorization: Basic TVBPQToyMDE0"//, | ||
| + | co je zahashovaná kombinace uživatelského jména //"MPOA"// a hesla //"2014"//. | ||
| + | Pokud tuto kombinaci nenajde, odešle klientovi zprávu //"401 Authorization Required"//. | ||
| + | Prohlížeč pak vyzve uživatele k zadání přihlašovacích údajů jednoduchým formulářem. | ||
| + | |||
| + | {{ 2014:mbed-http:auth.png |Přihlašovací dialog}} | ||
| + | |||
| + | Pokud uživatel dialog zruší, daleko se nedostane... | ||
| + | |||
| + | {{ 2014:mbed-http:unauthorized.png |Chybová stránka v případě zrušení přihlašovacího dialogu}} | ||
| + | |||
| + | Pokouší-li se užívatel odklepnout formulář prázdný nebo s nesprávnými údaji vyskakuje okno pořád dokola. | ||
| + | |||
| + | <code c> | ||
| + | if(!strstr(buffer, "Authorization: Basic TVBPQToyMDE0")){ //MPOA 2014 | ||
| + | sprintf(httpHeader,"HTTP/1.1 401 Authorization Required \r\nContent-Type: text\r\nWWW-Authenticate: Basic realm='Login required!'\r\n\r\n"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); //Odeslání výzvy k přihlášení | ||
| + | sprintf(httpHeader, "<HTML>\r\n\t<HEAD>\r\n\t\t<TITLE>Error</TITLE>\r\n\t</HEAD>\r\n\t<BODY>\r\n\t\t<H1>401 Unauthorised.</H1>\r\n\t</BODY>\r\n</HTML>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); //Formát stránky s chybovým hlášením | ||
| + | client.close(); | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | ==== metoda POST ==== | ||
| + | Přesto že předat těch několik parametrů není problém skrze URL, tedy metodou GET, ale výsledek není zrovna lahodivý oku, | ||
| + | proto byla implementována podpora metody POST. Obsluha je ve výsledku velice jednoduchá (ale ladění zabralo jeden celý den). | ||
| + | Data se posílají dvěma pakety (i když i přes WireShark to lze jen těžko poznat). První obsahuje identifikační hlavičku s identifikátorem metody, prohlížeče klienta, autorizačních ůdajů apod. | ||
| + | V druhém paketu se nacházejí samotná data a je nutné jej příjmout až v samotném průběhu zpracování metody POST, | ||
| + | jinak by do toho kecal autorizační algoritmus a ani selektor metod by si s tím nevěděl rady. | ||
| + | |||
| + | <code c> | ||
| + | } //konec metody GET | ||
| + | else if (!strncmp(buffer, "POST ", 5)) { //ověření, že se jedná o POST data | ||
| + | int n = client.receive(buffer, sizeof(buffer)); //příjem dalšího paketu se samotnými daty | ||
| + | ... //následuje zpracování přijatých dat | ||
| + | </code> | ||
| + | |||
| + | ==== vykreslení webové stránky ==== | ||
| + | Dekódování přijatých dat je věc hraní si s řetězci a následnými převody, ale toho je internet plný a o radu v případě problému není nouze. | ||
| + | Zde tedy pár řádku k nahlédnutí jak vypadá generátor webové stránky uvnitř procesoru. | ||
| + | |||
| + | <code c> | ||
| + | void display_page(void){ | ||
| + | sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"<html>\r\n\t<head>\r\n\t\t<title>K64F HTTP RGB controller</title>\r\n\t\t<meta http-equiv='refresh' content='5'>\r\n\t</head>\r\n\t<body>\r\n\t\t<h1>K64F RGB on-line controller</h1>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t<table cellspacing='5px'>\r\n\t\t\t<tr>\r\n\t\t\t\t<th colspan='2'>RGB controls & status</th>\r\n\t\t\t\t<th>SW2 status</th>\r\n\t\t\t\t<th>SW3 status</th>\r\n\t\t\t</tr>\r\n\t\t\t<tr>\r\n\t\t\t\t<td><form action='' method='post'>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t\t<label for='R'>R:\t</label><input type='checkbox' name='R' id='R'"); | ||
| + | if(r_on) | ||
| + | strcat(httpHeader, " checked='checked'><br>"); | ||
| + | else | ||
| + | strcat(httpHeader, "><br>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t\t<label for='G'>G:\t</label><input type='checkbox' name='G' id='G'"); | ||
| + | if(g_on) | ||
| + | strcat(httpHeader, " checked='checked'><br>"); | ||
| + | else | ||
| + | strcat(httpHeader, "><br>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t\t<label for='B'>B:\t</label><input type='checkbox' name='B' id='B'"); | ||
| + | if(b_on) | ||
| + | strcat(httpHeader, " checked='checked'><br>"); | ||
| + | else | ||
| + | strcat(httpHeader, "><br>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t\t<input type='submit' value='Save'>\r\n\t\t\t\t</form></td>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t<td style='vertical-align: bottom;'><div style='background: #%02x%02x%02x; width: 80px; height: 95%; border: 2px solid;'></div></td>",r_col, g_col, b_col); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t<td style='vertical-align: bottom;'><div style='background: black; width: 84px; height: %dpx;'></div></td>",s2_col); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t<td style='vertical-align: bottom;'><div style='background: black; width: 84px; height: %dpx;'></div></td>",s3_col); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t</tr>\r\n\t\t</table>\r\n\t</body>\r\n</html>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | a zde výsledek, který už umí přechroustat snad každý prohlížeč: | ||
| + | |||
| + | <code html> | ||
| + | <html> | ||
| + | <head> | ||
| + | <title>K64F HTTP RGB controller</title> | ||
| + | <meta http-equiv='refresh' content='5'> | ||
| + | </head> | ||
| + | <body> | ||
| + | <h1>K64F RGB on-line controller</h1> | ||
| + | <table cellspacing='5px'> | ||
| + | <tr> | ||
| + | <th colspan='2'>RGB controls & status</th> | ||
| + | <th>SW2 status</th> | ||
| + | <th>SW3 status</th> | ||
| + | </tr> | ||
| + | <tr> | ||
| + | <td><form action='' method='post'> | ||
| + | <label for='R'>R: </label><input type='checkbox' name='R' id='R' checked='checked'><br> | ||
| + | <label for='G'>G: </label><input type='checkbox' name='G' id='G' checked='checked'><br> | ||
| + | <label for='B'>B: </label><input type='checkbox' name='B' id='B'><br> | ||
| + | <input type='submit' value='Save'> | ||
| + | </form></td> | ||
| + | <td style='vertical-align: bottom;'><div style='background: #ffff00; width: 80px; height: 95; border: 2px solid;'></div></td> | ||
| + | <td style='vertical-align: bottom;'><div style='background: black; width: 84px; height: 30px;'></div></td> | ||
| + | <td style='vertical-align: bottom;'><div style='background: black; width: 84px; height: 70px;'></div></td> | ||
| + | </tr> | ||
| + | </table> | ||
| + | </body> | ||
| + | </html> | ||
| + | </code> | ||
| + | |||
| + | a zde ještě výsledek, který je mezi lidmi oblíbenější: | ||
| + | |||
| + | {{ 2014:mbed-http:webpage.png |Obrázek výsledné webové stránky}} | ||
| + | |||
| + | Přes tuto stránku je možné zapínat a vypínat jednotlivé barvy RGB LED a hned vidět i výsledek bez nutnosti být zrovna u desky. | ||
| + | Výsledná barevná kombinace je spočítaná ze všech tří složek a kdyby se aplikace poupravila tak, že samostatné vlákno by simulovalo PWM modulaci, | ||
| + | pořád by přibližně odpovídala barva na webové stránce barvě na LED. | ||
| + | Je to možné velice snadno ověřit na [[http://www.quackit.com/css/css_color_codes.cfm|paletě webových barev]]. | ||
| + | Na stránce se dále zobrazují dva jakoby bargrafy simulující klasické tlačítka (v horní poloze neaktivní). | ||
| + | Pro větší pohodlí a uživatelský komfort se stránka sama obnovuje každých 5 sekund. | ||
| + | ------- | ||
| + | |||
| + | ===== PŘÍLOHY ===== | ||
| + | |||
| + | ==== Zdrojový kód main.cpp ==== | ||
| + | |||
| + | <code c> | ||
| + | #include "mbed.h" | ||
| + | #include "EthernetInterface.h" | ||
| + | #include "SDFileSystem.h" | ||
| + | #include <stdio.h> | ||
| + | #include <string.h> | ||
| + | |||
| + | #define HTTPD_SERVER_PORT 80 | ||
| + | #define HTTPD_MAX_REQ_LENGTH 1023 | ||
| + | #define HTTPD_MAX_HDR_LENGTH 255 | ||
| + | |||
| + | Serial uart(USBTX, USBRX); | ||
| + | |||
| + | EthernetInterface eth; | ||
| + | TCPSocketServer server; | ||
| + | TCPSocketConnection client; | ||
| + | |||
| + | char buffer[HTTPD_MAX_REQ_LENGTH+1]; | ||
| + | char httpHeader[HTTPD_MAX_HDR_LENGTH+1]; | ||
| + | |||
| + | char *uristr; | ||
| + | char *eou; | ||
| + | char *qrystr; | ||
| + | |||
| + | int r_on=0, g_on=0, b_on=0; | ||
| + | int r_col=0, g_col=0, b_col=0, s2_col=80, s3_col=80; | ||
| + | |||
| + | void display_page(void){ | ||
| + | |||
| + | sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"<html>\r\n\t<head>\r\n\t\t<title>K64F HTTP RGB controller</title>\r\n\t\t<meta http-equiv='refresh' content='5'>\r\n\t</head>\r\n\t<body>\r\n\t\t<h1>K64F RGB on-line controller</h1>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t<table cellspacing='5px'>\r\n\t\t\t<tr>\r\n\t\t\t\t<th colspan='2'>RGB controls & status</th>\r\n\t\t\t\t<th>SW2 status</th>\r\n\t\t\t\t<th>SW3 status</th>\r\n\t\t\t</tr>\r\n\t\t\t<tr>\r\n\t\t\t\t<td><form action='' method='post'>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t\t<label for='R'>R:\t</label><input type='checkbox' name='R' id='R'"); | ||
| + | if(r_on) | ||
| + | strcat(httpHeader, " checked='checked'><br>"); | ||
| + | else | ||
| + | strcat(httpHeader, "><br>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t\t<label for='G'>G:\t</label><input type='checkbox' name='G' id='G'"); | ||
| + | if(g_on) | ||
| + | strcat(httpHeader, " checked='checked'><br>"); | ||
| + | else | ||
| + | strcat(httpHeader, "><br>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t\t<label for='B'>B:\t</label><input type='checkbox' name='B' id='B'"); | ||
| + | if(b_on) | ||
| + | strcat(httpHeader, " checked='checked'><br>"); | ||
| + | else | ||
| + | strcat(httpHeader, "><br>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t\t<input type='submit' value='Save'>\r\n\t\t\t\t</form></td>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t<td style='vertical-align: bottom;'><div style='background: #%02x%02x%02x; width: 80px; height: 95%; border: 2px solid;'></div></td>",r_col, g_col, b_col); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t<td style='vertical-align: bottom;'><div style='background: black; width: 84px; height: %dpx;'></div></td>",s2_col); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t\t<td style='vertical-align: bottom;'><div style='background: black; width: 84px; height: %dpx;'></div></td>",s3_col); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader,"\r\n\t\t\t</tr>\r\n\t\t</table>\r\n\t</body>\r\n</html>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | |||
| + | } | ||
| + | |||
| + | int main (void) | ||
| + | { | ||
| + | // RGB LED outputs | ||
| + | DigitalOut rled(LED_RED, 1); | ||
| + | DigitalOut gled(LED_GREEN, 1); | ||
| + | DigitalOut bled(LED_BLUE, 1); | ||
| + | |||
| + | // SW2 & SW3 inputs | ||
| + | DigitalIn sw2(PTC6); | ||
| + | DigitalIn sw3(PTA4); | ||
| + | |||
| + | // Serial Interface eth; | ||
| + | uart.baud(115200); | ||
| + | uart.printf("Initializing\n"); | ||
| + | |||
| + | // EthernetInterface eth; | ||
| + | uart.printf("Initializing Ethernet\n"); | ||
| + | eth.init(); //Use DHCP | ||
| + | uart.printf("Connecting\n"); | ||
| + | eth.connect(); | ||
| + | uart.printf("IP Address is %s\n", eth.getIPAddress()); | ||
| + | |||
| + | // TCPSocketServer server; | ||
| + | server.bind(HTTPD_SERVER_PORT); | ||
| + | server.listen(); | ||
| + | uart.printf("Server Listening\n"); | ||
| + | |||
| + | while (true) { | ||
| + | uart.printf("\nWaiting for new connection...\r\n"); | ||
| + | server.accept(client); | ||
| + | client.set_blocking(false, 1500); // Timeout after (1.5)s | ||
| + | |||
| + | if(r_on){ //zadost o rozsviceni cervene LED | ||
| + | rled=0; //led aktivni logickou 0 | ||
| + | } | ||
| + | else{ | ||
| + | rled=1; | ||
| + | } | ||
| + | |||
| + | if(g_on){ //zadost o rozsviceni zelene LED | ||
| + | gled=0; //led aktivni logickou 0 | ||
| + | } | ||
| + | else{ | ||
| + | gled=1; | ||
| + | } | ||
| + | |||
| + | if(b_on){ //zadost o rozsviceni modre LED | ||
| + | bled=0; //led aktivni logickou 0 | ||
| + | } | ||
| + | else{ | ||
| + | bled=1; | ||
| + | } | ||
| + | |||
| + | if(sw2){ //nacteni stavu tlacitka SW2 | ||
| + | s2_col = 70; //pokud neni tlacitko zmacknute zobrazi se vysoky sloupecek | ||
| + | } | ||
| + | else{ | ||
| + | s2_col = 30; //pokud je tlacitko zmacknute zobrazi se nizky sloupecek | ||
| + | } | ||
| + | if(sw3){ | ||
| + | s3_col = 70; | ||
| + | } | ||
| + | else{ | ||
| + | s3_col = 30; | ||
| + | } | ||
| + | |||
| + | |||
| + | uart.printf("Connection from: %s\r\n", client.get_address()); | ||
| + | while (true) { | ||
| + | |||
| + | int n = client.receive(buffer, sizeof(buffer)); | ||
| + | if (n <= 0) break; | ||
| + | uart.printf("Recieved Data: %d\r\n\r\n%.*s\r\n",n,n,buffer); | ||
| + | if (n >= 1024) { | ||
| + | sprintf(httpHeader,"HTTP/1.1 413 Request Entity Too Large \r\nContent-Type: text\r\nConnection: Close\r\n\r\n"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | client.send(buffer,n); | ||
| + | break; | ||
| + | } else { | ||
| + | buffer[n]=0; | ||
| + | } | ||
| + | |||
| + | if(!strstr(buffer, "Authorization: Basic TVBPQToyMDE0")){ //MPOA 2014 | ||
| + | sprintf(httpHeader,"HTTP/1.1 401 Authorization Required \r\nContent-Type: text\r\nWWW-Authenticate: Basic realm='Login required!'\r\n\r\n"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | sprintf(httpHeader, "<HTML>\r\n\t<HEAD>\r\n\t\t<TITLE>Error</TITLE>\r\n\t</HEAD>\r\n\t<BODY>\r\n\t\t<H1>401 Unauthorised.</H1>\r\n\t</BODY>\r\n</HTML>"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | client.close(); | ||
| + | } | ||
| + | |||
| + | if (!strncmp(buffer, "GET ", 4)) { | ||
| + | uristr = buffer + 4; | ||
| + | eou = strstr(uristr, " "); | ||
| + | if (eou == NULL) { | ||
| + | sprintf(httpHeader,"HTTP/1.1 400 Bad Request \r\nContent-Type: text\r\nConnection: Close\r\n\r\n"); | ||
| + | client.send(httpHeader,strlen(httpHeader)); | ||
| + | client.send(buffer,n); | ||
| + | } | ||
| + | else { | ||
| + | *eou = 0; | ||
| + | display_page(); | ||
| + | client.close(); | ||
| + | } | ||
| + | } | ||
| + | else if (!strncmp(buffer, "POST ", 5)) { | ||
| + | |||
| + | int n = client.receive(buffer, sizeof(buffer)); | ||
| + | |||
| + | if (strstr(buffer, "R=on")) { | ||
| + | r_on = 1; | ||
| + | r_col = 255; | ||
| + | } | ||
| + | else{ | ||
| + | r_on = 0; | ||
| + | r_col = 0; | ||
| + | } | ||
| + | if (strstr(buffer, "G=on")) { | ||
| + | g_on = 1; | ||
| + | g_col = 255; | ||
| + | } | ||
| + | else{ | ||
| + | g_on = 0; | ||
| + | g_col = 0; | ||
| + | } | ||
| + | if (strstr(buffer, "B=on")) { | ||
| + | b_on = 1; | ||
| + | b_col = 255; | ||
| + | } | ||
| + | else{ | ||
| + | b_on = 0; | ||
| + | b_col = 0; | ||
| + | } | ||
| + | |||
| + | display_page(); | ||
| + | client.close(); | ||
| + | } | ||
| + | } | ||
| + | client.close(); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | </code> | ||
| + | |||
| + | ------- | ||