Arduino Web Server Switch Status Using AJAX Manually

Created on: 4 February 2013

Part 7 of the Arduino Ethernet Shield Web Server Tutorial

The state of a switch connected to the Arduino / Ethernet shield is shown on a web page that is hosted by the Arduino. AJAX is used to fetch the state of the switch when a button on the web page is clicked.

The reason for using a button on the web page to refresh the state of the switch is to keep the code simple for those who are new to AJAX. The next part of this series will automate the reading of the switch using AJAX for a more practical application.

This video shows the Arduino web server displaying the switch status using AJAX.

What is AJAX?

AJAX stands for Asynchronous JavaScript and XML.

AJAX is basically the use of a set of JavaScript functions for getting information from the web server (our Arduino). This means that data on a web page can be updated without fetching the whole page each time.

Using AJAX will be an improvement on the previous part of this tutorial as HTML refresh code that makes the page flicker each time it is reloaded is no longer needed. Only the information that has changed (the state of the switch) will be updated on the page eliminating the flicker.

What is JavaScript?

JavaScript is a client side scripting language. This means that it is code that will run on the web browser.

JavaScript is included in the HTML page. When you surf to the HTML web page hosted by the Arduino, the page and the JavaScript is loaded to your browser. Your browser then runs the JavaScript code (provided that you have not disabled JavaScript in your browser).

Web Server Hardware

The switch is interfaced to the Arduino / Ethernet shield as done in the circuit diagram from this article: Project 4: Switch a LED on when Switch is Closed (Button) except that the switch is connected to pin 3 and not pin 2 of the Arduino (the article actually uses the circuit diagram from one of the Arduino examples on the Arduino website).

Arduino AJAX Sketch

The sketch for this part of the tutorial is shown below. Copy it and paste it into your Arduino IDE and then load it to the Arduino.

/*--------------------------------------------------------------
  Program:      eth_websrv_AJAX_switch

  Description:  Arduino web server shows the state of a switch
                on a web page using AJAX. The state of the
                switch must be read by clicking a button on
                the web page - for demonstrating AJAX.
                Does not use the SD card.
  
  Hardware:     Arduino Uno and official Arduino Ethernet
                shield. Should work with other Arduinos and
                compatible Ethernet shields.
                
  Software:     Developed using Arduino 1.0.3 software
                Should be compatible with Arduino 1.0 +
  
  References:   - WebServer example by David A. Mellis and 
                  modified by Tom Igoe
                - Ethernet library documentation:
                  http://arduino.cc/en/Reference/Ethernet
                - Learning PHP, MySQL & JavaScript by
                  Robin Nixon, O'Reilly publishers

  Date:         15 January 2013
 
  Author:       W.A. Smith, http://startingelectronics.org
--------------------------------------------------------------*/

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

// MAC address from Ethernet shield sticker under board
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(10, 0, 0, 20); // IP address, may need to change depending on network
EthernetServer server(80);  // create a server at port 80

String HTTP_req;            // stores the HTTP request

void setup()
{
    Ethernet.begin(mac, ip);  // initialize Ethernet device
    server.begin();           // start to listen for clients
    Serial.begin(9600);       // for diagnostics
    pinMode(3, INPUT);        // switch is attached to Arduino pin 3
}

void loop()
{
    EthernetClient client = server.available();  // try to get client

    if (client) {  // got client?
        boolean currentLineIsBlank = true;
        while (client.connected()) {
            if (client.available()) {   // client data available to read
                char c = client.read(); // read 1 byte (character) from client
                HTTP_req += c;  // save the HTTP request 1 char at a time
                // last line of client request is blank and ends with \n
                // respond to client only after last line received
                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: keep-alive");
                    client.println();
                    // AJAX request for switch state
                    if (HTTP_req.indexOf("ajax_switch") > -1) {
                        // read switch state and send appropriate paragraph text
                        GetSwitchState(client);
                    }
                    else {  // HTTP request for web page
                        // send web page - contains JavaScript with AJAX calls
                        client.println("<!DOCTYPE html>");
                        client.println("<html>");
                        client.println("<head>");
                        client.println("<title>Arduino Web Page</title>");
                        client.println("<script>");
                        client.println("function GetSwitchState() {");
                        client.println("nocache = \"&nocache=\"\
                                                         + Math.random() * 1000000;");
                        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(\"switch_txt\")\
.innerHTML = this.responseText;");
                        client.println("}}}}");
                        client.println(
                        "request.open(\"GET\", \"ajax_switch\" + nocache, true);");
                      //client.println("request.open(\"GET\", \"ajax_switch\", true);");
                        client.println("request.send(null);");
                        client.println("}");
                        client.println("</script>");
                        client.println("</head>");
                        client.println("<body>");
                        client.println("<h1>Arduino AJAX Switch Status</h1>");
                        client.println(
                        "<p id=\"switch_txt\">Switch state: Not requested...</p>");
                        client.println("<button type=\"button\"\
                            onclick=\"GetSwitchState()\">Get Switch State</button>");
                        client.println("</body>");
                        client.println("</html>");
                    }
                    // display received HTTP request on serial port
                    Serial.print(HTTP_req);
                    HTTP_req = "";            // finished with request, empty string
                    break;
                }
                // every line of text received from the client ends with \r\n
                if (c == '\n') {
                    // last character on line of received text
                    // starting new line with next character read
                    currentLineIsBlank = true;
                } 
                else if (c != '\r') {
                    // a text character was received from client
                    currentLineIsBlank = false;
                }
            } // end if (client.available())
        } // end while (client.connected())
        delay(1);      // give the web browser time to receive the data
        client.stop(); // close the connection
    } // end if (client)
}

