June 15, 2010

Arduino Tiny Web Server - part 1

Arduino | Hardware | Open Source

Update (December 8, 2010): The below information of the required Arduino hardware is obsolete and left here for informational purposes. Look at this newer post for updated information on the new hardware.


Arduino TinyWebServer is a small and extensible HTTP server implementation designed to run in a limited amount of space on an Arduino Duemilanove. It uses the Ethernet Shield for network connectivity (from Sparkfun or from Adafruit), and the Adafruit Data Logging shield for storage purposes.

Web pages, images and other content can be copied manually on the SD card or uploaded through the HTTP server. The latter allows you to push new versions of the web server's content without the need to remove the card, which can be a pain in embedded applications.

In the first part I present some changes that have to be made to the hardware used and its accompanying software. Part two presents a small open source software library that implements the Arduino TinyWebServer.

Hardware modifications: Data Logging shield

The 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 shield

The 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 library

To 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 SPI_SS_PIN in SdCard.h needs to change from 10 to 9.

Software modifications: Arduino's Ethernet library

The Ethernet library shipped with the Arduino 018 package has a bug. In the Client class in Client.h, the read() method does not differentiate between an 0xFF byte and the Ethernet hardware not having data available. This is not usually a problem if all you serve through the Web server are text files, including HTML. However for any binary file, including images, zip files etc. this however is a problem.

To fix this problem, I've added two more methods to the Client class:

  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 ch. The method returns 1 if it succeeded reading a character, 0 otherwise (as when there is no data available). The second method fills in the value of buf with as many characters as it can, up to size. It returns the number of characters it was able to read, or 0 if none were read. Here is how they're implemented:


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 utility/spi.h, to fix the hardware bug with the Wiznet chip. This change is described on the Arduino playground page.

Posted by ovidiu at June 15, 2010 03:31 PM |
Comments

felicitari pentru articol. Este featured pe hackaday - http://hackaday.com/2010/07/01/arduino-webserver/

Posted by: Cosmin on July 2, 2010 04:20 AM

Wow, nice work!
I'm using the Ethernet shield from DFRobot and it has a working SD / MMC card holder, so a simple (text only) HTTP web server took me about 5 minutes to complete, without any hardware modifications.
I still have to figure out how to load images from the card...

Posted by: iard on July 2, 2010 04:24 PM
Post a comment
Name:


Email Address:


URL:


Comments:


Remember info?



 
Copyright © 2002-2011 Ovidiu Predescu.