Building an Arduino Web Server

Oliver Higgins

Issue 1, July 2017

This article includes additional downloadable resources.
Please log in to access.

Log in

Web server technology is behind much of the modern world. However you don't need a whole lot of computing power to achieve great things.

A web server is a program that delivers data to a client machine via the Hyper Text Transfer Protocol (HTTP). This program serves data to users (most commonly referred to as a web page), in response to the request sent by their web browser (client machine). Dedicated devices exist to purely service this purpose. 

If you’re serving a lot of data from a web server, then an Arduino may not actually be the best choice. Web servers can be built from something like a small computer (Raspberry Pi or small PC) or scale up to a fully-fledged multi-processor web server used in data centres around the world. However for many simple hardware devices, web server technology allows us to send and receive all sorts of data using WiFi or ethernet connectivity, which is in just about every home and office (especially for the avid DIYODE reader!).


We're creating a lightweight web server which can respond to your requests for information, and provide General Purpose Input Output (GPIO) hardware information data to screen. We'll use a temperature/humidity sensor in this demonstration, but you could use any sensing module, or even just a jumper-wire from 5V to one of the GPIO pins.


The web server is an example of the server/client process. Commonly used programs that deliver your content is Apache, Microsoft Information exchange, and nginx. The web server forms part of a larger internet suite, or collection of protocols. This includes but is not limited to FTP, DNS, and IMAP, which you probably use every day (perhaps without even realising it).

How it works
Request Page

When you open your web browser and type a website address into your address bar, the web browser takes this information and sends the request to their server, which is serving the “page” that you wish to view. Their server receives this request for information, along with your return address. It then puts together the requested information, as well as information on how to build and display the page. There are visual and non-visual parts to this response. Firstly there is an amount of “header” data that is required to build this page. Primarily this information tells your web browser (client) what type of information you are viewing, the language or “encoding” that the page may be in, and even how to display the text or images on the page. While HTML is the most common way to format a response for "human consumption", many other solutions exist for content designed for machine-handling. But we'll cover those another time.

When dealing with HTML (or at least, a HTTP request), the format of this return data typically follows this pattern:

HEADER: What the information is, and how is it encoded.

METADATA: Important elements of the page, but are not displayed.

CONTENT: The visible portion. In its most common form, this is Hyper Text Markup Language (HTML) and is a sequence of tags that tells the browser how to display the text or image.

It is important to note in the understanding of how we will shape the responses for our web server, that pages can be served statically or dynamically. A static page is a basic file that is sent to the browser each time, which says the same thing (see example). A dynamic page has content that changes with each page request. This requires the server to do some preprocessing of the information, then assemble your page before serving it. In the context of this project, it may be reading a pin or sensor, and then relaying its status to the user, via the web page.

With the IoT, a lot of “things” now have web servers built in, providing a simple and low cost way of allowing the user to determine settings and supply valuable data to the device. Some examples would be your home router, televisions, set-top boxes, even some cars! What this means is we can provide a powerful user experience with one simple shield.

Using the Arduino compatible ethernet shield, it is easy to make your web server. This could be used for a simple way of viewing the temperature from wherever your Arduino is located, to a full-blown home automation setup. Need to switch on the front veranda light? Want to check the ambient light at home? Do the plants need watering? Whatever sensor you can read from, you can make a website direct from your Arduino to reflect that. What about a simple robotics interface? Move forward, move left, move back… don’t trust the automated system!

HTML 101: Giving Text Structure

HTML is basic markup text. It’s not a programming language, it is used to mark-up or format text for presentation. This is done through a series of tags, defined by an angle bracket and then the type of mark-up we want to change, and then another angle bracket. For example: <b> is the opening tag to make text bold. Next, would come to the text you want to appear bolded (e.g. “This is bold”) and then use the closing tag </b>.

When setting up your server, you will be sending the above code to the client’s browser. The browser interprets this information and then displays it as you have requested.

