How to Use Arduino Serial Ports

Arduino boards such as the Uno, MEGA2560 and Due all have a serial port that connects to the USB device port on the board. This port allows sketches to be loaded to the board using a USB cable. Code in a sketch can use the same USB / serial port to communicate with the PC by using the Arduino IDE Serial Monitor window, or a Processing application for example. The USB port appears as a virtual COM port on the PC.

This article shows how to use Arduino serial ports when additional serial ports are needed for a project.

Arduino Serial Ports Available

The serial port for programming the Arduino mentioned above is a hardware serial port. The microcontroller on the Arduino board has a hardware serial port built-in, so that after the port has been initialized by software, a byte sent to the port will be sent out serially by the hardware.

The Arduino Uno has only one hardware serial port because the microcontroller used on the Uno has only one built-in serial port. The Arduino MEGA 2560 and Arduino Due both have 3 extra hardware serial ports.

Serial Port Technical Details

The hardware serial ports referred to here are UART (Universal Asynchronous Receiver Transmitter) ports. They may be referred to as USART (Universal Synchronous Asynchronous Receiver Transmitter) ports in the microcontroller documentation if they are configurable in both synchronous and asynchronous modes.

Arduino Uno Serial Port

This image shows the only serial port available on the Arduino Uno highlighted in red. The port connects through a USB chip to the USB device port.

Arduino Uno Serial Port

Arduino Uno Serial Port

Arduino MEGA 2560 and Due

Both the MEGA 2560 and Due have 4 serial ports in total. One that connects through a USB port chip to the USB device port on the board and three extra serial ports that connect to pins on one of the pin headers of the board.

Arduino Due Serial Ports

Arduino Due Serial Ports

 

Arduino MEGA 2560 Serial Ports

Arduino MEGA 2560 Serial Ports

Pins 0 and 1 of the Due and MEGA connect serial port 0 through to the USB device port so that these Arduino boards are compatible with the pin numbering of the Uno and therefore with Arduino shields.

The extra serial ports are ports 1 to 3 with each port having a transmit and receive pin.

It is important to be aware that the MEGA 2560 serial port pins use 5V voltage levels, but the Due uses 3.3V voltage levels.

How to Use Additional Arduino Serial Ports

An extra serial port can be used on an Arduino Uno, but must be simulated in software by using the SoftwareSerial library.

Arduino Uno

The following code is taken from the article on serial communications with the GT-511C3 fingerprint scanner which connects the fingerprint scanner to a software serial port on an Arduino Uno.

#include <SoftwareSerial.h>

SoftwareSerial gtSerial(8, 7); // Arduino RX, Arduino TX

void setup() {
  Serial.begin(9600);    // serial / USB port
  gtSerial.begin(9600);  // software serial port
}

byte rx_byte = 0;        // stores received byte

void loop() {
  // check if byte available from USB port
  if (Serial.available()) {
    rx_byte = Serial.read();
    // send a byte to the software serial port
    gtSerial.write(rx_byte);
  }

  // check if byte available on the software serial port
  if (gtSerial.available()) {
    // get the byte from the software serial port
    rx_byte = gtSerial.read();
    Serial.write(rx_byte);
  }
}

To use the software serial port, first the header file for the software serial library must be included.

#include <SoftwareSerial.h>

Next create the software serial port, selecting the Arduino pins to use for receive (RX) and transmit (TX). Here pin 8 has been set as the receive pin and pin 7 as the transmit pin.

SoftwareSerial gtSerial(8, 7);

The software serial port had been given the name gtSerial which will be used in the sketch to refer to this serial port.

The port can now be checked for incoming data.

