Alberto Seva WebServerSD.015t

 

Mi proyecto es crear un servidor Web con el “Arduino UNO” (RS  715-4081 [1]) y el “ETHernet shield with SD card” (RS 715-4072 [2]).

 

En una primera búsqueda encontré el tutorial:

http://www.ladyada.net/wiki/tutorials/learn/arduino/ethfiles.html

donde propone el código ofrecido aquí:

https://github.com/adafruit/SDWebBrowse/blob/master/SDWebBrowse.ino

a partir del cual empecé a realizar modificaciones.

En este código se emplean la librería SdFat.h. Esta librería no aparece en el sistema de desarrollo en su versión para Windows 32bits. La obtengo desde:

http://sdfatlib.googlecode.com/files/sdfatlib20111205.zip

siendo el listado de las librerías:

http://code.google.com/p/sdfatlib/downloads/list

 

Ese código permite listar el directorio raiz de la tarjeta y abrir ficheros listados en ese directorio.

La idea es ser capaz de mostrar el árbol completo de la tarjeta con sus enlaces bien construidos en HTML para el acceso a cualquier fichero independientemente de la profundidad.

Una vez hecho esto, si el fichero es un fichero subdirectorio, se muestra el directorio con los enlaces adecuados, y si se trata de un fichero, se envia como respuesta HTML al cliente que lo solicita.

Realmente un servidor web estático (sin peticiones que tenga que evaluar) lo unico que hace es enviar un fichero. El navegador que recibe el fichero es el que averigua si necesita más piezas y las pide de una en una.

 

Las mejoras de mi código suponen lo siguiente:

 

A partir de aquí se puede mejorar la rutina de lectura de la linea de petición, para detectar no solo el nombre del fichero solicitado sino también argumentos.

Descargas:

Fichero ino: sketch_mar21c004WebServerSD_015t.ino
Fichero ino comprimido: sketch_mar21c004WebServerSD_015t.zip
Fichero SdBaseFile.cpp modificado: SdBaseFile.cpp (Path: arduino-1.0/libraries/SdFat)
Fichero SdBaseFile.cpp modificado comprimido: SdBaseFile.zip (Path: arduino-1.0/libraries/SdFat)

En fichero DOC: Proyecto Servidor web 15m.doc

Versiones anteriores:
Fichero ino: sketch_mar21c004WebServerSD_015m.ino
Fichero ino comprimido: sketch_mar21c004WebServerSD_015m.zip

Fuente:

 

Alberto Seva WebServerSD.015t

*--------------------------------------- Cortar por aquí ---------------------------------------*  

/*

 * Alberto Seva WebServerSD.015t

 *

 * http://dfists.ua.es/~alberto/arduino/arduino.htm

 *

 *

 * Mi proyecto es crear un servidor Web con el “Arduino UNO” (RS  715-4081[1]) y

 * el "ETHernet shield with SD card” (RS 715-4072[2]).

 *

 * [1] http://es.rs-online.com/web/p/products/715-4081/

 * [2] http://es.rs-online.com/web/p/microcontrolador-procesador/7154072/

 *

 * A partir del sketch 'SDWebBrowse.ino', las mejoras de mi código suponen

 * lo siguiente:

 *

 *  * Los nombres de fichero 8.3 que el sistema SdFat es capaz de encontrar,

 *    se guardan en el formato correspondiente a la página de códigos de

 *    MSDOS cp850. Se debe especificar esa página de códigos para que los

 *    enlaces se puedan reconstruir.

 *  * Los nombres 8.3 normalmente no deberían tener caracteres por encima

 *    del 0x7F, pero en algunas de mis tarjetas he descubierto que no es así.

 *    Surge el problema de que los enlaces con caracteres por encima del 0x7F

 *    enviados en los enlaces con formato cp850, regresan con el formto UTF-8

 *    expresado como %C2 ó %C3 y a continuación otro código con el formato

 *    %XX. Esos códigos hay que convertirlos primero a un carácter

 *    equivalente, y luego mediante una tabla totalmente arbitraria, al

 *    carácter correcto en cp850 que sirva en el proceso de búsqueda del

 *    nombre del fichero.

 *  * En el listado de directorio, la rutina es capaz de detectar las entradas

 *    de nombre largo y escribirlas como información aunque sin ninguna opción

 *    de búsqueda real.

 *  * En la petición de ficheros, se intenta mandar información del

 *    content-type de manera correcta. El HTML supone programas muchas cadenas

 *    constantes que hay que almacenar en la zona de programa para que el

 *    programa no se quede sin memoria.

 *  * La transmisión de ficheros usando un buffer, mejora muchísimo el

 *    rendimiento del servidor web.

 *

 * Se ha probado con el Arduino UNO y con el MEGA 2560 (RS 715-4084 [3].

 * Como intento que funcione en el primero, hay unos trucos de ahorro de

 * memoria compartiendo el buffer de entrada.

 *

 *

 * [3] http://es.rs-online.com/web/p/microcontrolador-procesador/7154084/

 */

 

