├── README.md ├── SDWebBrowse.ino └── SDWebBrowse.pde /README.md: -------------------------------------------------------------------------------- 1 | # SDWebBrowse 2 | File browsing using the Arduino Ethernet shield 3 | 4 | ## This repository has been archived 5 | 6 | The files in this repository accompanied the [Adafruit Learning System](https://learn.adafruit.com) tutorial 7 | Arduino Ethernet + SD Card and are now at [https://github.com/adafruit/Adafruit_Learning_System_Guides/tree/master/Arduino_Ethernet_SD_Card](https://github.com/adafruit/Adafruit_Learning_System_Guides/tree/master/Arduino_Ethernet_SD_Card) 8 | 9 | Tutorial: Arduino Ethernet + SD Card [https://learn.adafruit.com/arduino-ethernet-sd-card/](https://learn.adafruit.com/arduino-ethernet-sd-card/) 10 | 11 | All code MIT License, please keep attribution to Adafruit Industries, Limor Fried 12 | 13 | Please consider buying your parts at [Adafruit.com](https://www.adafruit.com) to support open source code. 14 | -------------------------------------------------------------------------------- /SDWebBrowse.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * USERS OF ARDUINO 0023 AND EARLIER: use the 'SDWebBrowse.pde' sketch... 3 | * 'SDWebBrowse.ino' can be ignored. 4 | * USERS OF ARDUINO 1.0 AND LATER: **DELETE** the 'SDWebBrowse.pde' sketch 5 | * and use ONLY the 'SDWebBrowse.ino' file. By default, BOTH files will 6 | * load when using the Sketchbook menu, and the .pde version will cause 7 | * compiler errors in 1.0. Delete the .pde, then load the sketch. 8 | * 9 | * I can't explain WHY this is necessary, but something among the various 10 | * libraries here appears to be wreaking inexplicable havoc with the 11 | * 'ARDUINO' definition, making the usual version test unusable (BOTH 12 | * cases evaluate as true). FML. 13 | */ 14 | 15 | /* 16 | * This sketch uses the microSD card slot on the Arduino Ethernet shield 17 | * to serve up files over a very minimal browsing interface 18 | * 19 | * Some code is from Bill Greiman's SdFatLib examples, some is from the 20 | * Arduino Ethernet WebServer example and the rest is from Limor Fried 21 | * (Adafruit) so its probably under GPL 22 | * 23 | * Tutorial is at http://www.ladyada.net/learn/arduino/ethfiles.html 24 | * Pull requests should go to http://github.com/adafruit/SDWebBrowse 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | /************ ETHERNET STUFF ************/ 33 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 34 | byte ip[] = { 192, 168, 0, 80 }; 35 | EthernetServer server(80); 36 | 37 | /************ SDCARD STUFF ************/ 38 | Sd2Card card; 39 | SdVolume volume; 40 | SdFile root; 41 | SdFile file; 42 | 43 | // store error strings in flash to save RAM 44 | #define error(s) error_P(PSTR(s)) 45 | 46 | void error_P(const char* str) { 47 | PgmPrint("error: "); 48 | SerialPrintln_P(str); 49 | if (card.errorCode()) { 50 | PgmPrint("SD error: "); 51 | Serial.print(card.errorCode(), HEX); 52 | Serial.print(','); 53 | Serial.println(card.errorData(), HEX); 54 | } 55 | while(1); 56 | } 57 | 58 | void setup() { 59 | Serial.begin(9600); 60 | 61 | PgmPrint("Free RAM: "); 62 | Serial.println(FreeRam()); 63 | 64 | // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with 65 | // breadboards. use SPI_FULL_SPEED for better performance. 66 | pinMode(10, OUTPUT); // set the SS pin as an output (necessary!) 67 | digitalWrite(10, HIGH); // but turn off the W5100 chip! 68 | 69 | if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed!"); 70 | 71 | // initialize a FAT volume 72 | if (!volume.init(&card)) error("vol.init failed!"); 73 | 74 | PgmPrint("Volume is FAT"); 75 | Serial.println(volume.fatType(),DEC); 76 | Serial.println(); 77 | 78 | if (!root.openRoot(&volume)) error("openRoot failed"); 79 | 80 | // list file in root with date and size 81 | PgmPrintln("Files found in root:"); 82 | root.ls(LS_DATE | LS_SIZE); 83 | Serial.println(); 84 | 85 | // Recursive list of all directories 86 | PgmPrintln("Files found in all dirs:"); 87 | root.ls(LS_R); 88 | 89 | Serial.println(); 90 | PgmPrintln("Done"); 91 | 92 | // Debugging complete, we start the server! 93 | Ethernet.begin(mac, ip); 94 | server.begin(); 95 | } 96 | 97 | void ListFiles(EthernetClient client, uint8_t flags) { 98 | // This code is just copied from SdFile.cpp in the SDFat library 99 | // and tweaked to print to the client output in html! 100 | dir_t p; 101 | 102 | root.rewind(); 103 | client.println(""); 154 | } 155 | 156 | // How big our line buffer should be. 100 is plenty! 157 | #define BUFSIZ 100 158 | 159 | void loop() 160 | { 161 | char clientline[BUFSIZ]; 162 | int index = 0; 163 | 164 | EthernetClient client = server.available(); 165 | if (client) { 166 | // an http request ends with a blank line 167 | boolean current_line_is_blank = true; 168 | 169 | // reset the input buffer 170 | index = 0; 171 | 172 | while (client.connected()) { 173 | if (client.available()) { 174 | char c = client.read(); 175 | 176 | // If it isn't a new line, add the character to the buffer 177 | if (c != '\n' && c != '\r') { 178 | clientline[index] = c; 179 | index++; 180 | // are we too big for the buffer? start tossing out data 181 | if (index >= BUFSIZ) 182 | index = BUFSIZ -1; 183 | 184 | // continue to read more data! 185 | continue; 186 | } 187 | 188 | // got a \n or \r new line, which means the string is done 189 | clientline[index] = 0; 190 | 191 | // Print it out for debugging 192 | Serial.println(clientline); 193 | 194 | // Look for substring such as a request to get the root file 195 | if (strstr(clientline, "GET / ") != 0) { 196 | // send a standard http response header 197 | client.println("HTTP/1.1 200 OK"); 198 | client.println("Content-Type: text/html"); 199 | client.println(); 200 | 201 | // print all the files, use a helper to keep it clean 202 | client.println("

