Basic Arduino Web Server

This tutorial goes through the steps to making your own Arduino web server.

Arduino Web Server

Setting up a web server on the Arduino is a straightforward process that covers the process of getting it up and running. I will also cover some core concepts such as using Ajax to update rather than refreshing the page over and over.

You will need to know some basic HTML which is incredibly easy to grasp the concepts of. If you have never done any HTML, then I recommend hitting up w3schools for a basic introduction.

This server won’t be able to do anything too fancy, but it’s incredibly useful for anyone who wants to display data from devices connected to the pins or would like to interact with a circuit from a webpage. The more powerful Raspberry Pi web server might interest you if this doesn’t really cover what you’re after.

All the code examples can be found over at my Github and can be easily downloaded (They’re also commented). If you notice any mistakes, please be sure to let me know in the comments section at the bottom of this page.

Equipment

The equipment that you will need for this Arduino web server tutorial is listed below.

Recommended

** The SD card will need to be formatted in either FAT16 or FAT32.

Code Basics

If you’re new to coding or interested in some of the basics being explained, then read on otherwise you can skip this section and go down to setting up the web server with or without an SD card section.

Initialisation

To begin, you will first need to initialize the web server by defining a few variables then calling a function.

Firstly, you need to define a mac address, the one below should be fine on most home networks. If you know exactly what you’re after, then don’t hesitate to change it.

byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};

Secondly, you will need to define an IP address, this is done by simply calling the IPAddress class with your chosen IP. Make sure you update this to an IP address that won’t conflict on your network.

IPAddress ip(192, 168, 1, 177);

Thirdly, you will need to set the port number that you would like the server to listen on. By default, plain HTTP runs over port 80. If you change this, then you will need to define the port when you go to access the web page in your browser. For example, if your port was 14213, then the address will look like this: 192.168.1.177:14213

EthernetServer server(80);

Lastly, in the setup function, you will need to initialize the Ethernet device. Once this is initialized simply make a call to server.begin(), once this is done you should now be able to listen for connections and respond with data when appropriate.

Ethernet.begin(mac, ip);
  server.begin();

Detecting a new connection

In the loop function, we need to check for a new connection every time it loops through. If a new connection is detected, then we enter our web page logic code.

The following code will detect when a new client is attempting to connect to the server.

EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
        if (client.available()) {

Once it is determined there is a client available, we then move on to outputting HTML from the Arduino web server.

Returning the HTML response header

For a browser to display the webpage correctly, we first need to reply with an HTML response header. It doesn’t need to be anything complicated, the example below is more than enough to get things working correctly.

If you’re after more information on the header fields, then this incredibly handy wiki page explains everything pretty well.

client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();

Outputting HTML

Outputting HTML can simply be done by calling client.println() or client.print() with the text/HTML passed as a parameter. This is straightforward, but as you can see in the examples further down this page, it really does bloat out the code.

Below is a basic example of outputting some HTML.


client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.print("<h1>Analogue Values</h1>");

The other option is to have the HTML files stored on an SD card which you’re able to access and load from. You can then perform AJAX requests to update elements on the web page so that they’re showing the correct data.

You can also do this to get the Arduino to perform actions such as turn an LED on and off.

Closing the connection

Once you’re done processing the web page, you should delay for a short amount of time to ensure the client gets the data. After a second or so is up, simply close the connection to the client.

delay(1);
    client.stop();

Arduino Web Server without a SD Card

If you’re not using the SD card, then it’s incredibly straightforward to get a web server up and running.

It’s important to note that if you do have an SD card inserted, but it’s not in use, then it can cause issues with sketch communicating with the Arduino. To avoid this from happening, add the following two lines in the setup function.

pinMode(4, OUTPUT);
digitalWrite(4, HIGH);

Once they’re added, you can add the SD card back into the device.

To quickly get an Arduino web server up and running, simply open sketch and copy and paste the code below. It can also be found over at my Github page along with the full code for all examples in this tutorial.

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 177);

EthernetServer server(80);

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
}

void loop() {
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(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();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          client.print("<h1>Analogue Values</h1>");
          for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
            int sensorReading = analogRead(analogChannel);
            client.print("analog input ");
            client.print(analogChannel);
            client.print(" is ");
            client.print(sensorReading);
            client.println("<br />");
          }
          client.println("</html>");
          break;
        }
        if (c == '\n') {
          currentLineIsBlank = true;
        } else if (c != '\r') {
          currentLineIsBlank = false;
        }
      }
    }
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
  }
}

Once you have uploaded this code to the Arduino, you should have a very basic web server up and running.

It will refresh every 5 seconds updating the values of the Arduino’s analog pins (This will just be random values unless you have an actual device or sensor connected to them).

You can also monitor the serial monitor for any debug lines that are located throughout the code.

