Extending LED Output Range on Arduino MEGA 2560 Web Server

This is a solution to the problem that users found when using an Arduino MEGA 2560 with the Switching LEDs on from a Web Page tutorial. The problem is when outputs (or LEDs connected to outputs) greater than 9 are used, they will not switch on. This is the case with the Arduino MEGA 2560 which increases the number of input/output pins above the range of the Arduino Uno by I/O from 14 to 53.

The Problem

The problem is that in the original sketch, the CheckLEDs() function only checks for output (or LED) numbers from 0 to 9. It actually checks for the ASCII characters ‘0’ to ‘9’ as shown below:

// get the state of the LED checkboxes from the HTTP request
void CheckLEDs()
{
    for (byte led_num = 0; led_num < sizeof(LED_pins); led_num++) {
        if ((HTTP_req.charAt(9) == (LED_pins[led_num] + '0')) &&
                    (HTTP_req.charAt(16) == (LED_pins[led_num] + '0'))) {  // LED box is checked
            LED[led_num] = 1;
        }
        else if (HTTP_req.charAt(9) == (LED_pins[led_num] + '0')) {        // LED box is unchecked
            LED[led_num] = 0;
        }
    }
}

 

The Solution

The solution is to check for numbers that are greater than 9 i.e. 2 digit numbers. To do this, first a check is done to determine whether the output being switched on is a single or two digit number. The ASCII numbers are then converted to integers (although they are stored in char variables).

The full sketch is shown below:

/*--------------------------------------------------------------
  Program:      eth_websrv_LED2_MEGA

  Description:  Arduino web server that serves up a web page
                allowing the user to control LEDs
  
  Hardware:     - Arduino MEGA 2560 and official Arduino
                  Ethernet shield. Should work with other
                  Arduinos and compatible Ethernet shields.
                - LED and resistor in series connected between
                  Arduino pin 2 and GND. Second LED connected
                  to pin 30. Third LED connected to pin 53.
                
  Software:     Developed using Arduino 1.0.6 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
                - Derived from eth_websrv_LED2 at:
                  http://blog.startingelectronics.com/?p=598

  Date:         25 February 2014
 
  Author:       W.A. Smith, http://startingelectronics.org
--------------------------------------------------------------*/

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

byte mac[] = { 0x90, 0xA2, 0x00, 0x00, 0x00, 0x01 };    // MAC address
IPAddress ip(192,168,0, 20);                               // IP address

EthernetServer server(80);

char char_in = 0;    // used to store a character from the Ethernet port
String HTTP_req;     // stores the received HTTP request

// add your LED outputs to be switched here
const byte LED_pins[] = {2, 30, 53};     // Arduino pins used as ouputs for LEDs
byte LED[sizeof(LED_pins)] = {0};        // the state of each LED

void setup()
{
//    Serial.begin(9600); ////////////debug
  
    Ethernet.begin(mac, ip);
    server.begin();
    
    // set up the LED pins as outputs
    for (byte index = 0; index < sizeof(LED_pins); index++) {
        pinMode(LED_pins[index], OUTPUT);
    }
}

void loop()
{
    EthernetClient client = server.available();

    if (client) {
        while (client.connected()) {
            if (client.available()) {
                char_in = client.read();  // get a single character from the Ethernet port
                HTTP_req += char_in;      // build a string out of the received characters

                // answer first HTTP request immediately
                if (char_in == '\n') {
                    // respond to HTTP request
                    client.println("HTTP/1.1 200 OK");
                    client.println("Content-Type: text/html");
                    client.println();
                    CheckLEDs();              // get state of LED checkboxes from web page
                    DisplayLEDs(&client);     // display checkboxes on web & write to LEDs
                    HTTP_req = "";      // delete string contents
                    client.stop();
                } // end if (char_in == '\n')
            } // end if (client.available()
        } // end while (client.connected())
    } // end if (client)
}

// get the state of the LED checkboxes from the HTTP request
void CheckLEDs()
{
    char num1, num2, num3, num4;
    
    num1 = HTTP_req.charAt(9);
    num2 = HTTP_req.charAt(10);
//    Serial.println(HTTP_req);///////////////debug
    
    // is it a 2 LED digit number?
    if ((num2 >= '0') && (num2 <= '9')) {
        // it is a 2 digit number, now get the 2nd 2 digit number
        num3 = HTTP_req.charAt(17);
        num4 = HTTP_req.charAt(18);
        // convert the two 2 digit ASCII numbers to integers
        num1 -= '0';
        num1 *= 10;
        num2 -= '0';
        num3 -= '0';
        num3 *= 10;
        num4 -= '0';
        num1 += num2;  // first number stored in num1
        num3 += num4;  // second number stored in num3
        
    }
    else {
        // single digit LED number
        // get the 2nd 1 digit number
        num3 = HTTP_req.charAt(16);
        // convert the numbers from ASCII to integer
        num1 -= '0';  // first number
        num3 -= '0';  // second number
    }
    for (byte led_num = 0; led_num < sizeof(LED_pins); led_num++) {
        if ((num1 == LED_pins[led_num]) && (num3 == LED_pins[led_num])) {  // LED box is checked
            LED[led_num] = 1;
        }
        else if (num1 == LED_pins[led_num]) {        // LED box is unchecked
            LED[led_num] = 0;
        }
    }
}

