/* * USERS OF ARDUINO 0023 AND EARLIER: use the 'SDWebBrowse.pde' sketch... * 'SDWebBrowse.ino' can be ignored. * USERS OF ARDUINO 1.0 AND LATER: **DELETE** the 'SDWebBrowse.pde' sketch * and use ONLY the 'SDWebBrowse.ino' file. By default, BOTH files will * load when using the Sketchbook menu, and the .pde version will cause * compiler errors in 1.0. Delete the .pde, then load the sketch. * * I can't explain WHY this is necessary, but something among the various * libraries here appears to be wreaking inexplicable havoc with the * 'ARDUINO' definition, making the usual version test unusable (BOTH * cases evaluate as true). FML. */ /* * This sketch uses the microSD card slot on the Arduino Ethernet shield * to serve up files over a very minimal browsing interface * * Some code is from Bill Greiman's SdFatLib examples, some is from the * Arduino Ethernet WebServer example and the rest is from Limor Fried * (Adafruit) so its probably under GPL * * Tutorial is at http://www.ladyada.net/learn/arduino/ethfiles.html * Pull requests should go to http://github.com/adafruit/SDWebBrowse */ /* // Para que funcione y busque los caracteres acentuados, debe modificarse una librería. // SdBaseFile.cpp, linea 418 pone: // if (i > n || c < 0X21 || c > 0X7E)goto fail; // debe cambiuarse a: // if (i > n || c < 0X21)goto fail; */ #include #include #include #include // Las cadenas constantes se ponen en memoria de programa #define P_P(x,y) print_P(x,PSTR(y)); #define Pln_P(x,y) println_P(x,PSTR(y)); // Descomentar para no mostrar la versión #define VERSION "Alberto Seva WebServerSD.015m" // Desconectar para no mostrar los atributos en el listado de directorios // Se pueden añadir condiciones para no ver determinados tipos de ficheros #define ATRIBUTOS // Descomentar para ver los caracteres leidos en HEX para cada byte de la entrada // de directorio. //#define DEBUG_FileOpenClose //#define DEBUG_VerFiledirHex //#define DEBUG_VerDatosTarjeta //#define DEBUG_VerTreeTarjeta //#define DEBUG_VerConversionUTF_8 //#define DEBUG_VerCambiosPath //#define DEBUG_NombreLargo // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1284593828 /************ ETHERNET STUFF ************/ byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; byte ip[] = { 192, 168, 1, 14 }; //byte gateway[] = { 192, 168, 1, 1 }; //byte subnet[] = { 255, 255, 255, 0 }; EthernetServer server(80); /************ SDCARD STUFF ************/ Sd2Card card; SdVolume volume; SdFile root; SdFile dir; SdFile file; // Buffer multiusos: // - Para Transferencia de ficheros en bloques // - Para Path en enlaces a ficheros al mostrar el directorio // - Buffer de recepción de la orden // Atención, una vez obtenido el nombre del fichero, se emplea para transferencia del propio fichero // o para gestion del nombre si es directorio #define SIZEBUFFER 128 // 256 38s, char MiBuffer[SIZEBUFFER]; #define BNL_FILAS 6 // Nombre largo // Como aparecen las entradas desde atrás hacia alante, se rellena desde atras // y luego se lee como unicode // unsigned int 2 bytes para unicode int BnlFila_i=0; // -1 si nombre largo mal boolean BnlSi=false; // Hay nombre largo char Bnl[BNL_FILAS+1][26]; // store error strings in flash to save RAM #define error(s) error_P(PSTR(s)) void error_P(const char* str) { PgmPrint("error: "); SerialPrintln_P(str); if (card.errorCode()) { PgmPrint("SD error: "); Serial.print(card.errorCode(), HEX); Serial.print(','); Serial.println(card.errorData(), HEX); } while(1); } void setup() { // Ultimo caracter de buffer de nombre largo a 0 Bnl[BNL_FILAS+1][0]='\0'; Bnl[BNL_FILAS+1][1]='\0'; // Debug por Serial Serial.begin(115200); #ifdef VERSION PgmPrint("\n");PgmPrintln(VERSION); PgmPrint("Free RAM: "); Serial.println(FreeRam()); #endif // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // breadboards. use SPI_FULL_SPEED for better performance. pinMode(10, OUTPUT); // set the SS pin as an output (necessary!) digitalWrite(10, HIGH); // but turn off the W5100 chip! // initilize card if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed!"); #ifdef DEBUG_VerDatosTarjeta // print the type of card // PgmPrint("\nCard type: "); Serial.print(("\nCard type: ")); switch(card.type()) { case SD_CARD_TYPE_SD1: PgmPrintln("SD1"); break; case SD_CARD_TYPE_SD2: PgmPrintln("SD2"); break; case SD_CARD_TYPE_SDHC: PgmPrintln("SDHC"); break; default: PgmPrintln("Unknown"); } #endif // initialize a FAT volume if (!volume.init(&card)) error("vol.init failed!"); #ifdef DEBUG_VerDatosTarjeta PgmPrint("Volume is FAT: "); Serial.println(volume.fatType(),DEC); PgmPrint(" Bloques por cluster: "); Serial.println(volume.blocksPerCluster(),DEC); PgmPrint(" Bloques por FAT: "); Serial.println(volume.blocksPerFat(),DEC); PgmPrint(" Numero de clusters: "); Serial.println(volume.clusterCount(),DEC); uint32_t volumesize; volumesize = volume.blocksPerCluster(); // clusters are collections of blocks volumesize *= volume.clusterCount(); // we'll have a lot of clusters volumesize *= 512; // SD card blocks are always 512 bytes volumesize /= 1024; // Kib volumesize /= 1024; // Mib PgmPrint(" Volume size (Mbytes): "); Serial.println(volumesize,DEC); #endif if (!root.openRoot(&volume)) error("openRoot failed"); #ifdef DEBUG_VerTreeTarjeta // Recursive list of all directories PgmPrintln("Files found in all dirs:"); root.ls(&Serial,LS_R | LS_DATE | LS_SIZE,1); Serial.println(); #endif PgmPrintln("Done"); // Debugging complete, we start the server! Ethernet.begin(mac, ip); server.begin(); } // Cabecera HTML (no se muestra en codigo fuente) #define TXT 1 #define RAR 2 #define MP3 3 #define JPG 4 #define XLS 5 #define GIF 6 #define HTM 7 #define ZIP 8 #define PDF 9 #define TAR 10 void htmlCab(EthernetClient client,int tipo) { Pln_P(&client,"HTTP/1.1 200 OK"); PgmPrintln("HTTP/1.1 200 OK"); P_P(&client,"Content-Type: "); PgmPrint("Content-Type: "); switch (tipo) { case TXT: Pln_P(&client,"text/html"); PgmPrint("text/html"); break; case RAR: Pln_P(&client,"application/x-rar"); PgmPrint("application/x-rar"); break; case MP3: Pln_P(&client,"audio/mpeg"); PgmPrint("audio/mpeg"); break; case JPG: Pln_P(&client,"image/jpeg"); PgmPrint("image/jpeg"); break; case XLS: Pln_P(&client,"application/xls"); PgmPrint("application/xls"); break; case GIF: Pln_P(&client,"image/gif"); PgmPrint("image/gif"); break; case HTM: Pln_P(&client,"text/html"); PgmPrint("text/html"); break; case ZIP: Pln_P(&client,"application/x-zip"); PgmPrint("application/x-zip"); break; case PDF: Pln_P(&client,"application/pdf"); PgmPrint("application/pdf"); break; case TAR: Pln_P(&client,"application/x-tar"); PgmPrint("application/x-tar"); break; default: Pln_P(&client,"application"); PgmPrint("application"); } client.println(); Serial.println(); PgmPrint("Tipo Content-Type: ");Serial.println(tipo); } // Para mostrar bien las cosas void htmlStart(EthernetClient client) { Pln_P(&client,""); Pln_P(&client,""); // Los nombres cortos 8.3 están guardados en cp850. // Pln_P(&client,""); Pln_P(&client,""); Pln_P(&client,""); Pln_P(&client,""); } // Para mostrar bien las cosas void htmlEnd(EthernetClient client) { Pln_P(&client,"\n"); } void HTNLNotFound(EthernetClient client) { Pln_P(&client,"HTTP/1.1 404 Not Found"); Pln_P(&client,"Content-Type: text/html"); client.println(); htmlStart(client); Pln_P(&client,"

