

Monday, 9 February 2015

Make your own Web Server

When we built Sniff and ported it to the Arduino, one of the first pieces of hardware we added support for was the Arduino Ethernet shield, based on the Wiznet w5100 chip. This lets you put an Arduino with sensors on your network, and then query it remotely. One of the easiest ways to do this is to write a web server in Sniff, and then you can display the results in any browser. In release 15 we revisited the code, fixed some bugs and added some additional features, so its probably a good time to write something about it.

Network Configuration

Networking is pretty complex, but the basic premise is that every machine has a unique address, so we can talk to that machine if we know its address. In fact most machines have two addresses - an ethernet or MAC address which is used by the low level hardware to move messages around your local network, and an IP address which is used by the higher level software.

Before we can start networking we need to configure these. For most hardware the MAC address is burnt into it when its manufactured - we don't normally go around changing MAC addresses. Every machine in the world is supposed to have a unique MAC address. However the Arduino doesn't have one, so we need to make one up. While you can probably use pretty much any mac address as long as its not the same as one of the ones already on your network there are websites which will generate you a random one that follows the rules. It should look something like "de:ad:be:ef:fe:ed".

IP addresses are normally allocated using DHCP - when your machine turns on it asks on the network for a free address and gets allocated one. However there's not really enough memory on the Arduino to waste including the code to do that, so we'll do it "old school". We walk down the hall, find the network admin, and ask him to allocate us an IP address. While we're there we should ask for the subnet mask, and the getaway address.

In my case I used
  • IP address:
  • Subnet Mask:
  • Gateway:
These are pretty typical values. The subnet mask tells us that everything on this network has the first three numbers the same - 192.168.0.XXX. I've been allocated number 201. I've also been told that if I want to talk to a machine that's not on the local network machine number 1 can help me. If  you're setting this up at home you can get the subnet mask and gateway from you computers network config (you should use the same as your computer). For the IP address, the last number must be unique.

There's an example program called "ethernetConfig.sniff" that programs these values into an Arduino's eeprom. That means that when we run any other program that uses networking it will use the values we've set. It means we don't have to remember to set them up every time, as getting them wrong can stop the network for other people, so its useful to get them right once. Edit the ethernetConfig program to use the values you've been given, and then run it on the Arduino. If you're doing this with a bunch of boards, you could program them all at the same time with different values, then just forget about it!

Arduino Web Server

Once the config is programmed into the Arduino, we can quickly write a simple server. First we make the necessary device:

make spi device
make ethernet device
make message string
make networkConnected boolean
make networkPort number
make networkPeer string

The ethernet shield uses SPI so we need to make one of those too..

when start
.set networkPort to 80
..repeat until networkConnected
...tell ethernet to "listen"
..say join "Connected:" networkPeer

Our Arduino has an IP address configured into it which allows other computers to find it on the network, but typically "real" computers have many programs running on them. To talk to a specific "service" (program) running on a machine, we use a port number. By default web servers run on port 80, so we set networkPort to 80, and then "listen" until a remote machine tries to connect. Once we're connected the address of the remote machine available in the variable networkPeer -we could use this for logging, security, or even to serve different content to different machines! We just print it out.

In the case of a web server, the next thing that happes is that the web browser sends some text to the server saying what information it would like, so we wait for that to arrive, and print it out:

..repeat until length of message > 0
...tell ethernet to "receive"
..repeat until length of message = 0
...say message
...tell ethernet to "receive"

Strictly we should parse that and see what the browser is asking us, but to keep things simple, lets just print it out, and ignore it! Now its our turn to send a reply. The reply begins with a header:

..set message to "HTTP/1.0 200 OK\r\n"
..tell ethernet to "send"
..set message to "Content-Type: text/plain; charset=UTF-8\r\n\r\n"
..tell ethernet to "send"

This just tells the remote end that we're all good, and we're about to send it the information it asked for. We're going to send it as plain text, but you could send html if you wanted to.

..set message to "The SNIFF Webserver\n"
..tell ethernet to "send"
..set message to join [ timer ] " seconds uptime\n"
..tell ethernet to "send"
..tell ethernet to "disconnect"

To keep things simple, we're just ending a simple message, and the current value of timer - note we're using '\n' to mean a new line. Once that's done we disconnect. You could just as easily check a sensor, trigger some event or simply read files from the SD card, and return the value back.

Now if you go to a web browser and put in the address (or rather the ip address you're using), you should get back something like:

The SNIFF Webserver
5.764 seconds uptime

Other Platforms

Currently the w5100 ethernet driver only works on Arduino, but we hope to get it working on Propeller too.

However if you're on a Unix machine (Mac, Linux, Pi, EV3 with wifi) you already have a network interface. In which case there's an alternative ethernet device that's used by "hosted" systems. It doesn't need any configuration. In terms of the code, start be removing the SPI device, as of course that's not needed.

The other difference is that you probably won't be able to make your code run on port 80. Port numbers less than 1024 are special and only root can use them, so you'll need to run as root if you want to run a web server on port 80. Alternatively you can use more or less any port number greater than 1024 (up to 65535), as long as no one else using it. To access (for example) port 1654 use the url

In the next post we'll look at how you can connect to a remote service.

No comments:

Post a Comment