// write the HTML that includes the state of the LED checkboxes for displaying on the web browser
void DisplayLEDs(Client *client)
{
    // some CSS in the HTML to change colours and position of the box containing the LED checkboxes
    client->print("<div style=\"background:white; color:green; position: absolute; margin:20px; border: grey solid 2px; padding:0 10px 4px 10px;\">");
    client->print("<h1 style=\"font: normal 20px/150% Verdana, Arial, Sans-serif;\">LED Outputs</h1>");
    // send each LED checkbox in turn
    for (byte led_num = 0; led_num < sizeof(LED_pins); led_num++) {
        // use hidden checkbox so that unchecking a checkbox sends a value to the Arduino
        // if only a normal checkbox is used, nothing is sent when it is unchecked
        // both hidden and normal checkboxes are produced here for each LED
        client->print("<form> <input type=\"hidden\" name=\"LED");
        client->print(LED_pins[led_num], DEC);
        client->print("\" value=\"0\"> <input type=\"checkbox\" name=\"LED");
        client->print(LED_pins[led_num], DEC);
        client->print("\" value=\"");
        client->print(LED_pins[led_num], DEC);
        client->print("\"");
        // write to the LED connected to the Arduino board
        if (LED[led_num]) {
            client->print(" checked ");             // part of HTML if checkbox is to be displayed checked
            digitalWrite(LED_pins[led_num], HIGH);  // switch LED on
        }
        else {
            digitalWrite(LED_pins[led_num], LOW);  // switch LED off
        }
        client->print(" onclick=\"submit();\">LED");
        client->print(LED_pins[led_num], DEC);
        client->print(" </form>");                 // end of HTML for 1 LED's form
    }
    client->print("</div>");                       // end of box containing LEDs
}

The images below show the hardware used to test the sketch as well as the web page that the sketch generates.

Arduino MEGA 2560 Web Server

Arduino MEGA 2560 Web Server

 

Arduino MEGA 2560 Web Page

Arduino MEGA 2560 Web Page

News: Solving the Arduino Ethernet Web Server Page Linking Problem

What’s happening at Starting Electronics? (14 June 2013)

I have been working on the sketch for part 10 of the Arduino Ethernet shield web server tutorial that many people could not get to work. The sketch allows the Arduino with Ethernet shield to host two web pages on the SD card. Each web page has a link to the other page and when clicked, should load the other page in the web browser.

The Problem

Firstly, there was no problem when I developed the code using Arduino software version 1.0.3, an Arduino Uno and Arduino Ethernet shield – everything worked. However, a number of people could not get the code to work as reported in the blog entry for the tutorial. I did not have any other Arduino hardware to test the code further, so could not get the problem to come up and then debug it.

New Hardware

Arduino Ethernet Board

After getting a new Arduino Ethernet board, and loading the sketch from part 10, the problems started appearing. The first web page loaded from the Arduino web server, but a blank page appeared in the web browser when the link to the second page was clicked. Compiling the sketch with Arduino software version 1.0.5 and trying to run the web server on the old hardware now resulted in the first web page not loading at all. Changing back to version 1.0.3 software solved the problem on the old hardware, but not on the new Arduino Ethernet board.

Arduino Mega 2560 Board

When testing the sketch on a new Arduino Mega 2560 with the original Ethernet shield the sketch worked with no problems.

Memory Sizes

It had been suggested by someone in the comments on the blog that the problem was with too little SRAM on the Uno board – changing to the Arduino Mega proved this. The SRAM sizes for the three boards that I have are as follows:

  • Arduino Uno – 2k Bytes SRAM
  • Arduino Ethernet – 2k Bytes SRAM
  • Arduino Mega 2560 – 8k Bytes SRAM

The Solution to the Problem

I had already suspected that using the String class in the sketch was the source of the problem and had mentioned a way of reducing the number of bytes that the String class object in the sketch was using in part 16 of the tutorial series. Restricting the number of bytes in the web page link example, however, made no difference.

The String Class Object

The String object was used to store the HTTP request from the web browser. The stored request could then be analysed later in the sketch to see which page the web browser was requesting. Restricting the String object to storing only the first 30 bytes of the HTTP request did not solve the problem. The only solution was to eliminate the use of the String class altogether.

My thinking in using the String class was that the sketch was small, so there should be no memory problems and it worked (at least on my hardware) – no need to write extra functions and make the sketch more complicated. The String class also had a method that made it easy to find out which web page was requested by searching in the stored HTTP request String for the web page name.

New String Functions

In the new sketch, the String class object was replaced by a char buffer with limited size. Two new functions were written to operate on the new buffer. One to clear the buffer and the other to search for a particular string in the buffer. This eliminated the need to use the String class at all. The function that searches for the string in the HTTP request buffer is used to find which web page the browser is requesting.

To see the new sketch, go to part 10 of the Arduino Ethernet web server tutorial: Arduino SD Card Web Server – Linking Pages.

IP Addresses

A few weeks ago, my ADSL router failed and I had to replace it. The old router used the address range 10.0.0.x and the new router now uses the address range 192.168.0.x. The new sketch now uses the new address range, so be sure to change the IP address in the sketch to suit your network. If the sketches were all working on your system at 10.0.0.20, but the updated sketches stop working, it is probably because the updated sketches are using the new IP address 192.168.0.20.

I will continue testing the remainder of the Arduino Ethernet web server tutorial using the Arduino Ethernet board and will eliminate the use of the String object from all sketches, which will hopefully make the code a lot more reliable and able to work across all the Arduino hardware.