Starting Electronics needs your help! Please make a donation to help cover our hosting and other costs. Click the donate button to send a donation of any amount.

Controlling an LED from an Arduino Web Page Button and a Push Button

Created on: 13 April 2015

This Arduino breadboard project uses an Arduino Uno and Arduino Ethernet shield with SD card as a web server. Two push button switches and two LEDs are connected to the Arduino.

The web page that the Arduino web server hosts allows one LED to be controlled by a checkbox and the other LED to be controlled by a HTML button on the web page.

The same two LEDs are also controlled by two push button switches. When a LED is switched on or off from the web page, the state of the LED can be seen in the checkbox or in the text of the button.

The checkbox and the text in the button are also updated if the physical hardware push buttons are pressed to switch the LEDs on or off. Ajax is used on the web page so that updates on the page are free of any flicker.

The video below shows the circuit being operated from the web browser on an Android phone connected to the network via WiFi. A browser on a PC connected to the network via WiFi or cable will also work.

Can't see the video? View on YouTube →

The Circuit Diagram

The connections of the push button switches and LEDs are shown in the circuit diagram below.

Arduino Web Server Switch and LED Connections
Arduino Web Server Switch and LED Connections

The hardware consists of two momentary tactile push button switches and two LEDs connected to the Arduino with Ethernet Shield. The switches have pull-down resistors with values of 10kΩ each and the LEDs have current limiting resistors with values of 470Ω each.

Books that may interest you:

C Programming with Arduino Book Ultimate Arduino MEGA 2560 Hardware Manual Ultimage Arduino Uno Hardware Manual

Arduino Code and HTML Page

Download the zipped code here or copy and paste from below.

Arduino sketch and HTML page zipped: web_server_hw_button_pg_button.zip (4.8kB)

Arduino Sketch

The Arduino sketch consists of a modified version of the code from the Arduino web server tutorial part 16 on web server Ajax I/O.

/*--------------------------------------------------------------
  Program:      web_server_hw_button_pg_button

  Description:  Arduino web server that allows two LEDs to be
                controlled by a checkbox, HTML button and two
                hardware push buttons.
                The web page is stored on the micro SD card.
  
  Hardware:     Arduino Uno and official Arduino Ethernet
                shield. Should work with other Arduinos and
                compatible Ethernet shields.
                2Gb micro SD card formatted FAT16.
                LEDs on pins 6 and 7.
                Switches on pins 2 and 3.
                
  Software:     Developed using Arduino 1.6.1 software
                Should be compatible with Arduino 1.0 +
                SD card contains web page called index.htm
  
  References:   - Based on Ajax I/O example from Starting
                  Electronics:
                  http://startingelectronics.org/tutorials/arduino/ethernet-shield-web-server-tutorial/SD-card-IO/
                  
                - Switch debouncing from Arduino IDE example code
                  File --> Examples --> 02.Digital --> Debounce
                  http://www.arduino.cc/en/Tutorial/Debounce

  Date:         13 April 2015
 
  Author:       W.A. Smith, http://startingelectronics.org
--------------------------------------------------------------*/

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>
// size of buffer used to capture HTTP requests
#define REQ_BUF_SZ   60

// MAC address from Ethernet shield sticker under board
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 0, 20); // IP address, may need to change depending on network
EthernetServer server(80);  // create a server at port 80
File webFile;               // the web page file on the SD card
char HTTP_req[REQ_BUF_SZ] = {0}; // buffered HTTP request stored as null terminated string
char req_index = 0;              // index into HTTP_req buffer
boolean LED_state[2] = {0}; // stores the states of the LEDs

