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.
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.
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.
You can help the Starting Electronics website by making a donation:
Any donation is much appreciated and used to pay the running costs of this website. Click the button below to make a donation.
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.
You can help the Starting Electronics website by making a donation:
Any donation is much appreciated and used to pay the running costs of this website. Click the button below to make a donation.
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.