Files:

"); 203 | ListFiles(client, LS_SIZE); 204 | } else if (strstr(clientline, "GET /") != 0) { 205 | // this time no space after the /, so a sub-file! 206 | char *filename; 207 | 208 | filename = clientline + 5; // look after the "GET /" (5 chars) 209 | // a little trick, look for the " HTTP/1.1" string and 210 | // turn the first character of the substring into a 0 to clear it out. 211 | (strstr(clientline, " HTTP"))[0] = 0; 212 | 213 | // print the file we want 214 | Serial.println(filename); 215 | 216 | if (! file.open(&root, filename, O_READ)) { 217 | client.println("HTTP/1.1 404 Not Found"); 218 | client.println("Content-Type: text/html"); 219 | client.println(); 220 | client.println("

File Not Found!

"); 221 | break; 222 | } 223 | 224 | Serial.println("Opened!"); 225 | 226 | client.println("HTTP/1.1 200 OK"); 227 | client.println("Content-Type: text/plain"); 228 | client.println(); 229 | 230 | int16_t c; 231 | while ((c = file.read()) > 0) { 232 | // uncomment the serial to debug (slow!) 233 | //Serial.print((char)c); 234 | client.print((char)c); 235 | } 236 | file.close(); 237 | } else { 238 | // everything else is a 404 239 | client.println("HTTP/1.1 404 Not Found"); 240 | client.println("Content-Type: text/html"); 241 | client.println(); 242 | client.println("

File Not Found!