void setup()
{
    // disable Ethernet chip
    pinMode(10, OUTPUT);
    digitalWrite(10, HIGH);
    
    Serial.begin(9600);       // for debugging
    
    // initialize SD card
    Serial.println("Initializing SD card...");
    if (!SD.begin(4)) {
        Serial.println("ERROR - SD card initialization failed!");
        return;    // init failed
    }
    Serial.println("SUCCESS - SD card initialized.");
    // check for index.htm file
    if (!SD.exists("index.htm")) {
        Serial.println("ERROR - Can't find index.htm file!");
        return;  // can't find index file
    }
    Serial.println("SUCCESS - Found index.htm file.");
    // switches
    pinMode(2, INPUT);
    pinMode(3, INPUT);
    // LEDs
    pinMode(6, OUTPUT);
    pinMode(7, OUTPUT);
    
    Ethernet.begin(mac, ip);  // initialize Ethernet device
    server.begin();           // start to listen for clients
}

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
                // limit the size of the stored received HTTP request
                // buffer first part of HTTP request in HTTP_req array (string)
                // leave last element in array as 0 to null terminate string (REQ_BUF_SZ - 1)
                if (req_index < (REQ_BUF_SZ - 1)) {
                    HTTP_req[req_index] = c;          // save HTTP request character
                    req_index++;
                }
                // 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");
                    // remainder of header follows below, depending on if
                    // web page or XML page is requested
                    // Ajax request - send XML file
                    if (StrContains(HTTP_req, "ajax_inputs")) {
                        // send rest of HTTP header
                        client.println("Content-Type: text/xml");
                        client.println("Connection: keep-alive");
                        client.println();
                        SetLEDs();
                        // send XML file containing input states
                        XML_response(client);
                    }
                    else {  // web page request
                        // send rest of HTTP header
                        client.println("Content-Type: text/html");
                        client.println("Connection: keep-alive");
                        client.println();
                        // send web page
                        webFile = SD.open("index.htm");        // open web page file
                        if (webFile) {
                            while(webFile.available()) {
                                client.write(webFile.read()); // send web page to client
                            }
                            webFile.close();
                        }
                    }
                    // display received HTTP request on serial port
                    Serial.print(HTTP_req);
                    // reset buffer index and all buffer elements to 0
                    req_index = 0;
                    StrClear(HTTP_req, REQ_BUF_SZ);
                    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)
    
    // read buttons and debounce
    ButtonDebounce();
}

// function reads the push button switch states, debounces and latches the LED states
// toggles the LED states on each push - release cycle
// hard coded to debounce two switches on pins 2 and 3; and two LEDs on pins 6 and 7
// function adapted from Arduino IDE built-in example:
// File --> Examples --> 02.Digital --> Debounce
void ButtonDebounce(void)
{
    static byte buttonState[2]     = {LOW, LOW};   // the current reading from the input pin
    static byte lastButtonState[2] = {LOW, LOW};   // the previous reading from the input pin
    
    // the following variables are long's because the time, measured in miliseconds,
    // will quickly become a bigger number than can be stored in an int.
    static long lastDebounceTime[2] = {0};  // the last time the output pin was toggled
    long debounceDelay = 50;         // the debounce time; increase if the output flickers
  
    byte reading[2];
    
    reading[0] = digitalRead(2);
    reading[1] = digitalRead(3);
    
    for (int i = 0; i < 2; i++) {
        if (reading[i] != lastButtonState[i]) {
            // reset the debouncing timer
            lastDebounceTime[i] = millis();
        }
      
        if ((millis() - lastDebounceTime[i]) > debounceDelay) {
            // whatever the reading is at, it's been there for longer
            // than the debounce delay, so take it as the actual current state:
        
            // if the button state has changed:
            if (reading[i] != buttonState[i]) {
                buttonState[i] = reading[i];
          
                // only toggle the LED if the new button state is HIGH
                if (buttonState[i] == HIGH) {
                    LED_state[i] = !LED_state[i];
                }
            }
        }
    } // end for() loop
    
    // set the LEDs
    digitalWrite(6, LED_state[0]);
    digitalWrite(7, LED_state[1]);
      
    // save the reading.  Next time through the loop,
    // it'll be the lastButtonState:
    lastButtonState[0] = reading[0];
    lastButtonState[1] = reading[1];
}