if (gtSerial.available()) {

If data is available, it can be read from the port.

rx_byte = gtSerial.read();

Data bytes can also be sent on the port.

gtSerial.write(rx_byte);

How to Use Additional Serial Ports on the Arduino MEGA 2560 and Due

The additional hardware ports on the Arduino MEGA 2560 and Due can be used in the same way as the main USB serial port is used in sketches, only changing the name of the port. The USB serial port, or serial port 0 is referred to as Serial in sketches. To use serial port 1, the name changes to Serial1. Serial ports 2 and 3 are referred to as Serial2 and Serial3.

This sketch shows serial port 3 being used which transmits on pin 14 of the MEGA or Due and receives on pin 15.

void setup() {
  // initialize serial ports
  Serial.begin(9600);    // USB serial port 0
  Serial3.begin(9600);   // serial port 3
}

byte rx_byte = 0;        // stores received byte

void loop() {
  // check for data byte on USB serial port
  if (Serial.available()) {
    // get byte from USB serial port
    rx_byte = Serial.read();
    // send byte to serial port 3
    Serial3.write(rx_byte);
  }
  // check for data byte on serial port 3
  if (Serial3.available()) {
    // get a byte from serial port 3
    rx_byte = Serial3.read();
    // send the byte to the USB serial port
    Serial.write(rx_byte);
  }
}

The additional serial ports are immediately available in the sketch without having to include any libraries.

Serial port 3 must first be initialized to the desired baud rate.

Serial3.begin(9600);

The port can be checked for incoming data.

if (Serial3.available()) {

If a byte has arrived on the serial port, it can be read.

rx_byte = Serial3.read();

A byte can be written to the serial port.

Serial.write(rx_byte);

Arduino Serial Port Resources

Arduino Website References for Software and Hardware

Projects, Articles and Tutorials

Can’t Find Arduino Due in Board List of IDE

Can’t find Arduino Due on the menu and therefore can’t load a sketch to the board? The Arduino Due does not appear in the board list of the Arduino IDE under the tools menu. Boards are normally listed under Tools → Board in the Arduino IDE, but if you can’t find the Arduino Due on the Board menu, additional software must be installed from within the IDE.

The Arduino Due used to have a separate IDE, but was later merged with the main Arduino IDE software. It is now a requirement to install the extra software in order to be able to program the Due.

Can’t Find Arduino Due on the Menu in the Arduino Software

In the image below, the Arduino Due is missing from the menu. The steps below show how to solve the problem and get the Arduino Due board listed on the menu.

Can't find Arduino Due on the menu - Arduino Due missing

Arduino Due Does not Appear in Board List of Menu

Installing Software for the Arduino Due

The following screen captures show how to install the software for the Arduino Due.

1. Boards Manager

Select the Boards Manager menu item from the Arduino IDE menu.
Tools → Board → Boards Manager …

Arduino IDE Boards Manager

Arduino IDE Boards Manager

2. Select Arduino SAM Boards

Click the Arduino SAM Boards item in the Boards Manager dialog box and then click the Install button.

Select Arduino SAM Boards

Select Arduino SAM Boards

It will take some time to install the software, depending on how fast the Internet connection is. After installation, click the Close button in the dialog box.

Programming the Arduino Due

After installation, the Arduino Due board will appear on the Board menu. Select Arduino Due (Programming Port) to allow Arduino sketches to be loaded to the board from the Arduino IDE. The USB cable must be plugged into the USB port labelled PROGRAMMING on the bottom of the board.

Arduino Due Programming Port

Arduino Due Programming Port

 

HTML Text Box to Send Text to Arduino Web Server

An Arduino and Ethernet shield are used as a web server to host a web page that contains a text box. Text can be typed into the web page text box using a web browser and sent to the Arduino by clicking a button on the web page.

An HTML textarea is used in an HTML form to create the text box. JavaScript is used to send the text to the Arduino using a HTTP GET request when the button on the web page is clicked. This is useful for any Arduino project that needs to receive text from a web page using a text box.

The Arduino code for this project follows the format of the Ajax I/O web server from the Arduino Ethernet shield web server tutorial, except that it calls the JavaScript function that sends the GET request when the button on the web page is clicked rather than periodically sending the GET request to the Arduino web server.

The video below shows the Arduino web page being accessed by a web browser and text being sent to the Arduino.

 

Arduino Hardware, Software and HTML Page Setup

Hardware

An Arduino Uno and Arduino Ethernet shield with 2GB micro SD card were used to test the project. Most Arduino boards that are compatible with the Ethernet shield should work.

Setup

Copy the HTML from below to a file called index.htm on the micro SD card and insert it into the Ethernet shield micro SD card socket. Load the Arduino sketch from below to the Arduino — first change the MAC and IP address in the sketch to suit your own network. In the Arduino IDE Serial Monitor window, set the baud rate at the bottom of the window to 115200.

Running the Project

With the Arduino connected to the Ethernet network, first open the Serial Monitor window, then open a web browser and surf to the IP address set in the sketch. Text can be typed into the text box on the web page and sent to the Arduino. The Arduino will display the text in the Serial Monitor window if the line of text is not too long (the length is set by buffer arrays in the Arduino code).

Arduino Text Box Sketch

The Arduino text box sketch listing called text_area is shown below. Copy and paste it to the Arduino IDE.

/*--------------------------------------------------------------
  Program:      text_area

  Description:  Arduino web server that gets text from an HTML
                textarea text box on the hosted web page.
                The web page is stored on the micro SD card.
  
  Date:         23 June 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   90
// size of buffer that stores the incoming string
#define TXT_BUF_SZ   50

// 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
char txt_buf[TXT_BUF_SZ] = {0};  // buffer to save text to

void setup()
{
    // disable Ethernet chip
    pinMode(10, OUTPUT);
    digitalWrite(10, HIGH);
    
    Serial.begin(115200);       // 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.");
    
    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();

                        // print the received text to the Serial Monitor window
                        // if received with the incoming HTTP GET string
                        if (GetText(txt_buf, TXT_BUF_SZ)) {
                          Serial.println("\r\nReceived Text:");
                          Serial.println(txt_buf);
                        }
                    }
                    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();
                        }
                    }
                    // 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)
}

// extract text from the incoming HTTP GET data string
// returns true only if text was received
// the string must start with "&txt=" and end with "&end"
// if the string is too long for the HTTP_req buffer and
// "&end" is cut off, then the function returns false
boolean GetText(char *txt, int len)
{
  boolean got_text = false;    // text received flag
  char *str_begin;             // pointer to start of text
  char *str_end;               // pointer to end of text
  int str_len = 0;
  int txt_index = 0;
  
  // get pointer to the beginning of the text
  str_begin = strstr(HTTP_req, "&txt=");
  if (str_begin != NULL) {
    str_begin = strstr(str_begin, "=");  // skip to the =
    str_begin += 1;                      // skip over the =
    str_end = strstr(str_begin, "&end");
    if (str_end != NULL) {
      str_end[0] = 0;  // terminate the string
      str_len = strlen(str_begin);

      // copy the string to the txt buffer and replace %20 with space ' '
      for (int i = 0; i < str_len; i++) {
        if (str_begin[i] != '%') {
          if (str_begin[i] == 0) {
            // end of string
            break;
          }
          else {
            txt[txt_index++] = str_begin[i];
            if (txt_index >= (len - 1)) {
              // keep the output string within bounds
              break;
            }
          }
        }
        else {
          // replace %20 with a space
          if ((str_begin[i + 1] == '2') && (str_begin[i + 2] == '0')) {
            txt[txt_index++] = ' ';
            i += 2;
            if (txt_index >= (len - 1)) {
              // keep the output string within bounds
              break;
            }
          }
        }
      }
      // terminate the string
      txt[txt_index] = 0;
      got_text = true;
    }
  }

  return got_text;
}

// 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;
}


Text Box HTML Page

Copy the HTML below and save it to a file called index.htm on the SD card.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Text Box using textarea</title>
        <script>

        strText = "";
        
        function SendText()
        {
            nocache = "&nocache=" + Math.random() * 1000000;
            var request = new XMLHttpRequest();
            
            strText = "&txt=" + document.getElementById("txt_form").form_text.value + "&end=end";
            
            request.open("GET", "ajax_inputs" + strText + nocache, true);
            request.send(null);
        }
        </script>
    </head>

    <body onload="GetArduinoIO()">
        <form id="txt_form" name="frmText">
            <textarea name="form_text" rows="10" cols="40"></textarea>
        </form>
        <input type="submit" value="Send Text" onclick="SendText()" />
    </body>

</html>

How the Text Box Sketch Works

JavaScript embedded in the web page sends the text from the text box to the Arduino as part of a HTTP GET request when the button on the web page is clicked.

Format of the String

This image shows what the string looks like before it is sent to the Arduino.

HTML Text Box and Text String

HTML Text Box and Text String

The text from the text box is put between &txt= and &end=end before being sent. The Arduino uses this text to find the string in the incoming HTTP GET request.

When the text reaches the Arduino, it has been altered with the spaces in the text changed to %20 as shown in the image below.

String from the Text Box Received by Arduino

String from the Text Box Received by Arduino

The Arduino sketch must change %20 in the text back to spaces.

Processing the String in the Arduino Sketch

The function GetText() is used to get the text box string from the incoming HTTP GET request. The HTTP_req array holds the beginning of the incoming GET request as shown in the above image — starting with GET /ajax_inputs&txt…

The GetText() function first gets a pointer to the beginning of the text box string by searching for &txt= and then terminating the string when it finds &end. A for loop is used to copy the string to the txt array (which is a pointer to the global txt_buf array). While the copying is taking place, the code looks for spaces that are encoded as %20 and changes them into spaces.

Limitations of the Arduino Text Box Sketch

The sketch is limited by the size of the buffer that saves the incoming HTTP GET request as well as the size of the array that stores the string from the text box. These two array buffers have been kept small so that the code will be able to run on an Arduino Uno.

If the string from the text box is too big for the HTTP buffer on the Arduino so that the string is truncated before the terminating &end, then the text will not be displayed in the Serial Monitor window because the GetText() function will return false.

This is just a simple demonstration that uses GET to send text. It would be better to send the text using POST instead and this will be added as a tutorial at a later stage.

 

Using HTML Drop-down Select Boxes for Hour and Minute on Arduino Web Server

An Arduino and Ethernet shield with SD card are used as a web server to host a page that allows a user to select hour and minute values from two HTML drop-down select boxes. The time sent to the Arduino can be used to set a real-time clock from a web page or for other time related purposes.

To demonstrate that the Arduino is getting the time values from the web page, they are displayed in the Serial Monitor window of the Arduino IDE. JavaScript in the hosted HTML web page is used to send the hour and minute time values to the Arduino periodically. This allows the code to easily be integrated with the Arduino Ajax web server code. The code could also be adapted to send the time only when a button is clicked.

This video shows the sketch running and the selected time values from the web page being sent to the Serial Monitor window:

 

Web Server Hardware

An Arduino Uno, Ethernet shield and 2GB micro SD card were used for testing the code. Any Arduino board that is compatible with the Ethernet shield should work.

Setting up the Web Server

The Arduino sketch below must be loaded to the Arduino with Ethernet shield. The HTML web page from below must be copied to a file called index.htm on the micro SD card. Insert the micro SD card into the socket on the Ethernet shield.

Arduino Web Server Sketch

Copy this code and paste it to the Arduino IDE for loading to the Arduino board with Ethernet shield and SD card. Change the MAC and IP address to suit your own system.

/*--------------------------------------------------------------
  Program:      drop_down_select

  Description:  Arduino web server that gets hour and
                minute from two drop-down select boxes on an
                HTML page.
                The web page is stored on the micro SD card.
  
  Date:         22 June 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

void setup()
{
    // disable Ethernet chip
    pinMode(10, OUTPUT);
    digitalWrite(10, HIGH);
    
    Serial.begin(115200);       // 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.");
    
    Ethernet.begin(mac, ip);  // initialize Ethernet device
    server.begin();           // start to listen for clients
}

void loop()
{
    byte hour;
    byte minute;
    
    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();
                        // print the received hour and minute to the Serial Monitor window
                        // if received with the incoming HTTP GET string
                        if (GetTime(&hour, &minute)) {
                          Serial.print("Hour: ");
                          Serial.println(hour);
                          Serial.print("Minute: ");
                          Serial.println(minute);
                        }
                    }
                    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();
                        }
                    }
                    // 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)
}

// extract hour and minute from the incoming HTTP GET data string
// returns true only if both the hour and minute have been received
boolean GetTime(byte *hr, byte *mn)
{
  boolean valid_time = false;  // hour and minute received flag
  char *str_part;              // pointer to part of the HTTP_req string
  char str_time[3] = {0};      // used to extract the times from HTTP_req string
  
  // get pointer to the beginning of hour data in string
  str_part = strstr(HTTP_req, "&h=");
  if (str_part != NULL) {
    // get the hour from the string
    str_time[0] = str_part[3];
    str_time[1] = str_part[4];
    *hr = atoi(str_time);
    // get pointer to the beginning of minute data in string
    str_part = strstr(HTTP_req, "&m=");
    if (str_part != NULL) {
      // get the minute from the string
      str_time[0] = str_part[3];
      str_time[1] = str_part[4];
      *mn = atoi(str_time);
      // got the hour and the minute
      valid_time = true;
    }
  }

  return valid_time;
}

// 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;
}


Arduino Hosted HTML Page

This code gets copied to the index.htm file on the micro SD card.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Drop-Down Select Box Example</title>
        <script>
        strHour = "";
        strMin = "";
        function GetArduinoIO()
        {
            nocache = "&nocache=" + Math.random() * 1000000;
            var request = new XMLHttpRequest();
            
            // get the values from the drop-down select boxes
            var sel_hour = document.getElementById("selHour");
            if (sel_hour.selectedIndex > 0) {
                strHour = "&h=" + sel_hour.options[sel_hour.selectedIndex].text;
            }
            else {
                // the first item in the drop-down box is invalid text, so clear the string
                strHour = "";
            }
            
            var sel_min = document.getElementById("selMin");
            if (sel_min.selectedIndex > 0) {
                strMin = "&m=" + sel_min.options[sel_min.selectedIndex].text;
            }
            else {
                strMin = "";
            }

            // send HTTP GET request with LEDs to switch on/off if any
            request.open("GET", "ajax_inputs" + strHour + strMin + nocache, true);
            request.send(null);
            setTimeout('GetArduinoIO()', 3000);
        }
    </script>
    </head>

    <body onload="GetArduinoIO()">
        <select id="selHour">
            <option selected="selected" value="">Select Hour</option>
            <option value="00">00</option>
            <option value="01">01</option>
            <option value="02">02</option>
            <option value="03">03</option>
            <option value="04">04</option>
            <option value="05">05</option>
            <option value="06">06</option>
            <option value="07">07</option>
            <option value="08">08</option>
            <option value="09">09</option>
            <option value="10">10</option>
            <option value="11">11</option>
            <option value="12">12</option>
        </select>
        <select id="selMin">
            <option selected="selected" value="">Select Minute</option>
            <option value="00">00</option>
            <option value="01">01</option>
            <option value="02">02</option>
            <option value="03">03</option>
            <option value="04">04</option>
            <option value="05">05</option>
            <option value="06">06</option>
            <option value="07">07</option>
            <option value="08">08</option>
            <option value="09">09</option>
            <option value="10">10</option>
            <option value="11">11</option>
            <option value="12">12</option>
            <option value="13">13</option>
            <option value="14">14</option>
            <option value="15">15</option>
            <option value="16">16</option>
            <option value="17">17</option>
            <option value="18">18</option>
            <option value="19">19</option>
            <option value="20">20</option>
            <option value="21">21</option>
            <option value="22">22</option>
            <option value="23">23</option>
            <option value="24">24</option>
            <option value="25">25</option>
            <option value="26">26</option>
            <option value="27">27</option>
            <option value="28">28</option>
            <option value="29">29</option>
            <option value="30">30</option>
            <option value="31">31</option>
            <option value="32">32</option>
            <option value="33">33</option>
            <option value="34">34</option>
            <option value="35">35</option>
            <option value="36">36</option>
            <option value="37">37</option>
            <option value="38">38</option>
            <option value="39">39</option>
            <option value="40">40</option>
            <option value="41">41</option>
            <option value="42">42</option>
            <option value="43">43</option>
            <option value="44">44</option>
            <option value="45">45</option>
            <option value="46">46</option>
            <option value="47">47</option>
            <option value="48">48</option>
            <option value="49">49</option>
            <option value="50">50</option>
            <option value="51">51</option>
            <option value="52">52</option>
            <option value="53">53</option>
            <option value="54">54</option>
            <option value="55">55</option>
            <option value="56">56</option>
            <option value="57">57</option>
            <option value="58">58</option>
            <option value="59">59</option>
        </select>
    </body>

</html>

Running the Web Server Drop-down Select Box Project

With the index.htm file on the SD card and the sketch loaded to the Arduino, open the Serial Monitor window and then use a web browser to surf to the IP address that is set in the sketch.

Change the hour and minute values in the drop-down boxes to valid time values to see the hour and minute displayed in the Serial Monitor window. Both the hour and minute must be set to valid values before anything is displayed.

A Comparison of Arduino Sketch Sizes in Memory

A comparison of several Arduino sketches from the Arduino code examples, as well as some code that uses a combination of library functions, shows how much memory is used in an Arduino Uno. This gives a rough idea of what the Arduino Uno is capable of handling in terms of memory usage / sketch size before having to move up to an Arduino that has more memory such as the Arduino MEGA.

Memory Usage in the Arduino IDE

Older versions of the Arduino IDE only showed the amount of Flash memory (non-volatile memory) used by an Arduino sketch at compile time i.e. when the Verify icon is clicked on the toolbar.

Newer versions of the Arduino IDE now show both the amount of Flash memory and SRAM memory that a sketch will use after compiling. The amount of SRAM memory used does not include local variables, however the compiler message shows the amount of SRAM left for local variables.

Arduino IDE Showing Sketch Memory Usage

Arduino IDE Showing Sketch Memory Usage

 

 

 

 

 

 

 

 

 

 

 

The above image shows the Arduino IDE after compiling. The area below the sketch shows the messages from the compiler that displays the sketch memory usage.

Arduino Sketch Memory Usage Examples

The examples below show the memory usage reported by the Arduino IDE compiler. Arduino IDE version 1.6.4 compiling for an Arduino Uno is used for all the examples.

Most of the sketches can be found in the Arduino IDE examples under File –> Examples as shown below, a link is given to sketches that are not found in with the IDE examples. The SRAM usage does not include SRAM used by local variables. The Flash memory maximum size of 32,256 bytes is the Arduino Uno Flash memory size of 32,768 bytes, minus 0.5kB used for the boot loader program.

Blinking An LED

File –> Examples –> 01.Basics –> Blink
This simple sketch blinks the on-board LED on and off at a set interval.

Flash usage: 1,030 bytes (3%) of 32,256
SRAM usage: 9 bytes of 2,048

Reading Analog Input

File –> Examples –> 01.Basics –> AnalogReadSerial
Reads a single analog value and prints it to the serial port.

Flash usage: 2,382 bytes (7%) of 32,256
SRAM usage: 184 bytes (8%) of 2,048

Reading Digital Input

File –> Examples –> 01.Basics –> DigitalReadSerial
Reads the state of a single digital input and prints the result to the serial port.

Flash usage: 2,704 bytes (8%) of 32,256
SRAM usage: 184 bytes (8%) of 2,048

Using the String Object

File –> Examples –> 08.Strings –> StringLength
Measures the length of a string sent over the serial port using a String object.

Flash usage: 3,966 bytes (12%) of 32,256
SRAM usage: 300 bytes (14%) of 2,048

Getting SD Card Information

File –> Examples –> SD –> CardInfo
Used for testing a SD card.

Flash usage: 1,502 bytes (35%) of 32,256
SRAM usage: 1,348 bytes (65%) of 2,048

Writing To and Reading From the SD Card

File –> Examples –> SD –> ReadWrite
Writes text to a file on an SD card and then reads the text back from the file.

Flash usage: 12,930 bytes (40%) of 32,256
SRAM usage: 992 bytes (48%) of 2,048

A Simple Web Server

File –> Examples –> Ethernet –> WebServer
A simple web server that hosts a web page displaying values read from analog pins. Does not used the SD card.

Flash usage: 12,612 bytes (39%) of 32,256
SRAM usage: 552 bytes (26%) of 2,048

A Simple Web Client

File –> Examples –> Ethernet –> WebClient
A web client that fetches a web page from the Internet.

Flash usage: 14,568 bytes (45%) of 32,256
SRAM usage: 673 bytes (32%) of 2,048

Printing Text to a LCD

File –> Examples –> LiquidCrystal –> HelloWorld
Prints a message to an LCD.

Flash usage: 2,328 bytes (7%) of 32,256
SRAM usage: 55 bytes (2%) of 2,048

Displaying Text from the Serial Port on the LCD

File –> Examples –> LiquidCrystal –> SerialDisplay
Display text sent over the serial port to the Arduino.

Flash usage: 3,182 bytes (9%) of 32,256
SRAM usage: 214 bytes (10%) of 2,048

Web Server that Uses the SD Card

Basic web server that hosts a web page on the SD card from the Arduino Ethernet tutorial.

Flash usage: 22,806 bytes (70%) of 32,256
SRAM usage: 1,274 bytes (62%) of 2,048

Web Server that Uses the SD Card and LCD

This code is based on the above SD card web server, but also uses an LCD display to show some text during initialization and to display the amount of free SRAM.

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

// 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;
// have to change the pin numbers from the example defaults when using the Ethernet shield
LiquidCrystal lcd(3, 2, 8, 7, 6, 5);

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

void setup()
{
    // set up the LCD's number of columns and rows:
    lcd.begin(16, 2);
    // Print a message to the LCD.
    lcd.print("Initializing...");
  
    Ethernet.begin(mac, ip);  // initialize Ethernet device
    server.begin();           // start to listen for clients
    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.");
    lcd.setCursor(0, 1);
    lcd.print("Done.");
}

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
                // 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: close");
                    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();
                    }
                    lcd.clear();
                    lcd.setCursor(0, 0);
                    lcd.print("Free RAM:");
                    lcd.setCursor(0, 1);
                    lcd.print(freeRam());
                    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)
}

Flash usage: 24,100 bytes (74%) of 32,256
SRAM usage: 1,338 bytes (65%) of 2,048

Finding the Amount of Available SRAM at Runtime

The above sketch uses the freeRam() function from the Arduino Playground article on available memory to display the amount of available SRAM at runtime (while the sketch is running as opposed to at compile time).

The compiler shows the amount of free memory as 710 bytes available for local variables. The amount of free memory shown by the freeRam() function is 657 bytes at the time that the web page has been sent to the client. This available SRAM now takes into account the local variables that are in use and this is the reason that the runtime RAM available is less than the compile time RAM.

Comparing the Memory Usage Results

From the above memory usage results, it can be seen that some of the simpler sketches use less than 15% of available Flash and less than 15% of available SRAM. These are very simple sketches that only demonstrate a single principle.

The SD card read / write example starts approaching the half way mark of the available memory resources with the web client and  simple web server using less than half the memory resources.

As soon as more libraries are used together, memory usage goes over the 50% mark as is the case with the SD card web server at 70% Flash memory usage and  62% SRAM usage. This is still a relatively simple web server hosting a single static web page, but now including the SD card library.

When the LCD is added to the SD card web server, memory usage goes up to 74% for Flash and 65% for SRAM.

From the above, we can see that a simple sketch that uses multiple libraries can quickly use up available memory resources on an Arduino Uno. There are some techniques for making more efficient use of memory. One of the simplest ways to free up RAM is to use the F() macro to keep text in Flash memory so that it does not get copied to RAM at all. The F() macro can be applied to strings that are constant and aren’t modified at runtime.

Freeing Up SRAM

The same sketch above can be modified using the F() macro to put the text strings that are sent from the Arduino over the serial port, to the LCD and to the Ethernet client into Flash memory. For example, the string in the following line of code:

Serial.println("Initializing SD card...");

can be put into Flash memory like this:

Serial.println(F("Initializing SD card..."));

by putting the string including the opening and closing quotation marks into the brackets of the F() macro.

The above sketch will then look like this:

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

// 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;
// have to change the pin numbers from the example defaults when using the Ethernet shield
LiquidCrystal lcd(3, 2, 8, 7, 6, 5);

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

void setup()
{
    // set up the LCD's number of columns and rows:
    lcd.begin(16, 2);
    // Print a message to the LCD.
    lcd.print(F("Initializing..."));
  
    Ethernet.begin(mac, ip);  // initialize Ethernet device
    server.begin();           // start to listen for clients
    Serial.begin(9600);       // for debugging
    
    // initialize SD card
    Serial.println(F("Initializing SD card..."));
    if (!SD.begin(4)) {
        Serial.println(F("ERROR - SD card initialization failed!"));
        return;    // init failed
    }
    Serial.println(F("SUCCESS - SD card initialized."));
    // check for index.htm file
    if (!SD.exists("index.htm")) {
        Serial.println(F("ERROR - Can't find index.htm file!"));
        return;  // can't find index file
    }
    Serial.println(F("SUCCESS - Found index.htm file."));
    lcd.setCursor(0, 1);
    lcd.print(F("Done."));
}

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
                // 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(F("HTTP/1.1 200 OK"));
                    client.println(F("Content-Type: text/html"));
                    client.println(F("Connection: close"));
                    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();
                    }
                    lcd.clear();
                    lcd.setCursor(0, 0);
                    lcd.print(F("Free RAM:"));
                    lcd.setCursor(0, 1);
                    lcd.print(freeRam());
                    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)
}

This changes the original sketch memory usage from:
Flash: 24,100 bytes (74%)
SRAM: 1,338 bytes (65%) — 710 bytes for local variables
Runtime SRAM available: 657 bytes

To:
Flash: 24,170 bytes (74%)
SRAM: 1,086 bytes (53%) — 962 bytes for local variables
Runtime SRAM available: 909 bytes

Which frees up 253 bytes of memory for local variables.

The image below shows the above sketch running and reporting back the number of free bytes of SRAM available from the freeRam() function.

Arduino SD Card Web Server with LCD

Arduino SD Card Web Server with LCD Displaying Free SRAM after using the F() Macro

From this comparison of sketches on the Arduino Uno, it can be seen that the main limiting factor on the Arduino Uno is the amount of available memory. A relatively short sketch that uses functions or objects from several libraries can quickly use up 70% of available memory. Any sketch that gets slightly more complex can start approaching the 100% memory usage mark.

When available RAM becomes limited, there is good chance that the sketch can start exhibiting unexpected behaviour and even crash as the unavailability of RAM results in variables being overwritten. One technique has been shown that can help to reduce the amount of RAM used by a sketch.