<!DOCTYPE html> 
    <title>Page Title</title> 
    <h1>This is a Heading</h1> 
    <b>This is Bold</b> 
    <i>This is Italics</i> 
    <p>This is a paragraph.</p> 
BROWSER OUTPUT: Browser Output


1 x Arduino Uno R3 or Compatible Board XC-4410 Z6240
1 x Ethernet Shield XC-4412 Z6242
1 x LED ZD-0100 Z0800
1 x 2200 Resistor RR-0556 R7038
1 x DHT11 (3 pin breakout) XC-4520 Z6240

You'll also need prototyping hardware such as a breadboard and jumper wires, power supply, and other standard equipment.


Align the ethernet shield to the Uno board and connect them to each other. Plug the Arduino board to your computer via USB, and connect an ethernet cable to the RJ45 port on the ethernet shield. You can compile the code without an ethernet connection present, but the web server may fail to boot.


MAC ADDRESS: You need to manually assign a MAC (Media Access Control) Address. Technically, all MAC addresses should be unique; however, the likelihood of two devices having the same address on the same network at the same time, is very slim. So feel free to use the MAC address included in the code.

IP ADDRESS: The IP Address is a unique identifier on a network, so machines can talk to each other. You can setup a static IP (ie, one that never changes), however most networks use a DHCP (Dynamic Host Control Protocol) to automatically assign you one. This is the fastest way to avoid an IP clash (two devices with the same address). However they can change from time to time. If you’re setting up for permanent use, you should use one of several methods to ensure it doesn’t change - but we’ll cover that another time.

A NOTE ABOUT THE SD SLOT: This example does not require an SD card. If an SD card is inserted but not used, it is possible for the sketch to hang, because pin 4 is used as SS (active low) of the SD, and when not used it is configured as INPUT by default. If this occurs, there are two possible solutions: 1. Remove the SD card 2. Add these lines of code in the setup function: pinMode(4, OUTPUT); digitalWrite(4, HIGH);


We start by importing the ethernet library, which includes the code required to make the server work. We also include SPI.h, which is the library for the SD card that is included with the ethernet shield.

There are a few principles here which also apply to WiFi networks. While the libraries required will be different, the network principles shown here are the same. MAC address, IP address, and server ports are all still required, whether it's a WiFi or wired ethernet connection.

As a result, once you're familiar with the basic concepts we have explained here, you could easily transfer the skills, expanding your Web Server know-how to a WiFi-enabled web server, opening up even more options.

#include <SPI.h> 
#include <Ethernet.h>
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED 
IPAddress ip(192, 168, 2, 101);

80 is the default port used for HTTP (web pages)