// checks if received HTTP request is switching on/off LEDs
// also saves the state of the LEDs
void SetLEDs(void)
{
    // LED 1 (pin 6)
    if (StrContains(HTTP_req, "LED1=1")) {
        LED_state[0] = 1;  // save LED state
        digitalWrite(6, HIGH);
    }
    else if (StrContains(HTTP_req, "LED1=0")) {
        LED_state[0] = 0;  // save LED state
        digitalWrite(6, LOW);
    }
    // LED 2 (pin 7)
    if (StrContains(HTTP_req, "LED2=1")) {
        LED_state[1] = 1;  // save LED state
        digitalWrite(7, HIGH);
    }
    else if (StrContains(HTTP_req, "LED2=0")) {
        LED_state[1] = 0;  // save LED state
        digitalWrite(7, LOW);
    }
}

// send the XML file with analog values, switch status
//  and LED status
void XML_response(EthernetClient cl)
{
    int analog_val;            // stores value read from analog inputs
    int count;                 // used by 'for' loops
    int sw_arr[] = {2, 3};  // pins interfaced to switches
    
    cl.print("<?xml version = \"1.0\" ?>");
    cl.print("<inputs>");
    // checkbox LED states
    // LED1
    cl.print("<LED>");
    if (LED_state[0]) {
        cl.print("checked");
    }
    else {
        cl.print("unchecked");
    }
    cl.println("</LED>");
    // button LED states
    // LED3
    cl.print("<LED>");
    if (LED_state[1]) {
        cl.print("on");
    }
    else {
        cl.print("off");
    }
    cl.println("</LED>");
    cl.print("</inputs>");
}

// sets every element of str to 0 (clears array)
void StrClear(char *str, char length)
{
    for (int i = 0; i < length; i++) {
        str[i] = 0;
    }
}

// searches for the string sfind in the string str
// returns 1 if string found
// returns 0 if string not found
char StrContains(char *str, char *sfind)
{
    char found = 0;
    char index = 0;
    char len;

    len = strlen(str);
    
    if (strlen(sfind) > len) {
        return 0;
    }
    while (index < len) {
        if (str[index] == sfind[found]) {
            found++;
            if (strlen(sfind) == found) {
                return 1;
            }
        }
        else {
            found = 0;
        }
        index++;
    }

    return 0;
}

HTML Page with Embedded CSS and JavaScript

The code below must be copied to a file called index.htm and the index.htm file must be copied to a micro SD card that is to be inserted into the card slot on the Arduino shield.

The HTML code with embedded CSS and JavaScript consists of a modified version of the code from the Arduino web server tutorial part 16 on web server Ajax I/O.

<!DOCTYPE html>
<html>
    <head>
        <title>Arduino Ajax LED Button Control</title>
        <script>
        strLED1 = "";
        strLED2 = "";
        var LED2_state = 0;
        function GetArduinoIO()
        {
            nocache = "&nocache=" + Math.random() * 1000000;
            var request = new XMLHttpRequest();
            request.onreadystatechange = function()
            {
                if (this.readyState == 4) {
                    if (this.status == 200) {
                        if (this.responseXML != null) {
                            // XML file received - contains analog values, switch values and LED states
                            var count;
                            // LED 1
                            if (this.responseXML.getElementsByTagName('LED')[0].childNodes[0].nodeValue === "checked") {
                                document.LED_form.LED1.checked = true;
                            }
                            else {
                                document.LED_form.LED1.checked = false;
                            }
                            // LED 2
                            if (this.responseXML.getElementsByTagName('LED')[1].childNodes[0].nodeValue === "on") {
                                document.getElementById("LED2").innerHTML = "LED 2 is ON (D7)";
                                LED2_state = 1;
                            }
                            else {
                                document.getElementById("LED2").innerHTML = "LED 2 is OFF (D7)";
                                LED2_state = 0;
                            }
                        }
                    }
                }
            }
            // send HTTP GET request with LEDs to switch on/off if any
            request.open("GET", "ajax_inputs" + strLED1 + strLED2 + nocache, true);
            request.send(null);
            setTimeout('GetArduinoIO()', 1000);
            strLED1 = "";
            strLED2 = "";
        }
        // service LEDs when checkbox checked/unchecked
        function GetCheck()
        {
            if (LED_form.LED1.checked) {
                strLED1 = "&LED1=1";
            }
            else {
                strLED1 = "&LED1=0";
            }
        }
        function GetButton1()
        {
            if (LED2_state === 1) {
                LED2_state = 0;
                strLED2 = "&LED2=0";
            }
            else {
                LED2_state = 1;
                strLED2 = "&LED2=1";
            }
        }
    </script>
    <style>
        .IO_box {
            float: left;
            margin: 0 20px 20px 0;
            border: 1px solid blue;
            padding: 0 5px 0 5px;
            width: 120px;
        }
        h1 {
            font-size: 120%;
            color: blue;
            margin: 0 0 10px 0;
        }
        h2 {
            font-size: 85%;
            color: #5734E6;
            margin: 5px 0 5px 0;
        }
        p, form, button {
            font-size: 80%;
            color: #252525;
        }
        .small_text {
            font-size: 70%;
            color: #737373;
        }
    </style>
    </head>
    <body onload="GetArduinoIO()">
        <h1>Arduino Ajax LED Button Control</h1>
        <div class="IO_box">
            <h2>LED Using Checkbox</h2>
            <form id="check_LEDs" name="LED_form">
                <input type="checkbox" name="LED1" value="0" onclick="GetCheck()" />LED 1 (D6)<br /><br />
            </form>
        </div>
        <div class="IO_box">
            <h2>LED Using Button</h2>
            <button type="button" id="LED2" onclick="GetButton1()">LED 2 is OFF (D7)</button><br /><br />
        </div>
    </body>