// send the state of the switch to the web browser
void GetSwitchState(EthernetClient cl)
{
    if (digitalRead(3)) {
        cl.println("Switch state: ON");
    }
    else {
        cl.println("Switch state: OFF");
    }
}

HTML and JavaScript

The above sketch will send the following HTML and JavaScript to the web browser.

HTML and JavaScript for this AJAX example
HTML and JavaScript Hosted by the Arduino

Page Structure

In the <head> part of the HTML code, a JavaScript function can be found between the opening and closing <script> tags.

Whenever the button on the web page is clicked, the GetSwitchState() JavaScript function is called.

JavaScript Function

When the web page button is clicked and the GetSwitchState() function is called, it sends a HTTP GET request to the Arduino that contains the text "ajax_switch". This request looks as follows:

GET /ajax_switch&nocache=29860.903564600583 HTTP/1.1
Host: 10.0.0.20
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:18.0) Gecko/20100101 Firefox/18.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-ZA,en-GB;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://10.0.0.20/
Connection: keep-alive

When the Arduino receives this request (containing the ajax_switch text), it responds with a standard HTTP response header followed by text that contains the state of the switch.

In the Arduino code, the function GetSwitchState() will read the switch state on the Arduino pin and send the text Switch state: ON or Switch state: OFF.

When the JavaScript in the browser receives this response, it runs the code in the unnamed function request.onreadystatechange = function(). This function runs every time that the Arduino sends a response to the browser. It replaces the Switch state: x text on the web page (or the default text Switch state: Not requested...) with the new text received from the Arduino.

This JavaScript request from the browser and response from the Arduino is AJAX in action.

Random Number

The JavaScript function is based on the code from the book Learning PHP, MySQL, and JavaScript: A Step-By-Step Guide to Creating Dynamic Websites (Animal Guide), page 385 (2009 edition).

In the book, the author explains that when using a GET request, the web browser may cache the GET request. This means that the first request will work fine, but subsequent requests will fail as the first response to the request is cached and will be displayed by the browser every time.

The introduction of a random number in the request fixes the caching problem. Out of five different browsers (Firefox, IE, Safari, Chromium, Chrome) it was found that only Internet Explorer cached the request, so the random number has been added to fix the problem in IE.

AJAX Summarized

The AJAX operation performed in this example can be summarized as follows:

1. AJAX Request from Browser

When the button on the web page is clicked, the JavaScript function GetSwitchState() is run. This function does the following:

  1. Generates a random number to send with the GET request: nocache = "&nocache=" + Math.random() * 1000000;
  2. Creates a XMLHttpRequest() object called request: var request = new XMLHttpRequest();
  3. Assigns a function to handle the response from the web server: request.onreadystatechange = function() (and following code between braces { }).
  4. Sets up a HTTP GET request to send to the web server: request.open("GET", "ajax_switch" + nocache, true);
  5. Sends the HTTP request: request.send(null);

2. Response from Arduino Web Server

When the Arduino web server receives the HTTP GET request, it sends back a standard HTTP response followed by text that represents the state of the switch. The state of the switch and the text sent is obtained from the Arduino's own GetSwitchState() function.

3. Browser JavaScript Handles Response

The HTTP response from the Arduino web server is handled by the JavaScript code. The JavaScript event handler function runs when the response from the Arduino is received (the event handler function is the unnamed function assigned to request.onreadystatechange).

If the received response is OK and not empty, then this line of JavaScript is run:

document.getElementById("switch_txt").innerHTML = this.responseText;

This JavaScript finds the paragraph in the HTML that is marked with the ID switch_txt and replaces the current text with the text received from the Arduino. The HTML for this paragraph looks as follows:

<p id="switch_txt">Switch state: Not requested...</p>

This example has illustrated the use of AJAX used to update a single paragraph of text in the browser. The next part of this tutorial will automate the AJAX request so that a button does not have to be clicked to initiate the request.