Introduction
Here's a dead simple web server for the Arduino with Wiznet 5100 Ethernet shield. You can load this sketch and begin receiving requests immediately!
- Implements IPV4. This example uses TCP/IP.
- Listens on port 80, the standard TCP port for unencrypted HTTP traffic. The device responds at http://192.168.1.23/.
- The default gateway is disabled by default. This means the device will only respond to requests from your local/private network.
- If for some reason you want it to respond to Internet requests, you can map a port through your router and set the default gateway to 192.168.1.1. The device will then respond through the router. For more info, do a search for "port address translation."
- Client requests and response codes are written to the console with 115200 baud.
- This web server is appropriate for text only, so any images must be hosted on a REAL web server (or use a more powerful Arduino).
- Keep realistic expectations. If your project exceeds basic automation requirements, consider a full embedded system or server operating system.
by Mark Scammacca
What You Need
(Total cost about $10)
- Arduino Uno - These are available everywhere. Find one on eBay for a few bucks.
Important: Pick one that has the micro USB connector so that the Wiznet 5100 ethernet shield will fit over it easily. -
Wiznet 5100 Ethernet Shield for Arduino - these are all over eBay as well.
Optional: Pick one that is POE (Power over Ethernet) capable. The POE board must be purchased separately and soldered on, but then your POE switch can power all of your Arduino devices directly from the Ethernet cable.
Important: There are other types of ethernet shields, and this code works only with the Wiznet compatible ones. But the general principal will work with some minor modifications with any ethernet shield.
- Arduino development software (free) - Install it completely free. There are no dependencies required for this project besides what is already in the development environment. No chip programmer needed - programming is done over USB. The USB port also acts as a serial port when the Arduino software is installed, so you can monitor the console output from this code with Putty or through the built in serial console in the Arduino software.
Power over Ethernet
The Wiznet 5100 shield is available with Power over Ethernet, but this requires an add-on board, purchased separately. Some models have the solder connections available to accept the POE add-on board, and some do not. Check carefully. I have seen them listed as "POE Ethernet Shields" but they mean "POE capable, sold separately." The device can then be powered by a POE switch or a POE injector.
If you are more curious about POE and picking the right kind of switch, there are three main standards to be aware of:
- IEEE 802.3af-2003
- IEEE 802.3at-2009 (more power and should be backwards compatible with af devices)
- Proprietary - usually cheapest, and sometimes incompatible with 802.3af or 802.3at devices, having smokey results. Sometimes includes a matching terminator.
Also look into POE splitters. These simplify connecting non-POE devices to POE switches because they provide a barrel plug output. The POE splitter takes the power off the line and supplies it via barrel plug to the device. They accept 48V POE power and reduce it to 5VDC, 12VDC, etc. I have had great luck with them.
Security Considerations
The security of this code has not been reviewed -- so connecting it directly to the Internet is probably a BAD idea. The default gateway is set to 0.0.0.0 for this reason. If you choose to connect it to your router, set the default gateway to 192.168.1.1 (or whatever your router's IP address is) and Google "Port Address Translation".
Do not use this code in a real product without carefully securing the device against hacking. People commonly hack IoT devices once they capture enough attention to make hacking them worthwhile.
Using the Code
After installing Arduino software, simply connect the USB and load this sketch to your Arduino device. The device and Ethernet shield can be powered by USB.
The requests are handled in the void respond()
function.
The default page is just a splash page with a hit counter: http://192.168.1.23/
The test page shows you how to pass in args and write them back in the response: http://192.168.1.23/test.html
Each request must be handled in full before the Arduino can respond to the next request, so make them short.
(Just highlight the code and copy/paste. Do not use the "Copy code" button or the HTML elements will be broken when you paste.)
#include <SPI.h>
#include <Ethernet.h>
char p_buffer[150];
#define P(str) (strcpy_P(p_buffer, PSTR(str)), p_buffer)
byte mac[] = { 0xAD, 0xDE, 0xEF, 0xBB, 0xFD, 0xDD };
IPAddress ip (192, 168, 1, 23); IPAddress myDns (192, 168, 1, 1); IPAddress gateway (0, 0, 0, 0); IPAddress subnet (255, 255, 255, 0);
EthernetServer server(80);
void setup()
{
Ethernet.begin(mac, ip);
server.begin();
Serial.begin(115200);
}
char bufferUrl[256];
char bufferArgs[512];
int urlChars = 0;
int argChars = 0;
int lineChars = 0;
long requests = 0;
int state = 0;
void loop()
{
EthernetClient client = server.available();
if (client)
{
state = 0;
urlChars = 0;
argChars = 0;
lineChars = 0;
bufferUrl[0] = 0;
bufferArgs[0] = 0;
while (client.connected())
{
if (client.available())
{
char c = client.read();
Serial.print(c);
if (c == '\r')
continue;
if (state == 0 && c == '/')
{
state = 1;
}
else if (state == 1 && c == '?')
{
state = 2;
}
else if ((state == 1 || state == 2) && c == ' ')
{
state = 3;
}
else if (state == 1 && urlChars < 255)
{
bufferUrl[urlChars++] = c;
bufferUrl[urlChars] = 0;
}
else if (state == 2 && argChars < 511)
{
bufferArgs[argChars++] = c;
bufferArgs[argChars] = 0;
}
else if (state == 3 && c == '\n' && lineChars == 0)
{
state = 4;
}
if (c == '\n')
lineChars = 0;
else
lineChars++;
if(state == 4)
{
state = 5;
requests++;
Serial.print(P("Request # "));
Serial.print(requests);
Serial.print(P(": "));
Serial.println(bufferUrl);
respond(client);
break;
}
}
}
client.flush();
client.stop();
}
}
void respond(EthernetClient client)
{
if (strcmp(bufferUrl, P("")) == 0)
{
sendHttpResponseOk(client);
client.println(P("<HTML><head><title>Welcome</title></head><body><h1>Welcome, visitor"));
client.print(requests);
client.println(P("!</h1>Click here to visit the <a href=/test.html>Test Page</a><p>"));
client.println(P("String output is stored in progmem to conserve RAM.
That's what all the P( ) stuff is about. Buffer is big enough for 149 chars at once. "));
client.println(P("Be careful not to exceed!<p><font color=red>
This web server is not secured for public access. Use at your own risk.</font> "));
client.println(P("If you want to use this in an actual product,
at least leave the gateway IP setting disabled.
You should consider a design where the Arduino acts"));
client.println(P("as the client, or maybe a design where the Arduino
can only be contacted by a more fully-secured server.
<p>Requests are echoed to the console"));
client.println(P("with baud rate of 115200. Have fun!<p>
<img src='https://cdn.meme.am/instances/250x250/54595677.jpg'/></body></html>"));
}
else if (strcmp(bufferUrl, P("test.html")) == 0)
{
sendHttpResponseOk(client);
client.println(P("<HTML><head><title>Test Page</title>
</head><body><h1>Test Page</h1>"));
client.println(P("<br><b>Resource:</b> "));
client.println(bufferUrl);
client.println(P("<br><b>Arguments:</b> "));
client.println(bufferArgs);
client.println(P("<br><br><form action='/test.html?'
method='GET'>Test arguments: <input type=text name='arg1'/>
<input type=submit value='GET'/></form>"));
client.println(P("</body></html>"));
}
else
{
sendHttp404(client);
client.println(P("<HTML><head><title>Resource not found
</title></head><body><h1>The requested resource was not found</h1>"));
client.println(P("<br><b>Resource:</b> "));
client.println(bufferUrl);
client.println(P("<br><b>Arguments:</b> "));
client.println(bufferArgs);
client.println(P("</body></html>"));
}
}
void sendHttpResponseOk(EthernetClient client)
{
Serial.println(P("200 OK"));
Serial.println();
client.println(P("HTTP/1.1 200 OK"));
client.println(P("Content-Type: text/html"));
client.println(P("Connnection: close")); client.println();
}
void sendHttp404(EthernetClient client)
{
Serial.println(P("404 Not Found"));
Serial.println();
client.println(P("HTTP/1.1 404 Not Found"));
client.println(P("Content-Type: text/html"));
client.println(P("Connnection: close")); client.println();
}
Example Output
That's It!
You can read or manipulate digital IO pins or do anything you would normally do.
Memory Usage Note
Since strings take up valuable RAM space, I've referenced a Macro which stores and retrieves these from PROGMEM. This allows you to make fairly large web pages for such a tiny processor (shame on you!) and store them in abundant flash memory without sacrificing all of your RAM.
The trade-off is that you MUST remember to limit each line of text to 149 chars or less, or the buffer which reads the strings from PROGMEM will overflow, and your code will have strange bugs.
If you require more memory, consider an ATmega or something beefier, or an SD Card (there are other implementations of HTTP for Arduino which use this).
History
- 20th May, 2017: Initial version