EthernetServer server(80);
void setup() { 
  Ethernet.begin(mac, ip); //This sets the MAC and IP address of the Server 
  server.begin(); //Starts the server 
void loop() { 
  EthernetClient client = server.available(); 
  if (client) { 
    boolean currentLineIsBlank = true; 
    while (client.connected()) { 
      if (client.available()) { 
        char c =; 
        if (c == ‘n’ && currentLineIsBlank) { 
        client.println("HTTP/1.1 200 OK"); 
        client.println("Content-Type: text/html"); 
        client.println("Connection: close"); 
        client.println("Refresh: 5"); 
        client.println("<!DOCTYPE HTML>"); 
        client.print("<p>Hello World</p>"); 
      if (c == ‘n’) { 
        currentLineIsBlank = true; 
      } else if (c != ‘r’) { 
        currentLineIsBlank = false; 

This will give you a complete test case to make sure everything is working. Compile the code onto your Arduino board, and you should see it boot through serial monitor. Open up the IP address shown in your favourite web browser and you should see something like:

Hello World

This may seem like a very simple output for a few dozen lines of code, but there is quite alot happening here. Much of the content is also "blind" to the user, which is hidden data sent in the form of header data. This provides the web browser software useful information on the type of content it's receiving.


From this point on, you only really need to change/add or subtract some basic parts to output more information to the webpage that you have created. Just copy and paste the client.print(“<p>Hello World</p>”); to add another line. However we want this to actually do something. This code refreshes every five seconds, so let’s make the LED flash each time.

Unfortunately the ethernet shield uses the inbuilt LED on pin 13. This means that we need to hook up a basic LED with 220Ω resistor to pin 7, to see this real world example. Add the following lines of code to the sketch:

  const int outputLED = 7; 
  pinMode(outputLED, OUTPUT); 
  digitalWrite(outputLED, HIGH); // turns it on

This does not really do much other the turn an LED on, when the server is on, but it has showed us how simple it is to use the Ethernet, and how to drive a basic output.

While simple on the face of it, you now effectively have an ethernet-powered switch. You can use the output to drive a relay output, trigger other systems, and so much more. This is effectively a one-way action, with no feedback. But keep reading - we can do that too!

Diagram 2


The next example reads data from the DHT11 temperature and humidity sensor, making it a bit more useful. For this example we're using a basic three-pin sensor that will work with the simpleDHT library. It is connected on pin 2.

The DHT11 sensor's library isn't included by default, so you'll need to install it. We've included it in the digital resources along with the Arduino sketch files.

Note that some of these modules are three-pin, others are four-pin. It only uses three pins (5V, data, GND). If you have a four-pin version, check the data sheet to find out which pin isn't used, and adjust from our wiring diagram accordingly.

Current temp


Add this to head:

#include <SimpleDHT.h> //DHT sesnor Lib 
int pinDHT11 = 2; 
SimpleDHT11 dht11;


byte temperature = 0; 
byte humidity = 0; 
if (, &temperature, &humidity, NULL)) 

  Serial.print("Read DHT11 failed."); 

// DHT11 sampling rate is 1HZ. 


client.print(" *C, "); 
client.println(" %");
Current temp


Finally, what if we need to interact with your Arduino board and turn a pin on or off? While this is a little more complex and requires some URL processing by the Arduino, it can be done with relative ease.

This sketch uses a modified version of the Model View Controller (MVC) framework. The MVC allows programmers a way of modularising the code, enabling users to have just one interface in the data structures (model) of their program. In the context of this project, the model is the LED that is being switched on or off. Separating the view element of the code enables us to create the interface the user sees, independently to the general code of the Arduino. With regards to our code in this sketch, the “View” is the dashboard function.


Perhaps this is a much larger project and so you’re working with another team member; and it’s one person’s job to program the data and another person’s to create the interface. Provided it is modelled correctly, this task can be broken down into smaller files and elements, to allow for independent working.

This final sketch creates the basic Arduino loop and calls the dashboard function to create the user interface. Upon clicking on the buttons the Ardunio reads the returning URL string to then run it through a series of “if” statements to determine the course of action. In this case, we are simply changing the state of one of the output pins, based upon the input. This could easily be used to turn on relays to active lights or watering systems - the possibilities for expansion of this are endless. This idea also forms the basis of the learning infrared remote project, elsewhere in this issue. It contains multiple ways to change the outcome and call functions based upon the user's requests.


To test this project we need to connect an LED to pin 7. Open Webserver_LHD_ON_OFF.ino and change your IP and MAC as required. Upload to your Arduino board and check the serial monitor to ensure you get the server is at 10.x.x.x. Once complete, go to your web browser and type the IP address that you have selected. You will be greeted with the following:


Click the on/off button to turn the LED off.


This is just a simple primer. It does not include a deep level of knowledge that you could achieve by using a web server. However, our infrared project in this issue uses the fundamentals of this web server for control. If this device is internet facing, there are security considerations beyond the scope of this article. But this should get you running within your local network for some fun IoT experimentation!

The next iteration to this would be using the more expensive Wi-Fi shield to gain wireless access to you project. Other project ideas include using SQL to insert data into a database live, rather than local data logging. Or what about making your project tweet or provide live interfaces through services such as Temboo?