File Not Found!

"); htmlEnd(client); } // Entre boolean Entre(char c,char cle, char cge) { return ((cle<=c) && (c<=cge)); } // How big our line buffer should be. 100 is plenty! //#define BUFSIZ 100 #define BUFSIZ SIZEBUFFER void loop() { // char clientline[BUFSIZ]; char * clientline = MiBuffer; int index = 0; EthernetClient client = server.available(); if (client) { // an http request ends with a blank line boolean current_line_is_blank = true; // reset the input buffer index = 0; while (client.connected()) { if (client.available()) { char c = client.read(); // If it isn't a new line, add the character to the buffer if (c != '\n' && c != '\r') { clientline[index] = c; index++; // are we too big for the buffer? start tossing out data if (index >= BUFSIZ) index = BUFSIZ -1; // continue to read more data! continue; } // got a \n or \r new line, which means the string is done clientline[index] = 0; // Print it out for debugging Serial.println(clientline); // Look for substring such as a request to get the root file if (strstr(clientline,"GET / ") != 0) { // send a standard http response header htmlCab(client,HTM); // print all the files htmlStart(client); Pln_P(&client,"

Files:

"); Pln_P(&client,"
");
          PonRoot(); // Path para enlaces
#ifdef DEBUG_VerCambiosPath          
          PgmPrint("++Path: ");Serial.println(MiBuffer);