/*

 * 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 cambiarse a:

//       if (i > n || c < 0X21)goto fail;

*/

 

#include <SdFat.h>

#include <SdFatUtil.h>

#include <Ethernet.h>

#include <SPI.h>

 

// 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.015t"

// 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

//#define DEBUG_CabeceraHTML

 

// 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 ip[] = { 172, 17, 34, 50 };

//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];

// Para nombre 8.3 el vista de subdirectorios

char * FN83=&(Bnl[BNL_FILAS+1][2]); // Reserva del 2 al 14 (13 bytes; 8+'.'+3+'\0')

 

// 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: ");

  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();

}

 

 

// Escribir cadenas de content type.

// Las cadenas se almacenan en zona de programa y se escribe 2 veces

 

// 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 htmlLin(EthernetClient client,const prog_char * p) {

  print_P(&client,p);

#ifdef DEBUG_CabeceraHTML 

  print_P(&Serial,p);

#endif 

}

 

void htmlLinln(EthernetClient client,const prog_char * p) {

  println_P(&client,p);

#ifdef DEBUG_CabeceraHTML 

  println_P(&Serial,p);

#endif 

}

 

#define HTMLIN(x)   htmlLin(client,PSTR(x))

#define HTMLINln(x) htmlLinln(client,PSTR(x))

 

void htmlCab(EthernetClient client,int tipo) {

  HTMLINln("HTTP/1.1 200 OK");

  HTMLIN("Content-Type: ");

  switch (tipo) {

    case TXT: HTMLINln("text/plain");

              break;

    case RAR: HTMLINln("application/x-rar");

              break;

    case MP3: HTMLINln("audio/mpeg");

              break;

    case JPG: HTMLINln("image/jpeg");

              break;

    case XLS: HTMLINln("application/xls");

              break;

    case GIF: HTMLINln("image/gif");

              break;

    case HTM: HTMLINln("text/html");

              break;

    case ZIP: HTMLINln("application/x-zip");

              break;

    case PDF: HTMLINln("application/pdf");

              break;

    case TAR: HTMLINln("application/x-tar");

              break;

    default:  HTMLINln("application");

  }

  client.println();

}

 

#undef HTMLIN

#undef HTMLINln

 

// Para mostrar bien las cosas

void htmlStart(EthernetClient client) {

  Pln_P(&client,"<html>");

  Pln_P(&client,"<head>");

  // Los nombres cortos 8.3 están guardados en cp850.

  // Pln_P(&client,"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1252\">");

  Pln_P(&client,"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=cp850\">");

  Pln_P(&client,"</head>");

  Pln_P(&client,"<body>");

}

 

// Para mostrar bien las cosas

void htmlEnd(EthernetClient client) {

  Pln_P(&client,"</body>\n</html>");

}

 

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,"<h2>File Not Found!</h2>");

  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,"<h2>Files:</h2>");

          Pln_P(&client,"<pre>");

          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,"</pre>");

          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;

               UTF_8acp850(&(clientline[j]));

               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,"<h2>Files: ");

            client.print(filename);

            Pln_P(&client,"</h2>");

            Pln_P(&client,"<pre>");

            // 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,"</pre>");

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