Arduino web page displaying analog inputs

Arduino Web Server with a SD Card

If you decide to go the SD card route for the Arduino web server, then the HTML files will need to be created on your computer and then copied to the SD card before it is inserted into the Arduino.

A pro with loading the webpage from the SD card is that you could have more complex/heavy pages without needing to write hundreds of client.write lines.  It also helps prevent any low memory situations that usually occur when you end up having too much code running on the Arduino.

Initialising the SD Card

Before you’re able to access the SD card, you will need to import the SD package and initialize it in the setup function. The check in the setup function will see if it can access the card otherwise throw an error in the serial monitor.

Serial.println("Checking SD card is accessible...");
  if (!SD.begin(4)) {
    Serial.println("ERROR - SD card initialization failed!");
    return;    // init failed
  }
  Serial.println("SUCCESS - SD card initialized.");

Loading the file

When you’re ready to load the file simply use sd.open(filename) to open the file. Use an if statement to make sure the file exists and then loop through the contents of the file until it reaches the end.

webPage = SD.open("index.htm"); // open web page file
if (webPage) {
    while (webPage.available()) {
    client.write(webPage.read()); // send web page to client
}
webPage.close();

Below is an example of the web page that is loaded via the SD card with Ajax (explained below) updating the elements on the page.

Arduino Web Page

Performing actions via the web page (AJAX)

There may be a time when you want to control a device or sensor via the web page. In this section, I will look at performing requests and updates using AJAX. This means you won’t see the page load over and over, but it will still be kept updated when required.

AJAX or Asynchronous JavaScript and XML is a little too complicated to cover completely in this tutorial, but I will go through the basics of using it on the Arduino. You should be able to adjust the examples to suit your needs without too many issues.

AJAX Example

AJAX works by sending a request to the server, the server will then check to see if the string ajaxrefresh or ledstatus exists in the header (For this example). If it does then it will generate the relevant data and return it.

The JavaScript then finds the relevant HTML element with the correct id then replaces the innerHTML with the response from the AJAX request.

The example below sends a request to the web server running on the Arduino with the GET variable ajaxrefresh. If the server returns data it will replace the innerHTML of the element that has the id analogue_data.

This script runs every 5 seconds but can be adjusted to better suit your needs.

<script>window.setInterval(function(){
  nocache = "&nocache=" + Math.random() * 10;
  var request = new XMLHttpRequest();
  request.onreadystatechange = function() {
    if (this.readyState == 4) {
      if (this.status == 200) {
        if (this.responseText != null) {
          document.getElementById("analoge_data").innerHTML = this.responseText;
        }
      }
    }
  }
  request.open("GET", "ajaxrefresh" + nocache, true);
  request.send(null);
  }, 5000);
</script>

Arduino Ajax Functions

The two functions below are called whenever the relevant AJAX request comes through to the server. They’re very simple with the first one reading the analog pins and returning the relevant data.

The second function will turn the RED LED on or off depending on its current status. It will also return the status of the LED so that AJAX can update the value on the webpage.

void ajaxRequest(EthernetClient client)
{
  for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
    int sensorReading = analogRead(analogChannel);
    client.print("analog input ");
    client.print(analogChannel);
    client.print(" is ");
    client.print(sensorReading);
    client.println("<br />");
  }
}

void ledChangeStatus(EthernetClient client)
{
  int state = digitalRead(RED);
  Serial.println(state);
  if (state == 1) {
    digitalWrite(RED, LOW);
    client.print("OFF");
  }
  else {
    digitalWrite(RED, HIGH);
    client.print("ON");
  }
}

AJAX with HTML generated by the Arduino

The example below has all the HTML & JavaScript completely generated by the Arduino. The JavaScript makes AJAX requests to the Arduino server which updates the page depending on the results it provides.

One issue I noticed by generating all the code on the Arduino is that it started to run low on space and give stability warnings. This could be an issue if you want tons of sensors and options on the page. You might want to look at storing the HTML on an SD Card and grabbing it from there instead.