#endif
//          ls(&root,&client,LS_R | LS_DATE | LS_SIZE,1);
          ls(&root,&client,LS_DATE | LS_SIZE,1);
          P_P(&client,"
"); htmlEnd(client); break; } // Caracteres %xx int i=0,j=i,estado=0,bien=true; if (strstr(clientline,"%") !=0 ) { #ifdef DEBUG_VerConversionUTF_8 PgmPrint("++Caracteres especiales: ");Serial.println(clientline); #endif while (bien) { if (estado==0) { while ( (clientline[j]=clientline[i++]) != '\0' ) { if (clientline[j]=='%') {estado=1;break;} else j++; } if (clientline[j]=='\0') break; } // Como está en estado 1 mira si es uno o dos bytes if (estado==1) { // si es dos bites mira y guarda si es 2 ó 3 // si es 2 bytes empieza por C if (clientline[i]=='C') { i++; // Debe seguir 2 ó 3 if (clientline[i]=='2') estado=2; else if (clientline[i]='3') estado=3; else {bien=false; break;} i++; // y finalizar con la cebecera del siguiente byte if (clientline[i]!='%') {bien=false; break;} else i++; } else { if (Entre(clientline[i],'0','7') && Entre(clientline[i+1],'0','F')) { clientline[j]=(char)( ( (clientline[i++] & 0xF) * 0x10) + (clientline[i++] & 0xF) ); j++; estado=0; } else { bien=false; break; } } } // Estado es 2 ó 3 if ( Entre(clientline[i],'8','F') && Entre(clientline[i+1],'0','F') ) { // clientline[j]=( ( (clientline[i++] & 0xF) * 0x10) + (clientline[i++] & 0xF) ); clientline[j]=clientline[i++]; clientline[j]&=0xF; clientline[j]*=0x10; clientline[j]+=(clientline[i++] & 0xF); if (estado==3) clientline[j]|=0x40; // Para que funcione y busque los caracteres acentuados, debe modificarse una librería. // SdBaseFile.cpp, linea 418 pone: // if (i > n || c < 0X21 || c > 0X7E)goto fail; // debe cambiuarse a: // if (i > n || c < 0X21)goto fail; // // Cambiar caracteres a cp850 // Podría hacerse de otra forma, pero no quedaría tan bonito switch (clientline[j]) { case 0xA0: {clientline[j]=(char)0xA0;break;} // NO-BREAK SPACE case 0xA1: {clientline[j]=(char)0xAD;break;} // ¡ INVERTED EXCLAMATION MARK case 0xA2: {clientline[j]=(char)0xBD;break;} // ¢ CENT SIGN case 0xA3: {clientline[j]=(char)0x9C;break;} // £ POUND SIGN case 0xA4: {clientline[j]=(char)0xCF;break;} // ¤ CURRENCY SIGN case 0xA5: {clientline[j]=(char)0xBE;break;} // ¥ YEN SIGN case 0xA6: {clientline[j]=(char)0xDD;break;} // ¦ BROKEN BAR case 0xA7: {clientline[j]=(char)0xF5;break;} // § SECTION SIGN case 0xA8: {clientline[j]=(char)0xF9;break;} // ¨ DIAERESIS case 0xA9: {clientline[j]=(char)0xB8;break;} // © COPYRIGHT SIGN case 0xAA: {clientline[j]=(char)0xA6;break;} // ª FEMININE ORDINAL INDICATOR case 0xAB: {clientline[j]=(char)0xAE;break;} // « LEFT-POINTING DOUBLE ANGLE QUOTATION MARK case 0xAC: {clientline[j]=(char)0xAA;break;} // ¬ NOT SIGN case 0xAD: {clientline[j]=(char)0xF0;break;} // ­ SOFT HYPHEN case 0xAE: {clientline[j]=(char)0xA9;break;} // ® REGISTERED SIGN case 0xAF: {clientline[j]=(char)0xEE;break;} // ¯ MACRON case 0xB0: {clientline[j]=(char)0xF8;break;} // ° DEGREE SIGN case 0xB1: {clientline[j]=(char)0xF1;break;} // ± PLUS-MINUS SIGN case 0xB2: {clientline[j]=(char)0xFD;break;} // ² SUPERSCRIPT TWO case 0xB3: {clientline[j]=(char)0xFC;break;} // ³ SUPERSCRIPT THREE case 0xB4: {clientline[j]=(char)0xEF;break;} // ´ ACUTE ACCENT case 0xB5: {clientline[j]=(char)0xE6;break;} // µ MICRO SIGN case 0xB6: {clientline[j]=(char)0xF4;break;} // ¶ PILCROW SIGN case 0xB7: {clientline[j]=(char)0xFA;break;} // · MIDDLE DOT case 0xB8: {clientline[j]=(char)0xF7;break;} // ¸ CEDILLA case 0xB9: {clientline[j]=(char)0xD5;break;} // ¹ SUPERSCRIPT ONE case 0xBA: {clientline[j]=(char)0xA7;break;} // º MASCULINE ORDINAL INDICATOR case 0xBB: {clientline[j]=(char)0xAF;break;} // » RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK case 0xBC: {clientline[j]=(char)0xAC;break;} // ¼ VULGAR FRACTION ONE QUARTER case 0xBD: {clientline[j]=(char)0xAB;break;} // ½ VULGAR FRACTION ONE HALF case 0xBE: {clientline[j]=(char)0xF3;break;} // ¾ VULGAR FRACTION THREE QUARTERS case 0xBF: {clientline[j]=(char)0xA8;break;} // ¿ INVERTED QUESTION MARK case 0xC0: {clientline[j]=(char)0xB7;break;} // À LATIN CAPITAL LETTER A WITH GRAVE case 0xC1: {clientline[j]=(char)0xB5;break;} // Á LATIN CAPITAL LETTER A WITH ACUTE case 0xC2: {clientline[j]=(char)0xB6;break;} // Â LATIN CAPITAL LETTER A WITH CIRCUMFLEX case 0xC3: {clientline[j]=(char)0xC7;break;} // Ã LATIN CAPITAL LETTER A WITH TILDE case 0xC4: {clientline[j]=(char)0x8E;break;} // Ä LATIN CAPITAL LETTER A WITH DIAERESIS case 0xC5: {clientline[j]=(char)0x8F;break;} // Å LATIN CAPITAL LETTER A WITH RING ABOVE case 0xC6: {clientline[j]=(char)0x92;break;} // Æ LATIN CAPITAL LETTER AE case 0xC7: {clientline[j]=(char)0x80;break;} // Ç LATIN CAPITAL LETTER C WITH CEDILLA case 0xC8: {clientline[j]=(char)0xD4;break;} // È LATIN CAPITAL LETTER E WITH GRAVE case 0xC9: {clientline[j]=(char)0x90;break;} // É LATIN CAPITAL LETTER E WITH ACUTE case 0xCA: {clientline[j]=(char)0xD2;break;} // Ê LATIN CAPITAL LETTER E WITH CIRCUMFLEX case 0xCB: {clientline[j]=(char)0xD3;break;} // Ë LATIN CAPITAL LETTER E WITH DIAERESIS case 0xCC: {clientline[j]=(char)0xDE;break;} // Ì LATIN CAPITAL LETTER I WITH GRAVE case 0xCD: {clientline[j]=(char)0xD6;break;} // Í LATIN CAPITAL LETTER I WITH ACUTE case 0xCE: {clientline[j]=(char)0xD7;break;} // Î LATIN CAPITAL LETTER I WITH CIRCUMFLEX case 0xCF: {clientline[j]=(char)0xD8;break;} // Ï LATIN CAPITAL LETTER I WITH DIAERESIS case 0xD0: {clientline[j]=(char)0xD1;break;} // Ð LATIN CAPITAL LETTER ETH case 0xD1: {clientline[j]=(char)0xA5;break;} // Ñ LATIN CAPITAL LETTER N WITH TILDE case 0xD2: {clientline[j]=(char)0xE3;break;} // Ò LATIN CAPITAL LETTER O WITH GRAVE case 0xD3: {clientline[j]=(char)0xE0;break;} // Ó LATIN CAPITAL LETTER O WITH ACUTE case 0xD4: {clientline[j]=(char)0xE2;break;} // Ô LATIN CAPITAL LETTER O WITH CIRCUMFLEX case 0xD5: {clientline[j]=(char)0xE5;break;} // Õ LATIN CAPITAL LETTER O WITH TILDE case 0xD6: {clientline[j]=(char)0x99;break;} // Ö LATIN CAPITAL LETTER O WITH DIAERESIS case 0xD7: {clientline[j]=(char)0x9E;break;} // × MULTIPLICATION SIGN case 0xD8: {clientline[j]=(char)0x9D;break;} // Ø LATIN CAPITAL LETTER O WITH STROKE case 0xD9: {clientline[j]=(char)0xEB;break;} // Ù LATIN CAPITAL LETTER U WITH GRAVE case 0xDA: {clientline[j]=(char)0xE9;break;} // Ú LATIN CAPITAL LETTER U WITH ACUTE case 0xDB: {clientline[j]=(char)0xEA;break;} // Û LATIN CAPITAL LETTER U WITH CIRCUMFLEX case 0xDC: {clientline[j]=(char)0x9A;break;} // Ü LATIN CAPITAL LETTER U WITH DIAERESIS case 0xDD: {clientline[j]=(char)0xED;break;} // Ý LATIN CAPITAL LETTER Y WITH ACUTE case 0xDE: {clientline[j]=(char)0xE7;break;} // Þ LATIN CAPITAL LETTER THORN case 0xDF: {clientline[j]=(char)0xE1;break;} // ß LATIN SMALL LETTER SHARP S case 0xE0: {clientline[j]=(char)0x85;break;} // à LATIN SMALL LETTER A WITH GRAVE case 0xE1: {clientline[j]=(char)0xA0;break;} // á LATIN SMALL LETTER A WITH ACUTE case 0xE2: {clientline[j]=(char)0x83;break;} // â LATIN SMALL LETTER A WITH CIRCUMFLEX case 0xE3: {clientline[j]=(char)0xC6;break;} // ã LATIN SMALL LETTER A WITH TILDE case 0xE4: {clientline[j]=(char)0x84;break;} // ä LATIN SMALL LETTER A WITH DIAERESIS case 0xE5: {clientline[j]=(char)0x86;break;} // å LATIN SMALL LETTER A WITH RING ABOVE case 0xE6: {clientline[j]=(char)0x91;break;} // æ LATIN SMALL LETTER AE case 0xE7: {clientline[j]=(char)0x87;break;} // ç LATIN SMALL LETTER C WITH CEDILLA case 0xE8: {clientline[j]=(char)0x8A;break;} // è LATIN SMALL LETTER E WITH GRAVE case 0xE9: {clientline[j]=(char)0x82;break;} // é LATIN SMALL LETTER E WITH ACUTE case 0xEA: {clientline[j]=(char)0x88;break;} // ê LATIN SMALL LETTER E WITH CIRCUMFLEX case 0xEB: {clientline[j]=(char)0x89;break;} // ë LATIN SMALL LETTER E WITH DIAERESIS case 0xEC: {clientline[j]=(char)0x8D;break;} // ì LATIN SMALL LETTER I WITH GRAVE case 0xED: {clientline[j]=(char)0xA1;break;} // í LATIN SMALL LETTER I WITH ACUTE case 0xEE: {clientline[j]=(char)0x8C;break;} // î LATIN SMALL LETTER I WITH CIRCUMFLEX case 0xEF: {clientline[j]=(char)0x8B;break;} // ï LATIN SMALL LETTER I WITH DIAERESIS case 0xF0: {clientline[j]=(char)0xD1;break;} // ð LATIN SMALL LETTER ETH case 0xF1: {clientline[j]=(char)0xA4;break;} // ñ LATIN SMALL LETTER N WITH TILDE case 0xF2: {clientline[j]=(char)0x95;break;} // ò LATIN SMALL LETTER O WITH GRAVE case 0xF3: {clientline[j]=(char)0xA2;break;} // ó LATIN SMALL LETTER O WITH ACUTE case 0xF4: {clientline[j]=(char)0x93;break;} // ô LATIN SMALL LETTER O WITH CIRCUMFLEX case 0xF5: {clientline[j]=(char)0xE4;break;} // õ LATIN SMALL LETTER O WITH TILDE case 0xF6: {clientline[j]=(char)0x94;break;} // ö LATIN SMALL LETTER O WITH DIAERESIS case 0xF7: {clientline[j]=(char)0xF6;break;} // ÷ DIVISION SIGN case 0xF8: {clientline[j]=(char)0x9B;break;} // ø LATIN SMALL LETTER O WITH STROKE case 0xF9: {clientline[j]=(char)0x97;break;} // ù LATIN SMALL LETTER U WITH GRAVE case 0xFA: {clientline[j]=(char)0xA3;break;} // ú LATIN SMALL LETTER U WITH ACUTE case 0xFB: {clientline[j]=(char)0x96;break;} // û LATIN SMALL LETTER U WITH CIRCUMFLEX case 0xFC: {clientline[j]=(char)0x81;break;} // ü LATIN SMALL LETTER U WITH DIAERESIS case 0xFD: {clientline[j]=(char)0xEC;break;} // ý LATIN SMALL LETTER Y WITH ACUTE case 0xFE: {clientline[j]=(char)0xE8;break;} // þ LATIN SMALL LETTER THORN case 0xFF: {clientline[j]=(char)0x98;break;} // ÿ LATIN SMALL LETTER Y WITH DIAERESIS } j++; estado=0; } else { bien=false; break; } } // while if (!bien) { PgmPrint("++Caracteres especiales: Ha fallado"); HTNLNotFound(client); } #ifdef DEBUG_VerConversionUTF_8 PgmPrint("++Caracteres especiales: ");Serial.println(clientline); #endif } // La cadena clientline está para extraer nombres de fichero if (strstr(clientline,"GET /") != 0) { // this time no space after the /, so a sub-file! char *filename; filename = clientline + 4; // look after the "GET /" (4 chars) // a little trick, look for the " HTTP/1.1" string and // turn the first character of the substring into a 0 to clear it out. (strstr(clientline," HTTP"))[0] = 0; // print the file we want Serial.println(filename); #ifdef DEBUG_VerConversionUTF_8 // debug de nombre for (int j=0;filename[j]!='\0';j++) { Serial.print((char)filename[j],HEX);Serial.print(' '); } Serial.println(); #endif if (! file.open(&root, filename, O_READ)) { #ifdef DEBUG_VerConversionUTF_8 PgmPrint("++Caracteres especiales: No encontrado"); #endif HTNLNotFound(client); break; } #ifdef DEBUG_FileOpenClose PgmPrintln("Opened!"); #endif // Si es directorio, muestra el árbol descendente. // Si es fichero lo traslada (write) if (file.isDir()) { htmlStart(client); P_P(&client,"

Files: "); client.print(filename); Pln_P(&client,"

"); Pln_P(&client,"
");
            // Atención: aqui hay un truco. Se copia el filename al principio del mismo buffer.
            // El puntero filename no debe volver a usarse.
            PonPath(filename); // Path para enlaces
#ifdef DEBUG_VerCambiosPath            
            PgmPrint("++Path: ");Serial.println(MiBuffer);
#endif
//            ls(&file,&client,LS_R | LS_DATE | LS_SIZE,1);
            ls(&file,&client,LS_DATE | LS_SIZE,1);
            Pln_P(&client,"
"); htmlEnd(client); file.close(); break; } // Es fichero a descargar #ifdef DEBUG_FileOpenClose PgmPrintln("A por la extension."); #endif char * extension; int i=0; while (filename[i++]!='\0'); #ifdef DEBUG_FileOpenClose PgmPrint("LongFilename: ");Serial.println(i); #endif while (i>0 && filename[i]!='.') i--; if (i>0) { extension=&(filename[++i]); #ifdef DEBUG_FileOpenClose PgmPrint("Extension: ");Serial.println(extension); #endif #define Ve3Z(x,y,z,t) Serial.print(x[0]);Serial.print(x[1]);Serial.print(x[2]);Serial.write(' ');Serial.write(y);Serial.write(z);Serial.write(t);Serial.println() Ve3Z(extension,'H','T','M'); #define Ve3(x,y,z,t) (((x[0]&0xDF)==y) && ((x[1]&0xDF)==z) && ((x[2]&0xDF)==t)) if Ve3(extension,'T','X','T') htmlCab(client,TXT); else if Ve3(extension,'R','A','R') htmlCab(client,RAR); else if Ve3(extension,'M','P','3') htmlCab(client,MP3); else if Ve3(extension,'J','P','G') htmlCab(client,JPG); else if Ve3(extension,'X','L','S') htmlCab(client,XLS); else if Ve3(extension,'G','I','F') htmlCab(client,GIF); else if Ve3(extension,'H','T','M') htmlCab(client,HTM); else if Ve3(extension,'Z','I','P') htmlCab(client,ZIP); else if Ve3(extension,'P','D','F') htmlCab(client,PDF); else if Ve3(extension,'T','A','R') htmlCab(client,TAR); else htmlCab(client,99); } else htmlCab(client,HTM); // Aqui hay un truco: el buffer empleado es el mimsmo que tiene filename y extensión. // Los punteros no deben volver a usarse // Leo en cantidades de BUFFESIZE uint32_t Size=file.fileSize(); while ((Size>=SIZEBUFFER) && (file.read(MiBuffer,SIZEBUFFER) > -1)) { client.write((uint8_t*)MiBuffer,SIZEBUFFER); Size-=SIZEBUFFER; } if ((Size>0) && (Size -1)) client.write((uint8_t*)MiBuffer,Size); file.close(); #ifdef DEBUG_FileOpenClose PgmPrintln("Closed!"); #endif } else { // everything else is a 404 HTNLNotFound(client); } break; } } // give the web browser time to receive the data delay(1); client.stop(); } } // // Escribe una cadena de unsigne int en HTML, caracteres y unicode cuando haga falta //void PrintUnicode(Print* pr,unsigned int *p) { void PrintUnicode(Print* pr) { unsigned int *p=(unsigned int *)&Bnl; while (*p != 0) { if ((0x1F < *p) && (*p < 0x80)) pr->write((char)*p); else { pr->print("&#x");pr->print(*p,HEX);pr->write(';'); } p++; } } // Nombre del último fichero encontrado. Si es directorio debe pasarse al Path para // tener la referencia del siguiente nivel char FN83[13]; // Listado de subestructura de directorios void ls(SdBaseFile* This,Print* pr, uint8_t flags, uint8_t indent) { This->rewind(); int8_t status; while ((status = lsPrintNext(This, pr, flags, indent))) { if (status > 1 && (flags & LS_R)) { uint16_t index = This->curPosition()/32 - 1; SdBaseFile s; PonDir(FN83); #ifdef DEBUG_VerCambiosPath PgmPrint("--Fichero: ");Serial.println(FN83); PgmPrint("++Path: ");Serial.println(MiBuffer); #endif if (s.open(This, index, O_READ)) ls(&s,pr, flags, indent + 2); QuitaDir(); #ifdef DEBUG_VerCambiosPath PgmPrint("--Path: ");Serial.println(MiBuffer); #endif This->seekSet(32 * (index + 1)); } } } //------------------------------------------------------------------------------ // saves 32 bytes on stack for ls recursion // return 0 - EOF, 1 - normal file, or 2 - directory int8_t lsPrintNext(SdBaseFile *This,Print* pr, uint8_t flags, uint8_t indent) { dir_t dir; uint8_t w = 0; // char FN83[13]; // No sale hasta que encuentra un fichero a mostrar // Las lineas de nombres largos rellenan el buffer de nombre largo while (1) { // No lee lo suficiente if (This->read(&dir, sizeof(dir)) != sizeof(dir)) return 0; // No hay mas ficheros #ifdef DEBUG_VerFiledirHex // debug d caracteres leídos for (int j=0;jprint(dir.name[j],HEX);pr->print(' '); // Serial.print(dir.name[j],HEX);Serial.print(' '); } pr->println(); // Serial.println; #endif // entrada libre en fin de entradas de fichero if (dir.name[0] == DIR_NAME_FREE) return 0; // no hay mas ficheros // es una entrada borrada if (dir.name[0] == DIR_NAME_DELETED) continue; // buscar siguiente // salta el directorio . (el .. me interesa para subir) if ((dir.name[0] == '.') && (dir.name[1] != '.')) continue; // buscar siguiente // Condicion de nombre largo negativa para salis if (DIR_IS_FILE_OR_SUBDIR(&dir)) break; // Ficheros y directorios if ((dir.attributes & DIR_ATT_LONG_NAME_MASK) != DIR_ATT_LONG_NAME) break; // Sale a impimir linea // Nombre largo, se añade al buffer if (BnlFila_i == -1) continue; // Error en nombre largo. La rutina de escritura lo debe poner a cero // Si es final 0x4X (aparece primero) y no hay nombre largo inicializo nombre largo if ((dir.name[0] & 0xF0) == 0x40) { if (BnlSi) {BnlFila_i=-1; continue;} // Algo raro en la secuencia else BnlSi=true; // Debería ser un else pero no hace falta if ((BnlFila_i=(dir.name[0] & 0x3F)) > BNL_FILAS) {BnlFila_i=-1; continue;} // Fila inicial No cabe if (BnlFila_iprint(dir.name[j],HEX);pr->print(' '); Serial.print(dir.name[j],HEX);Serial.print(' '); } pr->println(); Serial.println(); } #endif } // while // indent for dir level for (uint8_t i = 0; i < indent; i++) pr->write(' '); // print name int j=0; for (uint8_t i = 0; i < 11; i++) { if (dir.name[i] == ' ')continue; if (i == 8) { FN83[j++]='.'; w++; } FN83[j++]=dir.name[i]; w++; } // Termina en 0 FN83[j]='\0'; // Escribe referencia de fichero // if (!(dir.attributes & DIR_ATT_VOLUME_ID)) { // No es etiqueta de volumen pr->print("print(MiBuffer); // Path termina en / pr->print(FN83); pr->print("\">"); } // endif No es etiqueta de volumen pr->print(FN83); //Serial.print(FN83); // Esto solo a la vista, no en el enlace if (DIR_IS_SUBDIR(&dir)) { pr->write('/'); w++; } if (!(dir.attributes & DIR_ATT_VOLUME_ID)) { // No es etiqueta de volumen pr->print(""); } // endif No es etiqueta de volumen if (flags & (LS_DATE | LS_SIZE)) { while (w++ < 14) pr->write(' '); } #ifdef ATRIBUTOS // Mostrar atributos pr->write(' '); pr->write((dir.attributes & DIR_ATT_READ_ONLY) ? 'R' : ' '); //1 pr->write((dir.attributes & DIR_ATT_HIDDEN ) ? 'H' : ' '); //2 pr->write((dir.attributes & DIR_ATT_SYSTEM ) ? 'S' : ' '); //4 pr->write((dir.attributes & DIR_ATT_VOLUME_ID) ? 'V' : ' '); //8 pr->write((dir.attributes & DIR_ATT_DIRECTORY) ? 'D' : ' '); //16 0x10 pr->write((dir.attributes & DIR_ATT_ARCHIVE ) ? 'A' : ' '); //32 0x20 pr->write((dir.attributes & 0x40 ) ? '6' : ' '); //64 0x40 pr->write((dir.attributes & 0x80 ) ? '7' : ' '); //128 0x80 #endif // print modify date/time if requested if (flags & LS_DATE) { pr->write(' '); This->printFatDate(pr, dir.lastWriteDate); pr->write(' '); This->printFatTime(pr, dir.lastWriteTime); } // print size if requested if (!DIR_IS_SUBDIR(&dir) && !(dir.attributes & DIR_ATT_VOLUME_ID) && (flags & LS_SIZE)) { pr->write(' '); pr->print(dir.fileSize); } // nombre largo if (BnlSi && (BnlFila_i!=-1)) { pr->write(' '); PrintUnicode(pr); // PrintUnicode(&Serial); } BnlSi=false;BnlFila_i=0; // pr->println(); //Serial.println(); int8_t retorno; if (!DIR_IS_SUBDIR(&dir)) retorno=1; else retorno=(dir.name[0] == '.') ? 1: 2; return retorno; } // Pon directorio raiz void PonRoot() { MiBuffer[0]='/'; MiBuffer[1]='\0'; // inicializo la cadena #ifdef DEBUG_VerCambiosPath PgmPrint("PonRoot: ");Serial.println(MiBuffer); #endif } // Pone el path completo void PonPath(char *c) { // PgmPrint("-- Rutina PonPath: ");Serial.println(c); int i=0; //int j=i;Serial.print(j);PgmPrint(" ");Serial.print((char)c[j]); while ((MiBuffer[i]=c[i])!='\0') i++; // {PgmPrint(" ");Serial.println(MiBuffer[j],HEX);j=i;} // Si no termina en / la añado if (MiBuffer[(i-1)]!='/') { MiBuffer[i++]='/'; MiBuffer[i]='\0'; #ifdef DEBUG_VerCambiosPath PgmPrint("PonPath: ");Serial.println(MiBuffer); #endif } } // Añado nombre al path y pongo barra si falta void PonDir(char* c) { int i=0,j=0; for (i=0;i0) && (MiBuffer[--i]=='/')) i--; while ((i>0) && (MiBuffer[i]!='/')) i--; // Busco la barra if (i==0) {MiBuffer[i]='/';}; // Directorio raiz MiBuffer[++i]='\0'; // Nuevo cero }