"); 243 | } 244 | break; 245 | } 246 | } 247 | // give the web browser time to receive the data 248 | delay(1); 249 | client.stop(); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /SDWebBrowse.pde: -------------------------------------------------------------------------------- 1 | /* 2 | * USERS OF ARDUINO 0023 AND EARLIER: use the 'SDWebBrowse.pde' sketch... 3 | * 'SDWebBrowse.ino' can be ignored. 4 | * USERS OF ARDUINO 1.0 AND LATER: **DELETE** the 'SDWebBrowse.pde' sketch 5 | * and use ONLY the 'SDWebBrowse.ino' file. By default, BOTH files will 6 | * load when using the Sketchbook menu, and the .pde version will cause 7 | * compiler errors in 1.0. Delete the .pde, then load the sketch. 8 | * 9 | * I can't explain WHY this is necessary, but something among the various 10 | * libraries here appears to be wreaking inexplicable havoc with the 11 | * 'ARDUINO' definition, making the usual version test unusable (BOTH 12 | * cases evaluate as true). FML. 13 | */ 14 | 15 | /* 16 | * This sketch uses the microSD card slot on the Arduino Ethernet shield 17 | * to serve up files over a very minimal browsing interface 18 | * 19 | * Some code is from Bill Greiman's SdFatLib examples, some is from the 20 | * Arduino Ethernet WebServer example and the rest is from Limor Fried 21 | * (Adafruit) so its probably under GPL 22 | * 23 | * Tutorial is at http://www.ladyada.net/learn/arduino/ethfiles.html 24 | * Pull requests should go to http://github.com/adafruit/SDWebBrowse 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | /************ ETHERNET STUFF ************/ 33 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 34 | byte ip[] = { 192, 168, 1, 177 }; 35 | Server server(80); 36 | 37 | /************ SDCARD STUFF ************/ 38 | Sd2Card card; 39 | SdVolume volume; 40 | SdFile root; 41 | SdFile file; 42 | 43 | // store error strings in flash to save RAM 44 | #define error(s) error_P(PSTR(s)) 45 | 46 | void error_P(const char* str) { 47 | PgmPrint("error: "); 48 | SerialPrintln_P(str); 49 | if (card.errorCode()) { 50 | PgmPrint("SD error: "); 51 | Serial.print(card.errorCode(), HEX); 52 | Serial.print(','); 53 | Serial.println(card.errorData(), HEX); 54 | } 55 | while(1); 56 | } 57 | 58 | void setup() { 59 | Serial.begin(9600); 60 | 61 | PgmPrint("Free RAM: "); 62 | Serial.println(FreeRam()); 63 | 64 | // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with 65 | // breadboards. use SPI_FULL_SPEED for better performance. 66 | pinMode(10, OUTPUT); // set the SS pin as an output (necessary!) 67 | digitalWrite(10, HIGH); // but turn off the W5100 chip! 68 | 69 | if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed!"); 70 | 71 | // initialize a FAT volume 72 | if (!volume.init(&card)) error("vol.init failed!"); 73 | 74 | PgmPrint("Volume is FAT"); 75 | Serial.println(volume.fatType(),DEC); 76 | Serial.println(); 77 | 78 | if (!root.openRoot(&volume)) error("openRoot failed"); 79 | 80 | // list file in root with date and size 81 | PgmPrintln("Files found in root:"); 82 | root.ls(LS_DATE | LS_SIZE); 83 | Serial.println(); 84 | 85 | // Recursive list of all directories 86 | PgmPrintln("Files found in all dirs:"); 87 | root.ls(LS_R); 88 | 89 | Serial.println(); 90 | PgmPrintln("Done"); 91 | 92 | // Debugging complete, we start the server! 93 | Ethernet.begin(mac, ip); 94 | server.begin(); 95 | } 96 | 97 | void ListFiles(Client client, uint8_t flags) { 98 | // This code is just copied from SdFile.cpp in the SDFat library 99 | // and tweaked to print to the client output in html! 100 | dir_t p; 101 | 102 | root.rewind(); 103 | client.println(""); 154 | } 155 | 156 | // How big our line buffer should be. 100 is plenty! 157 | #define BUFSIZ 100 158 | 159 | void loop() 160 | { 161 | char clientline[BUFSIZ]; 162 | int index = 0; 163 | 164 | Client client = server.available(); 165 | if (client) { 166 | // an http request ends with a blank line 167 | boolean current_line_is_blank = true; 168 | 169 | // reset the input buffer 170 | index = 0; 171 | 172 | while (client.connected()) { 173 | if (client.available()) { 174 | char c = client.read(); 175 | 176 | // If it isn't a new line, add the character to the buffer 177 | if (c != '\n' && c != '\r') { 178 | clientline[index] = c; 179 | index++; 180 | // are we too big for the buffer? start tossing out data 181 | if (index >= BUFSIZ) 182 | index = BUFSIZ -1; 183 | 184 | // continue to read more data! 185 | continue; 186 | } 187 | 188 | // got a \n or \r new line, which means the string is done 189 | clientline[index] = 0; 190 | 191 | // Print it out for debugging 192 | Serial.println(clientline); 193 | 194 | // Look for substring such as a request to get the root file 195 | if (strstr(clientline, "GET / ") != 0) { 196 | // send a standard http response header 197 | client.println("HTTP/1.1 200 OK"); 198 | client.println("Content-Type: text/html"); 199 | client.println(); 200 | 201 | // print all the files, use a helper to keep it clean 202 | client.println("

Files:

"); 203 | ListFiles(client, LS_SIZE); 204 | } else if (strstr(clientline, "GET /") != 0) { 205 | // this time no space after the /, so a sub-file! 206 | char *filename; 207 | 208 | filename = clientline + 5; // look after the "GET /" (5 chars) 209 | // a little trick, look for the " HTTP/1.1" string and 210 | // turn the first character of the substring into a 0 to clear it out. 211 | (strstr(clientline, " HTTP"))[0] = 0; 212 | 213 | // print the file we want 214 | Serial.println(filename); 215 | 216 | if (! file.open(&root, filename, O_READ)) { 217 | client.println("HTTP/1.1 404 Not Found"); 218 | client.println("Content-Type: text/html"); 219 | client.println(); 220 | client.println("

File Not Found!

"); 221 | break; 222 | } 223 | 224 | Serial.println("Opened!"); 225 | 226 | client.println("HTTP/1.1 200 OK"); 227 | client.println("Content-Type: text/plain"); 228 | client.println(); 229 | 230 | int16_t c; 231 | while ((c = file.read()) > 0) { 232 | // uncomment the serial to debug (slow!) 233 | //Serial.print((char)c); 234 | client.print((char)c); 235 | } 236 | file.close(); 237 | } else { 238 | // everything else is a 404 239 | client.println("HTTP/1.1 404 Not Found"); 240 | client.println("Content-Type: text/html"); 241 | client.println(); 242 | client.println("

File Not Found!

"); 243 | } 244 | break; 245 | } 246 | } 247 | // give the web browser time to receive the data 248 | delay(1); 249 | client.stop(); 250 | } 251 | } 252 | --------------------------------------------------------------------------------