June 2010 Archives |
June 16, 2010
As I mentioned in part 1, there are several hardware modifications, as well as software modifications that need to be made. Make sure you have those modifications done to your hardware before proceeding further. To make things easy, I've decided to bundle the TWS library with the modifications to those libraries, as well as with two additional libraries that TWS depends on: Georg Kaindl's EthernetDHCP and Mikal Hart's Flash library. After you download and unzip the package, copy the contents of the directory in the directory where you store your Arduino libraries. The library comes with few examples, look in Basic web serverTo make use of the TWS library, you need to include the following your sketch: #include <Ethernet.h> #include <EthernetDHCP.h> #include <Flash.h> #include <Fat16.h> #include <Fat16util.h> #include <TinyWebServer.h>
TWS is implemented by the An HTTP handler is a simple function that takes as argument a reference to the static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; boolean index_handler(TinyWebServer& web_server) { web_server.send_error_code(200); web_server << F("<html><body><h1>Hello World!</h1></body></html>\n"); return true; } TinyWebServer::PathHandler handlers[] = { // Register the index_handler for GET requests on / {"/", TinyWebServer::GET, &index_handler }, {NULL}, // The array has to be NULL terminated this way }; // Create an instance of the web server. No HTTP headers are requested // by the HTTP request handlers. TinyWebServer web = TinyWebServer(handlers, NULL); void setup() { Serial.begin(115200); EthernetDHCP.begin(mac); web.begin(); } void loop() { EthernetDHCP.maintain(); web.process(); } In the For a complete working example look in Serving files from the SD cardNow that we've seen the basics, let's see how we can extend this web server to serve files stored on the SD card. The idea is to register a handler that serves any URLs. Once the handler is invoked, it interprets the URL path as a file name on the SD card and returns that. boolean file_handler(TinyWebServer& web_server) { char* filename = TinyWebServer::get_file_from_path(web_server.get_path()); if (!filename) { web_server.send_error_code(404); web_server << "Could not parse URL"; } else { TinyWebServer::MimeType mime_type = TinyWebServer::get_mime_type_from_filename(filename); web_server.send_error_code(mime_type, 200); if (file.open(filename, O_READ)) { web_server.send_file(file); file.close(); } else { web_server << "Could not find file: " << filename << "\n"; } free(filename); } return true; } We can now register this in the TinyWebServer::PathHandler handlers[] = { {"/" "*", TinyWebServer::GET, &file_handler }, {NULL}, }; Note how the URL for the HTTP request is specified. We want it to be The Uploading files to the web server and store them on SD card's file systemNow wouldn't it be nice to update Arduino's Web server files using HTTP? This way we can focus on building the actual interface with the hardware, and provide just enough HTTP handlers to interact with it. After we implement a minimal user interface, we can iterate it without having to remove the SD card from the embedded project, copy the HTML, JavaScript and/or image files on a computer, and plug it back in. We could do this remotely from the computer, using a simple script. TinyWebServer provides a simple file upload HTTP handler that uses the HTTP 1.0 Here's how you add file uploads to your Arduino web server: TinyWebServer::PathHandler handlers[] = { // `put_handler' is defined in TinyWebServer {"/upload/" "*", TinyWebServer::PUT, &TinyWebPutHandler::put_handler }, {"/" "*", TinyWebServer::GET, &file_handler }, {NULL}, Note that the order in which you declare the handlers is important. The URLs are matched in the order in which they are declared. This is where the headers array mentioned before comes into picture. The const char* headers[] = { "Content-Length", NULL }; And we now initialize the instance of TinyWebServer web = TinyWebServer(handlers, headers); The The user provided function take 4 parameters. The first is a reference to the Here is a small example of a user provided function that writes the PUT request's content to a file: void file_uploader_handler(TinyWebServer& web_server, TinyWebPutHandler::PutAction action, char* buffer, int size) { static uint32_t start_time; switch (action) { case TinyWebPutHandler::START: start_time = millis(); if (!file.isOpen()) { // File is not opened, create it. First obtain the desired name // from the request path. char* fname = web_server.get_file_from_path(web_server.get_path()); if (fname) { Serial << "Creating " << fname << "\n"; file.open(fname, O_CREAT | O_WRITE | O_TRUNC); free(fname); } } break; case TinyWebPutHandler::WRITE: if (file.isOpen()) { file.write(buffer, size); } break; case TinyWebPutHandler::END: file.sync(); Serial << "Wrote " << file.fileSize() << " bytes in " << millis() - start_time << " millis\n"; file.close(); } } To activate this user provided function, assign its address to void setup() { // ... // Assign our function to `upload_handler_fn'. TinyWebPutHandler::put_handler_fn = file_uploader_handler; // ... } You can now test uploading a file using curl: curl -0 -T index.htm http://my-arduino-ip-address/upload For a complete working example of the file upload and serving web server, look in Advanced topic: persistent HTTP connectionsSometimes it's useful to have an HTTP client start a request. For example, I need to be able to enter an IR learning process. This means that I cannot afford If you noticed the HTTP handlers return a boolean. If the returned value is To obtain the In my remotely controlled projection screen application, I have another handler on June 15, 2010
Hardware modifications: Data Logging shieldThe hardware shields need few modifications in order to work together. The cards were designed to work independently and use the default pins allocated on the hardware SPI bus (the CS, MOSI, MISO and SCK lines on the 10, 11, 12, 13 pins on an Arduino Duemilanove). When stacking the boards together they'd end up in a bus conflict and they won't work. The conflict is solved by having the two boards use different CS pins. They can still share the MOSI, MISO and SCK lines, and if it wasn't for a buggy chip on the Ethernet shield, we'd have ended up using only 5 total digital I/O pins for the whole setup. See below for more info. To make things easy, I chose to use a different CS pin for the Adafruit Data Logging shield: I use pin 9 as the CS pin. For this to work, first make sure you cut out the original trace that goes to pin 10, as in the picture below. ![]() The Data Logging shield board comes unassembled. After you solder all the components on it, run a wire from the CS pin to pin 9, as shown in the picture below. ![]() Hardware modifications: Ethernet shieldThe Ethernet shield uses a Wiznet W5100 chip, which has a buggy hardware SPI implementation. In a post on Adafruit's forum, jaredforshey pointed me to this Arduino playground page which points to an easy way to fix this. The proposed solution disables the chip' SPI part when not in use. This is done by connecting pin 8 to the lower PAD on the board, as shown below. At the same time, make sure you cut the trace leading from pin 8. This bug ends up costing us another pin, for a total of 6 I/O pins for the whole setup.
Software modifications: Fat16 libraryTo read and write files on an SD card, we need to be able to access a file system on the SD card. There are two main file systems used on SD cards: FAT16 and FAT32. The main differences between them are the maximum card sizes supported and more importantly, file naming conventions. FAT16 allows only the old 8.3 DOS file format and cards up to 2GB. Arduino supports both file systems on SD cards using either of these libraries: Fat16 or SdFat. For all its limitations, FAT16' library is smaller that FAT32, so I decided to go with it. Our Data Logging shield uses pin 9 as the CS pin. The FAT16 library assumes the CS pin used in pin 10, so we need to modify that in the code. For Arduino Duemilanove, the definition of Software modifications: Arduino's Ethernet libraryThe Ethernet library shipped with the Arduino 018 package has a bug. In the To fix this problem, I've added two more methods to the int read(uint8_t* ch); int read(uint8_t *buf, size_t size); The first reads a character and puts its value at the address pointed to by int16_t Client::read(uint8_t *ch) { if (!connected() || !available()) { return 0; } return recv(_sock, ch, 1); } int16_t Client::read(uint8_t *buf, uint16_t size) { uint16_t i; for (i = 0; i < size; i++) { if (!read(buf + i)) { break; } } return i; } The second change to the Ethernet library is in |
Cool stuff
Search
More from me
Picture gallery
![]()
Topics
Anteater (2)
Apple (44) Arduino (6) BeagleBone (1) Cars (5) Cocoon (26) Cool gadgets (14) Emacs (24) Google (15) Hardware (9) Java (31) JavaScript (2) Linux (12) Music (4) Open Source (22) People (2) Photo (23) Politics (1) Random (39) Travel (4) Web services (3) Weblogs (30) Work (1) XSLT (12)
Archives
October 2015
January 2015 January 2014 May 2013 June 2011 December 2010 September 2010 August 2010 June 2010 June 2009 May 2009 April 2009 February 2008 November 2007 July 2007 June 2007 May 2007 April 2007 October 2006 August 2006 June 2006 April 2006 March 2006 February 2006 January 2006 December 2005 November 2005 October 2005 September 2005 July 2005 June 2005 April 2005 March 2005 February 2005 October 2004 September 2004 August 2004 June 2004 May 2004 April 2004 March 2004 February 2004 January 2004 December 2003 November 2003 October 2003 September 2003 August 2003 July 2003 June 2003 May 2003 April 2003 March 2003 February 2003 January 2003 December 2002 November 2002 October 2002 September 2002 August 2002 July 2002 June 2002
People
Admin
|