</html>

How the Code Works

Both the Arduino code and the HTML page were copied from part 16 of the Arduino web server tutorial and modified.

The Code was Cleaned Up

All unnecessary code for the analog inputs, extra checkboxes and buttons was removed from the HTML, JavaScript and Arduino code. The code that controls LED 2 was modified to use an HTML button.

At this stage, the two LEDs could be controlled by using a checkbox on the Arduino hosted web page for the first LED and a button on the HTML page for the second LED.

Adding the Physical Button Control Code

Nothing further had to be changed in the HTML/JavaScript for the hardware switches because the states of the LEDs was already being sent back to the web page using Ajax. Adding two physical push buttons to control the LEDs would update the stored states of the LEDs in the Arduino which would then be sent to the web page on the next Ajax request.

Switch Debouncing and Latching

Momentary push button switches do not latch. Pushing the button closes the circuit, releasing the button opens the circuit again – the contacts never stay closed, but always open when the button is released.

Buttons, switches and relays all have contact bounce. That is when the switch contacts are closed, they do not close cleanly and stay closed, but rather bounce for a small amount of time.

By bounce, we mean that they close initially, then bounce open, then closed again, then open and closed a few times before they settle. This bounce won't be perceived if the switch is used in a circuit that directly switches a light on, but will be detected by a microcontroller that is running at many megahertz.

The switches can be "debounced" using software that waits for the switch contacts to settle.

There is already some software in the Arduino IDE built-in examples that takes care of turning a momentary push button switch into a latching switch and debounces the switch contacts at the same time.

The example can be found from the Arduino IDE menu: File → Examples → 02.Digital → Debounce

When this code example is loaded to the Arduino, pushing and releasing the push button switch once, switches an LED on. Pushing and releasing the push button switch a second time switches the LED off.

In the Arduino code for this project, the Arduino IDE Debounce example code is used in the function ButtonDebounce() to debounce and latch the two push button switches and toggle the LED states.

The code is modified to handle two push buttons and two LEDs by converting the variables in the code to two-dimensional arrays and using one element of the array per push button / LED pair.

Updating the LED States on the Web Page

The array LED_state[] holds the current states of the two LEDs and is modified by the ButtonDebounce() function. This same array is also modified by the checkbox and HTML button from the web page in the SetLEDs() function.

When either a physical push button is pressed, or the checkbox or HTML button is clicked on the web page, the state of the LED is updated in the LED_state[] array.

The next time that the web page does an Ajax request, the LED states in the LED_state[] array are used to update the XML file that is sent back to the web page when the XML_response() function is called.

This updates the checkbox to either checked or unchecked and the text in the HTML button to either ON or OFF.