Nesse vídeo mostro como incluir imagens, javascript, e outros tipos de arquivos e mídias na sua aplicação web baseada no Ethernet Shield W5100
31/10/2016 - Observação
Erro de login
Várias pessoas têm me relatado problemas no login, e eu não havia conseguido achar o problema, mas o Rodrigo (nos comentários abaixo) encontrou o problema e postou a solução. Vou manter o código original, e se quem tiver erros, siga o que o Rodrigo recomendou:
//Descobri o problema e mudei o meu código. //A função base64_encode não retorna corretamente o primeiro byte codificado, //por isso a função validar_usuario sempre recebia a string "out" errada. //Tive que trocar a função base64_encode para uma que retornasse o valor //inteiro de "out" codificado. Dai minha função validar_usuario ficou assim: boolean validar_usuario(char * linebuf) { char usuario_senha[] = "admin:admin"; //"admin:admin"; byte t = strlen(usuario_senha); int tamanhoEnc = (((t-1) / 3) + 1) * 4; //tamanho da string codificada char *out = base64_encode(usuario_senha, t); char out2[tamanhoEnc]; for (t=0; t<(tamanhoEnc); t++) { out2[t] = linebuf[21+t]; } return (strstr(out2, out)>0); }
Entenda o Mime Types:
https://pt.wikipedia.org/wiki/MIME
Lista de todos (acredito eu) os Mime Types de Arquivos:
http://www.sitepoint.com/web-foundations/mime-types-complete-list/
Para rodar a aplicação, copie os seguintes arquivos para dentro do SDCard
atmel.pdf
Qualquer arquivo PDF chamado atmel.pdf (cuidado com o tamanho do arquivo)
favicon.ico
qualquer arquivo do tipo ico, chamado favicon.ico
uno.jpg
qualquer imagem do tipo jpg, chamada uno.jpg
os demais arquivos, devem ser conforme mostrados abaixo
HTML - index.htm
<!DOCTYPE html> <html> <head> <link rel="stylesheet" type="text/css" href="/css.css"> <script src="/js.js"></script> </head> <body onload="init();"> <h1>ARDUINO - ETHERNET SHIELD + SDCARD + ARQUIVOS</h1> <br> <img src="/uno.jpg" /> <br> <a href="/atmel.pdf">Download datasheet</a> | <a href='/logoff'>Logoff</a> <br> </body> </html>
Javascript - js.js
function init(){ window.alert("hello world!"); }
CSS - css.css
body { background-color: #FAEBD7; } h1 { margin-left: 40px; }
Sketch - código-fonte:
/* Web Server - HTTP Autentication Baseado na versão de exemplo do Arduino. - exemplo que mostra como incluir html, css, javascript e imagem em arquivos no sdcard e também como fazer download de um arquivo pdf do sdcard. by Fabiano A. Arndt (fabianoallex) */ /********************************************************************************** ************************************BIBLIOTECAS************************************ **********************************************************************************/ #include <SPI.h> #include <Ethernet.h> #include <SD.h> /********************************************************************************** ********************************FIM BIBLIOTECAS************************************ **********************************************************************************/ /********************************************************************************** *************************ROTINAS USUARIO E SENHA*********************************** ***********************************************************************************/ boolean validar_usuario(char * linebuf) { /* nesse exemplo o usuario e senha estão definidos dentro do código fonte. mas o usuário e senha poderiam ser autenticados de diversas maneiras, lendo os dados de um servidor web, arquivo texto, etc, bastando apenas atribuir o valor lido para a variável usuario_senha. */ char usuario_senha[] = "admin:admin"; //usuario e senha para acessar a pagina int t = strlen(usuario_senha); int tamanhoEnc = (((t-1) / 3) + 1) * 4; //tamanho da string codificada char out[tamanhoEnc]; base64_encode(out, usuario_senha, t+1 ); //---desconta é usado pra eliminar os caracteres '=' int desconta = 0; if ((t%3) == 1) { desconta = 2; } if ((t%3) == 2) { desconta = 1; } char out2[tamanhoEnc-desconta]; byte i; for (i=0; i<(tamanhoEnc-desconta);i++){ out2[i] = out[i]; } out2[i] = '\0'; return ( strstr(linebuf, out2)>0 ); } /********************************************************************************** *************************FIM ROTINA USUARIO E SENHA******************************** ***********************************************************************************/ /**************************************************************************************** ****************************SD CARD INICIO*********************************************** ****************************************************************************************/ #define PIN_SD_CARD 4 boolean iniciar_sd_card() { pinMode(10, OUTPUT); digitalWrite(10, HIGH); if (!SD.begin(PIN_SD_CARD)) { return false; } return true; } /**************************************************************************************** ****************************SD CARD FIM************************************************** ****************************************************************************************/ /********************************************************************************** ***********************************PAGINAS HTML************************************ ***********************************************************************************/ EthernetServer * server; void write_from_file(EthernetClient &client, char * filename){ File webFile = SD.open(filename); if (webFile) { while(webFile.available()) { client.write(webFile.read()); } webFile.close(); } else { Serial.print("Erro SD CARD: "); Serial.println(filename); } } void iniciar_ethernet_01(){ byte ip[4] = {192,168,200,25}; //definir aqui o ip byte gateway[4] = {192,168,200,254}; byte subnet[4] = {255,255,255,0}; byte mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; int porta = 80; server = new EthernetServer(porta); Ethernet.begin(mac, ip, gateway, subnet); //caso necessario gateway utilizar essa linha server->begin(); } void iniciar_ethernet_02(){ byte ip[4] = {192,168,200,25}; //definir aqui o ip byte mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; int porta = 80; server = new EthernetServer(porta); Ethernet.begin(mac, ip); server->begin(); } void iniciar_ethernet() { //escolher apenas um dos dois modos de iniciar o ethernet shield iniciar_ethernet_01(); //inicia com ip, gateway, mascara e porta //iniciar_ethernet_02(); //inicia só com ip e porta } void html_cab_200_ok(EthernetClient &client){ client.println(F("HTTP/1.1 200 OK\n" "Content-Type: text/html\n" "Connection: keep-alive\n\n")); } void html_logoff(EthernetClient &client){ client.print(F( "HTTP/1.1 401 Authorization Required\n" "Content-Type: text/html\n" "Connnection: close\n\n" "<!DOCTYPE HTML>\n" "<html><head><title>Logoff</title>\n" "<script>document.execCommand('ClearAuthenticationCache', 'false');</script>" //IE logoff "<script>try {" //mozila logoff " var agt=navigator.userAgent.toLowerCase();" " if (agt.indexOf(\"msie\") != -1) { document.execCommand(\"ClearAuthenticationCache\"); }" " else {" " var xmlhttp = createXMLObject();" " xmlhttp.open(\"GET\",\"URL\",true,\"logout\",\"logout\");" " xmlhttp.send(\"\");" " xmlhttp.abort();" " }" " } catch(e) {" " alert(\"erro ao fazer logoff\");" " }" "function createXMLObject() {" " try {if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();} else if (window.ActiveXObject) {xmlhttp=new ActiveXObject(\"Microsoft.XMLHTTP\");}} catch (e) {xmlhttp=false;}" " return xmlhttp;" "}</script>" "</head><body><h1>Voce nao esta mais conectado</h1></body></html>\n")); } void html_autenticar(EthernetClient &client) { client.print(F("HTTP/1.1 401 Authorization Required\n" "WWW-Authenticate: Basic realm=\"Area Restrita\"\n" "Content-Type: text/html\n" "Connnection: close\n\n" "<!DOCTYPE HTML>\n" "<html><head><title>Error</title>\n" "</head><body><h1>401 Acesso nao autorizado</h1></body></html>\n")); } void html_autenticado(EthernetClient &client, char * filename){ client.println(F("HTTP/1.1 200 OK\n" "Content-Type: text/html\n" "Connection: keep-alive\n")); write_from_file(client, filename); } void js_file(EthernetClient &client, char * filename){ client.println(F("HTTP/1.1 200 OK\n" "Content-Type: text/javascript\n" "Connection: keep-alive\n")); write_from_file(client, filename); } void css_file(EthernetClient &client, char * filename){ client.println(F("HTTP/1.1 200 OK\n" "Content-Type: text/css\n" "Connection: keep-alive\n")); write_from_file(client, filename); } void favicon_file(EthernetClient &client, char * filename){ client.println(F("HTTP/1.1 200 OK\n" "Content-Type: image/x-icon\n" "Connection: keep-alive\n")); write_from_file(client, filename); } void pdf_file_download(EthernetClient &client, char * filename){ client.println(F("HTTP/1.1 200 OK\n" "Content-Type: application/download\n" "Connection: close\n")); write_from_file(client, filename); } void jpg_file(EthernetClient &client, char * filename){ client.println(F("HTTP/1.1 200 OK\n" "Content-Type: file/jpg\n" "Connection: close\n")); write_from_file(client, filename); } void exec_ethernet() { EthernetClient client = server->available(); if (client) { char linebuf[80]; memset(linebuf, 0, sizeof(linebuf)); int charCount = 0; boolean autenticado = false; boolean currentLineIsBlank = true; boolean logoff = false; boolean indCss = false; boolean indJs = false; boolean indPdfDataSheet = false; boolean indJpgUno = false; boolean indFavicon = false; while (client.connected()) { if (client.available()) { char c = client.read(); linebuf[charCount] = c; if (charCount<sizeof(linebuf)-1) { charCount++; } Serial.write(c); if (c == '\n' && currentLineIsBlank) { if (autenticado && !logoff ) { if (indJs){ js_file(client, "js.js"); //js file } else if(indCss){ css_file(client, "css.css"); //css file } else if(indPdfDataSheet){ pdf_file_download(client, "atmel.pdf"); //datasheet download } else if(indJpgUno){ jpg_file(client, "uno.jpg"); //jpg file } else if(indFavicon){ jpg_file(client, "favicon.ico"); //icone do browser } else { html_autenticado(client, "index.htm"); //página inicial } } else { logoff ? html_logoff(client) : html_autenticar(client); } break; } if (c == '\n') { currentLineIsBlank = true; if (strstr(linebuf, "GET /logoff" )>0 ) { logoff = true; } if (strstr(linebuf, "Authorization: Basic")>0 ) { if ( validar_usuario(linebuf) ) { autenticado = true; } } if (strstr(linebuf, "GET /css.css" )>0 ) { indCss = true; } if (strstr(linebuf, "GET /js.js" )>0 ) { indJs = true; } if (strstr(linebuf, "GET /atmel.pdf" )>0 ) { indPdfDataSheet = true; } if (strstr(linebuf, "GET /uno.jpg" )>0 ) { indJpgUno = true; } if (strstr(linebuf, "GET /favicon.ico" )>0 ) { indFavicon = true; } memset(linebuf, 0, sizeof(linebuf)); charCount = 0; } else if (c != '\r') { currentLineIsBlank = false; // you've gotten a character on the current line } } } delay(1); client.stop(); } } /********************************************************************************** **************************************** FIM PAGINAS HTML************************** ***********************************************************************************/ /********************************************************************************** *************************BASE 64 CODE/DECODE DO USUARIO E SENHA******************** ***********************************************************************************/ void a3_to_a4(unsigned char * a4, unsigned char * a3); void a4_to_a3(unsigned char * a3, unsigned char * a4); unsigned char b64_lookup(char c); int base64_encode(char *output, char *input, int inputLen) { const char b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int i=0, j=0, encLen=0; unsigned char a3[3], a4[4]; while(inputLen--) { a3[i++] = *(input++); if(i == 3) { a3_to_a4(a4, a3); for(i=0; i<4; i++) { output[encLen++] = b64_alphabet[a4[i]]; } i = 0; } } if (i) { for(j = i; j < 3; j++) { a3[j] = '\0'; } a3_to_a4(a4, a3); for(j = 0; j < i + 1; j++) { output[encLen++] = b64_alphabet[a4[j]]; } while((i++ < 3)) { output[encLen++] = '='; } } output[encLen] = '\0'; return encLen; } /* int base64_decode(char * output, char * input, int inputLen) { int i = 0, j = 0; int decLen = 0; unsigned char a3[3]; unsigned char a4[4]; while (inputLen--) { if (*input == '=') { break; } a4[i++] = *(input++); if (i == 4) { for (i = 0; i <4; i++) { a4[i] = b64_lookup(a4[i]); } a4_to_a3(a3,a4); for (i = 0; i < 3; i++) { output[decLen++] = a3[i]; } i = 0; } } if (i) { for (j=i; j<4; j++) { a4[j] = '\0'; } for (j=0; j<4; j++) { a4[j] = b64_lookup(a4[j]);} a4_to_a3(a3,a4); for (j=0; j<i-1; j++) { output[decLen++] = a3[j]; } } output[decLen] = '\0'; return decLen; } */ //int base64_enc_len(int plainLen) { // int n = plainLen; // return (n + 2 - ((n + 2) % 3)) / 3 * 4; //} //int base64_dec_len(char * input, int inputLen) { // int i = 0; // int numEq = 0; // for(i = inputLen - 1; input[i] == '='; i--) { numEq++; } // return ((6 * inputLen) / 8) - numEq; //} void a3_to_a4(unsigned char * a4, unsigned char * a3) { a4[0] = (a3[0] & 0xfc) >> 2; a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); a4[3] = (a3[2] & 0x3f); } //void a4_to_a3(unsigned char * a3, unsigned char * a4) { // a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); // a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); // a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; //} unsigned char b64_lookup(char c) { if(c >='A' && c <='Z') return c - 'A'; if(c >='a' && c <='z') return c - 71; if(c >='0' && c <='9') return c + 4; if(c == '+') return 62; if(c == '/') return 63; return -1; } /********************************************************************************** *************************FIM BASE 64 CODE/DECODE DO USUARIO E SENHA**************** ***********************************************************************************/ /********************************************************************************** **************************************** VOID / LOOP ****************************** ***********************************************************************************/ void setup() { Serial.begin(9600); iniciar_sd_card(); iniciar_ethernet(); } void loop() { exec_ethernet(); } /********************************************************************************** *************************************FIM VOID / LOOP******************************* ***********************************************************************************/