#undef Ve3

         

          // 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<SIZEBUFFER) && (file.read(MiBuffer,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(F("&#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;j<sizeof(dir);j++) {

      pr->print(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_i<BNL_FILAS) Bnl[BnlFila_i--][0]=0; // Asegurar de poner un 0 por si las mosca

        else BnlFila_i--;    

    } else {

      if (!(BnlFila_i == (dir.name[0] & 0x0F))) {BnlFila_i=-1; continue;} // Algo raro en la secuencia

      BnlFila_i--;

    }

    // Ahora a rellenar la linea

    int k=0;

    for (int i=1;i<32;i++) {

      if (i==0 || i==11 || i==12 || i==13 || i==26 || i==27) continue;

      Bnl[BnlFila_i][k++]=dir.name[i];

    }

 

#ifdef DEBUG_NombreLargo

  // Si entrada de nombre largo, mostrarlo en hexa

  if ((dir.attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME) {

    for (int j=0;j<sizeof(dir);j++) {

      pr->print(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(F("<a href=\""));

  pr->print(MiBuffer); // Path termina en /

  pr->print(FN83);

  pr->print(F("\">"));

 

  } // 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(F("</a>"));

  } // 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;i<SIZEBUFFER;i++) { // Busco el 0

    if (MiBuffer[i]=='\0') break;

  }

  // Si no termina en '/' lo añado

  if ((i==0) || (MiBuffer[i-1]!='/')) {

    MiBuffer[i++]='/';

    MiBuffer[i]='/0';

  }

  while ((MiBuffer[i++]=c[j++])!='\0');

  if (MiBuffer[i-2]!='/') {

    MiBuffer[i-1]='/';

    MiBuffer[i]='\0';

  }

}

 

// Termina en barra, por lo que tengo que buscar la segunda barra

void QuitaDir() {

  int i;

  for (i=0;i<SIZEBUFFER;i++) { // Busco el 0

    if (MiBuffer[i]=='\0') break; 

  }

  if ((i>0) && (MiBuffer[--i]=='/')) i--;

  while ((i>0) && (MiBuffer[i]!='/')) i--; // Busco la barra

  if (i==0) {MiBuffer[i]='/';}; // Directorio raiz

  MiBuffer[++i]='\0'; // Nuevo cero

}

 

 

// 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 cambiarse a:

//       if (i > n || c < 0X21)goto fail;

// Cambiar caracteres a cp850