void loop() {
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if( HTTP_req.length() < 120) 
           HTTP_req += c; // save the HTTP request 1 char at a time 
           Serial.write(c); 
           if (c == '\n' && currentLineIsBlank)
           { 
            // send a standard http response header
            client.println("HTTP/1.1 200 OK");
            client.println("Content-Type: text/html");
            client.println("Connection: close");
            client.println();
            Serial.println(HTTP_req);
            if (HTTP_req.indexOf("ajaxrefresh") >= 0 ) {
            // read switch state and analog input
            ajaxRequest(client);
            break;
          }
          else if (HTTP_req.indexOf("ledstatus") >= 0 ) {
            // read switch state and analog input
            ledChangeStatus(client);
            break;
          }
          else {
            client.println("<!DOCTYPE HTML>");
            client.println("<html lang=\"en\">");
            client.println("<script>window.setInterval(function(){");
            client.println("nocache = \"&nocache=\" + Math.random() * 10;");
            client.println("var request = new XMLHttpRequest();");
            client.println("request.onreadystatechange = function() {");
            client.println("if (this.readyState == 4) {");
            client.println("if (this.status == 200) {");
            client.println("if (this.responseText != null) {");
            client.println("document.getElementById(\"analoge_data\").innerHTML = this.responseText;");
            client.println("}}}}");
            client.println("request.open(\"GET\", \"ajaxrefresh\" + nocache, true);");
            client.println("request.send(null);");
            client.println("}, 5000);");
            client.println("function changeLEDStatus() {");
            client.println("nocache = \"&nocache=\" + Math.random() * 10;");
            client.println("var request = new XMLHttpRequest();");
            client.println("request.onreadystatechange = function() {");
            client.println("if (this.readyState == 4) {");
            client.println("if (this.status == 200) {");
            client.println("if (this.responseText != null) {");
            client.println("document.getElementById(\"led_status\").innerHTML = this.responseText;");
            client.println("}}}}");
            client.println("request.open(\"GET\", \"?ledstatus=1\" + nocache, true);");
            client.println("request.send(null);");
            client.println("}");
            client.println("</script></head>");
            // output the value of each analog input pin
            client.print("<h1>Analogue Values</h1>");
            client.println("<div id=\"analoge_data\">Arduino analog input values loading.....</div>");
            client.println("<h1>Arduino LED Status</h1>");
            client.println("<div><span id=\"led_status\">");
            if(digitalRead(RED) == 1)
             client.println("On");
            else
              client.println("Off");
            client.println("</span> | <button onclick=\"changeLEDStatus()\">Change Status</button> </div>");
            client.println("</html>");
            break;
          }
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    delay(1);
    client.stop();
    HTTP_req = "";
    Serial.println("client disconnected");
  }
}

AJAX with HTML files on SD Card

As you can see below, the result is much cleaner by simply storing the HTML within a file on the SD Card. This should help keep your script to mainly processing code, especially if you do everything via AJAX and load the HTML off the SD card.

Keep in mind that this is just the loop part of the code if you want the full version head over to my Github page.

void loop()
{
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if ( HTTP_req.length() < 80)
          HTTP_req += c;
        if (c == '\n' && currentLineIsBlank) {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");
          client.println();
          if (HTTP_req.indexOf("ajaxrefresh") >= 0 ) {
            ajaxRequest(client);
            break;
          }
          else if (HTTP_req.indexOf("ledstatus") >= 0 ) {
            ledChangeStatus(client);
            break;
          }
          else {
            webPage = SD.open("index.htm");
            if (webPage) {
              while (webPage.available()) {
                client.write(webPage.read());
              }
              webPage.close();
            }
            break;
          }
          if (c == '\n') {
            currentLineIsBlank = true;
          } else if (c != '\r') {
            currentLineIsBlank = false;
          }
        }
      }
    }
    delay(1);
    client.stop();
    HTTP_req = "";
    Serial.println("client disconnected");
  }
}

Troubleshooting

As with any Arduino project you may come across some issues, the following issues are just a few that I ran into when putting together this tutorial.

  • I can’t access my web page: Double check the IP address and make sure it’s not conflicting with another device on the network. Also, try pinging the IP address and seeing if you get a response.
  • My SD Card says it’s inaccessible: Hold down the off button then insert the SD card, I found this usually fixed the problem.
  • CSS: You can include CSS on your web pages, you can do this either inline or located in the header between style tags. This is something that you might want to read up on more if you haven’t done much CSS. It’s not really necessary unless you want your pages to look good.

Further Implementations

There are a lot more ways that you’re able to use a web server that would increase the functionality of your circuit. I will quickly list a few projects where you might want to implement a web interface.

  • Fan/Temperature Control – Monitor a server room, greenhouse, or something similar where heat and airflow are important. You could add controls to turn on and off heaters and fans.
  • Counter – Use a motion sensor to detect motion and count every time the sensor is triggered. You could apply this same logic with almost any kind of sensor.

I hope that this Arduino web server tutorial has aided you with getting a website up and going where you can both view and interact with data. If you have any recommendations, tips or noticed that something is wrong, then please don’t hesitate to leave a comment below.

One Comment

  1. Avatar for Jack Dickens
    Jack Dickens on

    When I run this code, the site goes up but instead of showing the site, it shows a blank white page with no html at all. Ive checked the ethernet shield with the just AJAX code and it worked fine so I know it must be something with the SD card being read by the arduino. Any clue on how to fix this?

Leave a Reply

Your email address will not be published. Required fields are marked *