void UTF_8acp850(char * c) {

  if ( (unsigned char)*c < 0xA0 ) return;

  switch (*c) {

                   

case 0xA0: {*c=0xA0;break;} //  NO-BREAK SPACE

case 0xA1: {*c=0xAD;break;} // ¡ INVERTED EXCLAMATION MARK

case 0xA2: {*c=0xBD;break;} // ¢ CENT SIGN

case 0xA3: {*c=0x9C;break;} // £ POUND SIGN

case 0xA4: {*c=0xCF;break;} // ¤ CURRENCY SIGN

case 0xA5: {*c=0xBE;break;} // ¥ YEN SIGN

case 0xA6: {*c=0xDD;break;} // ¦ BROKEN BAR

case 0xA7: {*c=0xF5;break;} // § SECTION SIGN

case 0xA8: {*c=0xF9;break;} // ¨ DIAERESIS

case 0xA9: {*c=0xB8;break;} // © COPYRIGHT SIGN

case 0xAA: {*c=0xA6;break;} // ª FEMININE ORDINAL INDICATOR

case 0xAB: {*c=0xAE;break;} // « LEFT-POINTING DOUBLE ANGLE QUOTATION MARK

case 0xAC: {*c=0xAA;break;} // ¬ NOT SIGN

case 0xAD: {*c=0xF0;break;} // ­ SOFT HYPHEN

case 0xAE: {*c=0xA9;break;} // ® REGISTERED SIGN

case 0xAF: {*c=0xEE;break;} // ¯ MACRON

case 0xB0: {*c=0xF8;break;} // ° DEGREE SIGN

case 0xB1: {*c=0xF1;break;} // ± PLUS-MINUS SIGN

case 0xB2: {*c=0xFD;break;} // ² SUPERSCRIPT TWO

case 0xB3: {*c=0xFC;break;} // ³ SUPERSCRIPT THREE

case 0xB4: {*c=0xEF;break;} // ´ ACUTE ACCENT

case 0xB5: {*c=0xE6;break;} // µ MICRO SIGN

case 0xB6: {*c=0xF4;break;} // ¶ PILCROW SIGN

case 0xB7: {*c=0xFA;break;} // · MIDDLE DOT

case 0xB8: {*c=0xF7;break;} // ¸ CEDILLA

case 0xB9: {*c=0xD5;break;} // ¹ SUPERSCRIPT ONE

case 0xBA: {*c=0xA7;break;} // º MASCULINE ORDINAL INDICATOR

case 0xBB: {*c=0xAF;break;} // » RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK

case 0xBC: {*c=0xAC;break;} // ¼ VULGAR FRACTION ONE QUARTER

case 0xBD: {*c=0xAB;break;} // ½ VULGAR FRACTION ONE HALF

case 0xBE: {*c=0xF3;break;} // ¾ VULGAR FRACTION THREE QUARTERS

case 0xBF: {*c=0xA8;break;} // ¿ INVERTED QUESTION MARK

case 0xC0: {*c=0xB7;break;} // À LATIN CAPITAL LETTER A WITH GRAVE

case 0xC1: {*c=0xB5;break;} // Á LATIN CAPITAL LETTER A WITH ACUTE

case 0xC2: {*c=0xB6;break;} // Â LATIN CAPITAL LETTER A WITH CIRCUMFLEX

case 0xC3: {*c=0xC7;break;} // Ã LATIN CAPITAL LETTER A WITH TILDE

case 0xC4: {*c=0x8E;break;} // Ä LATIN CAPITAL LETTER A WITH DIAERESIS

case 0xC5: {*c=0x8F;break;} // Å LATIN CAPITAL LETTER A WITH RING ABOVE

case 0xC6: {*c=0x92;break;} // Æ LATIN CAPITAL LETTER AE

case 0xC7: {*c=0x80;break;} // Ç LATIN CAPITAL LETTER C WITH CEDILLA

case 0xC8: {*c=0xD4;break;} // È LATIN CAPITAL LETTER E WITH GRAVE

case 0xC9: {*c=0x90;break;} // É LATIN CAPITAL LETTER E WITH ACUTE

case 0xCA: {*c=0xD2;break;} // Ê LATIN CAPITAL LETTER E WITH CIRCUMFLEX

case 0xCB: {*c=0xD3;break;} // Ë LATIN CAPITAL LETTER E WITH DIAERESIS

case 0xCC: {*c=0xDE;break;} // Ì LATIN CAPITAL LETTER I WITH GRAVE

case 0xCD: {*c=0xD6;break;} // Í LATIN CAPITAL LETTER I WITH ACUTE

case 0xCE: {*c=0xD7;break;} // Î LATIN CAPITAL LETTER I WITH CIRCUMFLEX

case 0xCF: {*c=0xD8;break;} // Ï LATIN CAPITAL LETTER I WITH DIAERESIS

case 0xD0: {*c=0xD1;break;} // Ð LATIN CAPITAL LETTER ETH

case 0xD1: {*c=0xA5;break;} // Ñ LATIN CAPITAL LETTER N WITH TILDE

case 0xD2: {*c=0xE3;break;} // Ò LATIN CAPITAL LETTER O WITH GRAVE

case 0xD3: {*c=0xE0;break;} // Ó LATIN CAPITAL LETTER O WITH ACUTE

case 0xD4: {*c=0xE2;break;} // Ô LATIN CAPITAL LETTER O WITH CIRCUMFLEX

case 0xD5: {*c=0xE5;break;} // Õ LATIN CAPITAL LETTER O WITH TILDE

case 0xD6: {*c=0x99;break;} // Ö LATIN CAPITAL LETTER O WITH DIAERESIS

case 0xD7: {*c=0x9E;break;} // × MULTIPLICATION SIGN

case 0xD8: {*c=0x9D;break;} // Ø LATIN CAPITAL LETTER O WITH STROKE

case 0xD9: {*c=0xEB;break;} // Ù LATIN CAPITAL LETTER U WITH GRAVE

case 0xDA: {*c=0xE9;break;} // Ú LATIN CAPITAL LETTER U WITH ACUTE

case 0xDB: {*c=0xEA;break;} // Û LATIN CAPITAL LETTER U WITH CIRCUMFLEX

case 0xDC: {*c=0x9A;break;} // Ü LATIN CAPITAL LETTER U WITH DIAERESIS

case 0xDD: {*c=0xED;break;} // Ý LATIN CAPITAL LETTER Y WITH ACUTE

case 0xDE: {*c=0xE7;break;} // Þ LATIN CAPITAL LETTER THORN

case 0xDF: {*c=0xE1;break;} // ß LATIN SMALL LETTER SHARP S

case 0xE0: {*c=0x85;break;} // à LATIN SMALL LETTER A WITH GRAVE

case 0xE1: {*c=0xA0;break;} // á LATIN SMALL LETTER A WITH ACUTE

case 0xE2: {*c=0x83;break;} // â LATIN SMALL LETTER A WITH CIRCUMFLEX

case 0xE3: {*c=0xC6;break;} // ã LATIN SMALL LETTER A WITH TILDE

case 0xE4: {*c=0x84;break;} // ä LATIN SMALL LETTER A WITH DIAERESIS

case 0xE5: {*c=0x86;break;} // å LATIN SMALL LETTER A WITH RING ABOVE

case 0xE6: {*c=0x91;break;} // æ LATIN SMALL LETTER AE

case 0xE7: {*c=0x87;break;} // ç LATIN SMALL LETTER C WITH CEDILLA

case 0xE8: {*c=0x8A;break;} // è LATIN SMALL LETTER E WITH GRAVE

case 0xE9: {*c=0x82;break;} // é LATIN SMALL LETTER E WITH ACUTE

case 0xEA: {*c=0x88;break;} // ê LATIN SMALL LETTER E WITH CIRCUMFLEX

case 0xEB: {*c=0x89;break;} // ë LATIN SMALL LETTER E WITH DIAERESIS

case 0xEC: {*c=0x8D;break;} // ì LATIN SMALL LETTER I WITH GRAVE

case 0xED: {*c=0xA1;break;} // í LATIN SMALL LETTER I WITH ACUTE

case 0xEE: {*c=0x8C;break;} // î LATIN SMALL LETTER I WITH CIRCUMFLEX

case 0xEF: {*c=0x8B;break;} // ï LATIN SMALL LETTER I WITH DIAERESIS

case 0xF0: {*c=0xD1;break;} // ð LATIN SMALL LETTER ETH

case 0xF1: {*c=0xA4;break;} // ñ LATIN SMALL LETTER N WITH TILDE

case 0xF2: {*c=0x95;break;} // ò LATIN SMALL LETTER O WITH GRAVE

case 0xF3: {*c=0xA2;break;} // ó LATIN SMALL LETTER O WITH ACUTE

case 0xF4: {*c=0x93;break;} // ô LATIN SMALL LETTER O WITH CIRCUMFLEX

case 0xF5: {*c=0xE4;break;} // õ LATIN SMALL LETTER O WITH TILDE

case 0xF6: {*c=0x94;break;} // ö LATIN SMALL LETTER O WITH DIAERESIS

case 0xF7: {*c=0xF6;break;} // ÷ DIVISION SIGN

case 0xF8: {*c=0x9B;break;} // ø LATIN SMALL LETTER O WITH STROKE

case 0xF9: {*c=0x97;break;} // ù LATIN SMALL LETTER U WITH GRAVE

case 0xFA: {*c=0xA3;break;} // ú LATIN SMALL LETTER U WITH ACUTE

case 0xFB: {*c=0x96;break;} // û LATIN SMALL LETTER U WITH CIRCUMFLEX

case 0xFC: {*c=0x81;break;} // ü LATIN SMALL LETTER U WITH DIAERESIS

case 0xFD: {*c=0xEC;break;} // ý LATIN SMALL LETTER Y WITH ACUTE

case 0xFE: {*c=0xE8;break;} // þ LATIN SMALL LETTER THORN

case 0xFF: {*c=0x98;break;} // ÿ LATIN SMALL LETTER Y WITH DIAERESIS

  }

}

*--------------------------------------- Cortar por aquí ---------------------------------------*  

 


[1] http://es.rs-online.com/web/p/products/715-4081/

[2] http://es.rs-online.com/web/p/microcontrolador-procesador/7154072/