Category Archives: Arduino

Arduino: Super Graphing Data Logger

The intensity of natural light in my basement.

  1. Introduction
  2. The Results
  3. How to Make One For Yourself


What is the Super Graphing Data Logger (SGDL)? It is an Arduino project that integrates data logging and the graphing of this data online using little more than an Arduino with the appropriate shields and sensors.   It differs from similar projects in that it doesn’t require a separate server or system to collect the data or to run script for the actual plot. Between the Arduino and the user’s browser, everything is taken care of.

Some time back I came across this neat javaScript based library for plotting and graphing called Highcharts JS. It didn’t take long for me to realize that charting with javaScript is very convenient for projects in which the server is limited in it’s capabilities, such as when using an Arduino with the Ethernet shield. Since the user’s browser does all the heavy lifting, the Arduino only needs to serve the files which is something it is perfectly capable of. This is especially true now that the Ethernet and SD libraries included in 1.0 support opening of multiple files simultaneously amongst other things. Thus the use of Highcharts allows us to create beautiful interactive charts based on data logged by the Arduino using nothing but the Arduino (and your browser, and a public javaScript CDN).

The Results

The best way to appreciate the final product is to actually play with it. While I’m not going to open up my home network and Arduino to the big wide internet, I have mirrored the pages and datafiles it produces on the webhost I used for my Has the World Ended Yet? project. You can find them here. These won’t be updated with new datapoints like the actual Arduino version will be, but they should at least give a fair impression of how the project looks and feels without the need to actually implement it.

For those who are unsure what they are looking at, I’ll offer a quick interpretation:

The list of data files available for graphing.
The list of data files available for graphing.

Going to the above page, we see that we are presented with a very basic list of the data files that can be selected from. Clicking any of them will cause  the graph for that datafile to be loaded (much more quickly than the Arduino can manage).

A graph for the first week of data collected.
A graph for the first week of data collected.

This chart for the 25-12-12.CSV file is already complete, and won’t have any new data added to it in the future, because the files for subsequent weeks have already been made. There is a lot to see though. The two data points that are at 1000 on the y-axis are from when I pointed a bright flashlight directly at the photo sensor. All of the data points between 300 and 400 on the y-axis are the result of the basement lights being on. The abnormally large gaps in the data are periods when the Arduino was powered off because I was still tweaking and developing it. Finally, the short humps that occur everyday are the result of natural light coming through one of the basement windows. By zooming in on one of them, we can see even more detail:

The intensity of natural light in my basement.
The intensity of natural light in my basement.

The first thing we notice is that the levels rise from zero to about 65 before falling and levelling out at close to 35 for two hours. This is followed by a another small increase before it ultimately decreases down to a value of ~10 where it levels out. That middle valley where the light levels are equal to 35 is due to the shadow cast  on the basement window by our neighbour’s house to the south of us. The levelling out of the light intensity at 10 after all the daylight has disappeared is because a light out in the hallway is usually on in the evening. It is eventually turned off for the night, causing the light levels to drop to zero where they will usually remain until the next morning. I must admit, I’m impressed that the cheap $1.00 photoresistor is capable of capturing this level of detail, and that these trends are so easily interpreted from the graphs.

How to Make One For Yourself

To replicate this project, a few things are necessary. You’ll obviously need an Arduino capable of connecting over Ethernet and storing files on an SD card. In my case, this is achieved through the use of an Uno with the Ethernet shield. Presumably an Arduino Ethernet model will also work fine, though I have not personally tested it. Other non official Ethernet shields and SD card adapters may also work if they use the same libraries, though I make no guarantees. For the more adventurous, it may be possible to adapt my code to achieve the same functionality using a Wifi shield. You will also need a data source of some sort. For my project I chose to use a very cheap photoresistor, which I rigged up on a small perf board to plug directly into the 5v, gnd, and A0 pins of my Arduino (or more precisely,  the headers on the Ethernet shield). It is set up in such a way that the minimum recordable light intensity is zero, while the maximum is 1024.

The photo sensor board fits like a charm.
The photo sensor board fits like a charm.

One header is bent to reach A0.

The pins on the male headers don’t quite line up, so I intentionally used extra long ones and added a slight S-curve to the one that goes to A0. This can be seen in better detail above. For those who are interested, the circuit is very simple:

The circuit.
The circuit.

Before we get started, we need to make sure our SD card is good to go. It should be formatted as a FAT16 or FAT32 filesystem, the details of which are available on the official Arduino website. Once that is done, we need to ensure two things are present in the root directory of the card: the HC.htm file, and a data/ directory for our datafiles. The data directory is easily made with the same computer that was used to format the card provided one has an SD card reader of some sort. The HC.htm simply consists of the following code:

        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Super Graphing Data Logger!</title>

        <script type="text/javascript" src=""></script>
        <script type="text/javascript">
function getDataFilename(str){
    point = str.lastIndexOf("file=")+4;

    tempString = str.substring(point+1,str.length)
    if (tempString.indexOf("&") == -1){
        return tempString.substring(0,tempString.indexOf("&"));

query  =;

var dataFilePath = "/data/"+getDataFilename(query);

$(function () {
    var chart;
    $(document).ready(function() {
        // define the options
        var options = {
            chart: {
                renderTo: 'container',
                zoomType: 'x',
                spacingRight: 20
            title: {
                text: 'Light levels recorded by the Arduino'
            subtitle: {
                text: 'Click and drag in the plot area to zoom in'
            xAxis: {
                type: 'datetime',
                maxZoom: 2 * 3600000
            yAxis: {
                title: {
                    text: 'Light Levels (0 - 1024)'
                min: 0,
                startOnTick: false,
                showFirstLabel: false
            legend: {
                enabled: false
            tooltip: {
                formatter: function() {
                        return '<b>'+ +'</b><br/>'+
                        Highcharts.dateFormat('%H:%M - %b %e, %Y', this.x) +': '+ this.y;
            plotOptions: {
                series: {
                    cursor: 'pointer',
                    lineWidth: 1.0,
                    point: {
                        events: {
                            click: function() {
                                hs.htmlExpand(null, {
                                    pageOrigin: {
                                        x: this.pageX,
                                        y: this.pageY
                                    maincontentText: Highcharts.dateFormat('%H:%M - %b %e, %Y', this.x) +':<br/> '+
                                    width: 200
            series: [{
                name: 'Light Levels',
                marker: {
                    radius: 2
        // Load data asynchronously using jQuery. On success, add the data
        // to the options and initiate the chart.
        jQuery.get(dataFilePath, null, function(csv, state, xhr) {
            var lines = [],
                // set up the two data series
                lightLevels = [];
            // inconsistency
            if (typeof csv !== 'string') {
                csv = xhr.responseText;
            // split the data return into lines and parse them
            csv = csv.split(/\n/g);
            jQuery.each(csv, function(i, line) {
                // all data lines start with a double quote
                line = line.split(',');
                date = parseInt(line[0], 10)*1000;
                    parseInt(line[1], 10)
            options.series[0].data = lightLevels;
            chart = new Highcharts.Chart(options);
        <p style="text-align:center;">Please allow the chart to load, it may take up to 30 seconds </p>
<script src=""></script>

<!-- Additional files for the Highslide popup effect -->
<script type="text/javascript" src=""></script>
<script type="text/javascript" src="" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="" />

<div id="container" style="min-width: 400px; height: 400px; margin: 0 auto"></div>


You will need to edit this file first to make sure it points towards the preferred  location of your highcharts.js files. You can leave this as the public CDN:, change it to point towards your own webhost, or it can even be on the Arduino’s SD card (this will be slow). It is not necessary to create a datafile before hand, the SGDL sketch will take care of that when it decides to record its first data point. Before we get that far though, it is necessary to make sure we have configured the EEPROM memory for the SGDL sketch. This is very easily accomplished using a separate sketch, which I have called EEPROM_config. This sketch (along with SGDL itself) requires an extra library called EEPROMAnything, which needs to be added to the Arduino’s libraries folder wherever one’s sketchbook folder is. While you’re at it, you should also add the Time library which we need for SGDL.

/* ************************************************************************
 * ***            Super Graphing Data Logger - EEPROM config            ***
 * ************************************************************************
 * Everett Robinson, December 2012.
 * The following extra non standard libraries were used, and will need to be
 * added to the libraries folder:
 * - EEPROMAnything:
 * This sketch helps you set the values in EEPROM which are necessary for
 * Super Graphing Data Logger. It should only need the be run once before
 * the first time you set up SGDL, or in the unlikely event that the EEPROM
 * becomes corrupted.
 * Please ensure that the values in configuration config are appropriate for
 * your project before uncommenting the EEPROM_writeAnything(0, config); line.

#include <EEPROM.h>
#include <EEPROMAnything.h>

typedef struct{
    unsigned long newFileTime;
    char workingFilename[19];
  } configuration;

//This is a one off thing, so everything is in setup
void setup(){
  //Create the config struct to write to EEPROM, change values as appropriate
  //Make sure your filename is not too long for the workingFilename char array 
  configuration config = {1356912000L,"/data/25-12-12.csv"};
  //Write the values to the EEPROM
  //EEPROM_writeAnything(0, config);       //Uncomment when you're sure everything is correct
  configuration config2;                   //Create a second config struct for verification
  Serial.print("The value read from EEPROM for newFileTime is: ");
  Serial.print("The value read from EEPROM for workingFilename is: ");
  Serial.println("If those values are correct then everything went as planned. Otherwise,");
  Serial.println("please double check that the values declared for the struct config are");
  Serial.println("correct and that that EEPROM_writeAnything line is uncommented.");

void loop(){

I have intentionally commented out the write line so that no one writes junk to the EEPROM by accident. While the EEPROM has a life of ~100,000 write cycles, I’d rather not waste any of them. Please review the sketch carefully and ensure you’ve adjusted it accordingly before uploading it to the Arduino. The most important thing is to ensure that your newFileTime is something sensible (in the near future most of all).

Now that that’s all taken care of, we’re ready to get SGDL all set up! The code will need a few adjustments for your own specific setup, mostly in regards to the Ethernet MAC and IP addresses. I trust that anyone making use of this code already knows how to configure their router to work with the Arduino, and that they can find the appropriate local IP address to update this sketch with. You may also wish to change the timeserver IP address to one that is geographically closer to yourself.

I currently have my code set up to make a measurement every 10 minutes, and to create a new data file every week. You are welcome to change those parameters, just be aware that the current data file management names files using a dd-mm-yy.csv date format, so the new file interval should be at least 24 hours. Another concern, is that the shorter the measurement interval and the longer the new data file interval is, the larger the files will be. Because the Arduino is not especially powerful, this will have consequences for the loading times of each chart.

/* ************************************************************************
 * ***                    Super Graphing Data Logger                    ***
 * ************************************************************************
 * Everett Robinson, December 2012. More at:
 * This sketch relies on the SD and ethernet libraries in arduino 1.0 or newer.
 * The following extra non standard libraries were also used, and will need to
 * be added to the libraries folder:
 * - Time:
 * - EEPROMAnything:
 * If this is your first time setting up this project, please go get the
 * EEPROM_config sketch from so that you can 
 * configure the config struct in the EEPROM memory. Usage of the EEPROM 
 * is needed to make the project resiliant against a temporary loss of power.
 * You must also ensure that you have the HC.htm file in the root directory
 * of your SD card, as well as a data directory where the datafiles will be
 * stored.
 * This sketch combines the functionality of an existing fileserver example
 * which can be found at
 * with the Datalogger example that comes with the new SD library from 1.0,
 * as well as some code from the UdpNtpClient example that cones with the
 * ethernet library. 
 * Added to all of these are some tricks to make it manage and serve up the
 * datafiles in conjunction with a page which uses highcharts JS to graph it.
 * This is basically accomplished using the arduino by itself. Because I
 * actually host the highcharts.js files externally, this is true more in
 * theory than in actual practice, but oh well. It should work just fine to
 * have the highcharts.js file on the arduino's SD card, though loading the 
 * page will be painfully slow.
 * Some of the code this was derived from may or may not be under a GPL
 * licence; I'm not entirely sure. I suppose anyone using this should treat 
 * it like it is too, but I don't really care too much.
 * Also if one intends to use this for commercial applications, it may be
 * necessary to purchase a license for Highcharts.
 * Changes:   -------------------------------------------------------------
 * January 2013: Updated so that the dd-mm-yy.csv file format is properly 
 * followed, all single digit days, months, and years will have a leading 
 * zero now. 

#include <SD.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SPI.h>
#include <string.h>
#include <Time.h>
#include <EEPROM.h>
#include <EEPROMAnything.h>
#include <avr/pgmspace.h>

/************ ETHERNET STUFF ************/
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x4C, 0x64 };
byte ip[] = { 192,168,1, 100 };
EthernetServer server(80);

/************** NTP STUFF ***************/
unsigned int localPort = 8888;          // local port to listen for UDP packets
IPAddress timeServer(132, 163, 4, 101); //NIST time server IP address: for more info

const int NTP_PACKET_SIZE= 48; //NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 
EthernetUDP Udp;

const int analogPin = 0;
unsigned long lastIntervalTime = 0; //The time the last measurement occured.
#define MEASURE_INTERVAL 600000     //10 minute intervals between measurements (in ms)
unsigned long newFileTime;          //The time at which we should create a new week's file
#define FILE_INTERVAL 604800        //One week worth of seconds

//A structure that stores file config variables from EEPROM
typedef struct{                     
    unsigned long newFileTime;      //Keeps track of when a newfile should be made.
    char workingFilename[19];       //The path and filename of the current week's file
} configuration;
configuration config;               //Actually make our config struct

// Strings stored in flash mem for the Html Header (saves ram)
prog_char HeaderOK_0[] PROGMEM = "HTTP/1.1 200 OK";            //
prog_char HeaderOK_1[] PROGMEM = "Content-Type: text/html";    //
prog_char HeaderOK_2[] PROGMEM = "";                           //

// A table of pointers to the flash memory strings for the header
PROGMEM const char *HeaderOK_table[] = {   

// A function for reasy printing of the headers  
void HtmlHeaderOK(EthernetClient client) {
    char buffer[30]; //A character array to hold the strings from the flash mem
    for (int i = 0; i < 3; i++) {
      strcpy_P(buffer, (char*)pgm_read_word(&(HeaderOK_table[i]))); 
      client.println( buffer );
// Strings stored in flash mem for the Html 404 Header
prog_char Header404_0[] PROGMEM = "HTTP/1.1 404 Not Found";     //
prog_char Header404_1[] PROGMEM = "Content-Type: text/html";    //
prog_char Header404_2[] PROGMEM = "";                           //
prog_char Header404_3[] PROGMEM = "<h2>File Not Found!</h2>"; 

// A table of pointers to the flash memory strings for the header
PROGMEM const char *Header404_table[] = {   

// Easy peasy 404 header function
void HtmlHeader404(EthernetClient client) {
    char buffer[30]; //A character array to hold the strings from the flash mem
    for (int i = 0; i < 4; i++) {
      strcpy_P(buffer, (char*)pgm_read_word(&(Header404_table[i]))); 
      client.println( buffer );

void setup() {
  pinMode(10, OUTPUT);          // set the SS pin as an output (necessary!)
  digitalWrite(10, HIGH);       // but turn off the W5100 chip! 
  // see if the card is present and can be initialized:
  if (!SD.begin(4)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
  Serial.println("card initialized.");
  // The SD card is working, start the server and ethernet related stuff!
  Ethernet.begin(mac, ip);
  EEPROM_readAnything(0,config); // make sure our config struct is syncd with EEPROM

// A function that takes care of the listing of files for the
// main page one sees when they first connect to the arduino.
// it only lists the files in the /data/ folder. Make sure this
// exists on your SD card.
void ListFiles(EthernetClient client) {
  File workingDir ="/data");
    while(true) {
      File entry =  workingDir.openNextFile();
       if (! entry) {
       client.print("<li><a href=\"/HC.htm?file=");

// A function to get the Ntp Time. This is used to make sure that the data
// points recorded by the arduino are referenced to some meaningful time
// which in our case is UTC represented as unix time (choosen because it 
// works simply with highcharts without too much unecessary computation).
unsigned long getTime(){
  sendNTPpacket(timeServer); // send an NTP packet to a time server

  // wait to see if a reply is available
  if ( Udp.parsePacket() ) {  
    // We've received a packet, read the data from it,NTP_PACKET_SIZE);  // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;  
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;     
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;  
    // return Unix time:
    return epoch;

// send an NTP request to the time server at the given address,
// necessary for getTime().
unsigned long sendNTPpacket(IPAddress& address){
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:         
  Udp.beginPacket(address, 123); //NTP requests are to port 123

// How big our line buffer should be for sending the files over the ethernet.
// 75 has worked fine for me so far.
#define BUFSIZ 75

void loop(){
  if ((millis() % lastIntervalTime) >= MEASURE_INTERVAL){ //Is it time for a new measurement?
    char dataString[20] = "";
    int count = 0;
    unsigned long rawTime;
    rawTime = getTime();

    while((rawTime == 39) && (count < 12)){     //server seems to send 39 as an error code
      delay(5000);                              //we want to retry if this happens. I chose
      rawTime = getTime();                      //12 retries because I'm stubborn/persistent.
      count += 1;                               //NIST considers retry interval of <4s as DoS
    }                                           //attack, so fair warning.
    if (rawTime != 39){                         //If that worked, and we have a real time
      //Decide if it's time to make a new file or not. Files are broken
      //up like this to keep loading times for each chart bearable.
      //Lots of string stuff happens to make a new filename if necessary.
      if (rawTime >= config.newFileTime){
        int dayInt = day(rawTime);
        int monthInt = month(rawTime);
        int yearInt = year(rawTime);
        char newFilename[18] = "";
        char dayStr[3];
        char monthStr[3];
        char yearStr[5];
        char subYear[3];
        if (dayInt < 10){
        if (monthInt < 10){
        //we only want the last two digits of the year
        memcpy( subYear, &yearStr[2], 3 );
        //make sure we update our config variables:
        config.newFileTime += FILE_INTERVAL;
        //Write the changes to EEPROM. Bad things may happen if power is lost midway through,
        //but it's a small risk we take. Manual fix with EEPROM_config sketch can correct it.
        EEPROM_writeAnything(0, config); 
      //get the values and setup the string we want to write to the file
      int sensor = analogRead(analogPin);  
      char timeStr[12];
      char sensorStr[6];
      //open the file we'll be writing to.
      File dataFile =, FILE_WRITE);
      // if the file is available, write to it:
      if (dataFile) {
        // print to the serial port too:
      // if the file isn't open, pop up an error:
      else {
        Serial.println("Error opening datafile for writing");
      Serial.println("Couldn't resolve a time from the Ntp Server.");
    //Update the time of the last measurment to the current timer value
    lastIntervalTime = millis();
  //No measurements to be made, make sure the webserver is available for connections.
    char clientline[BUFSIZ];
    int index = 0;
    EthernetClient client = server.available();
    if (client) {
      // an http request ends with a blank line
      boolean current_line_is_blank = true;
      // reset the input buffer
      index = 0;
      while (client.connected()) {
        if (client.available()) {
          char c =;
          // If it isn't a new line, add the character to the buffer
          if (c != '\n' && c != '\r') {
            clientline[index] = c;
            // are we too big for the buffer? start tossing out data
            if (index >= BUFSIZ) 
              index = BUFSIZ -1;
            // continue to read more data!
          // got a \n or \r new line, which means the string is done
          clientline[index] = 0;
          // Print it out for debugging
          // Look for substring such as a request to get the root file
          if (strstr(clientline, "GET / ") != 0) {
            // send a standard http response header
            // print all the data files, use a helper to keep it clean
            client.println("<h2>View data for the week of (dd-mm-yy):</h2>");
          else if (strstr(clientline, "GET /") != 0) {
            // this time no space after the /, so a sub-file!
            char *filename;
            filename = strtok(clientline + 5, "?"); // look after the "GET /" (5 chars) but before
            // the "?" if a data file has been specified. A little trick, look for the " HTTP/1.1"
            // string and turn the first character of the substring into a 0 to clear it out.
            (strstr(clientline, " HTTP"))[0] = 0;
            // print the file we want
            File file =,FILE_READ);
            if (!file) {
            int16_t c;
            while ((c = > 0) {
                // uncomment the serial to debug (slow!)
          else {
            // everything else is a 404
      // give the web browser time to receive the data

Arduino Project: HTML to LCD

The original source code for this project can be found Here, and an updated version of it for Arduino-1.0 can be found Here.


  1. Arduino (I use the UNO, but I think the older revisions should also work)
  2. An Ethernet Shield
  3. 16×2 Basic Character LCD (There are many more colour options than just white on black)
  4. An Ethernet cable (I just used an old one lying around)
  5. A Breadboard  or some other means to connect the LCD to the Arduino and one 2.2 kΩ resistor


This project combines the above to turn the Arduino into web server which is hooked up to the LCD. It produces a simple HTML web page, from which the user may see what text is currently displayed on the LCD, and provides them the opportunity to change the text using simple input forms. The hardware side of this project is fairly simple, and there were no physical hacks or modifications that needed to be made. The real challenge to this project were working within the limitations of the Arduino as a computing device.


As we can see, the Ethernet shield is plugged into the Arduino, and is hooked up to the breadboard and LCD according to the scheme outlined in the comments of the source code. This one is completely set up and running, since there is already some text displayed on the LCD. The Arduino’s digital pins 4, 10, 11, 12 and 13 are left free since they are utilized by the shield. All the remaining pins are used by the LCD, so unfortunately this means all of the digital IO pins are used which restricts future additions to this project . The Arduino is also being powered over the USB cable, since this allows me to use the serial monitor to debug, and also because the Wiznet chip and voltage regulator get very hot when my 9V wall adapter is used. I have read that this may be because the shield draws a fair amount of current, and the linear voltage regulator reduces the voltage in accordance with the equation       P = I*ΔV. The power is proportional to the current draw, and it is dissipated by the voltage regulator in the form of heat.

With those notes aside, we can connect to the web page by typing in the local network IP address of the Arduino, which I have set up to be fixed using my router’s DHCP reservation functionality. The Arduino did not automatically receive an IP address when the Ethernet cable was first plugged in, so it was necessary to manually add the it to the router’s client list using the mac address on the underside of the shield.

On the web page served by the Arduino it tells us what is currently displayed by the LCD, and it provides us with two boxes to enter more text, one for each row. Each row is limited by the HTML code to 16 characters which provides instant feedback about the limitations of our setup to the user.

Typing in a couple of new lines and clicking the submit button causes the page to refresh and update with what we just entered! In a related issue, due to the RAM limitations of the Arduino and the way symbols are encoded for a URL (a % sign followed by two Hex digits), symbols initially use three times as many bytes as normal alpha-numeric characters. Therefore, if a user were to enter nothing but symbols in each field, the Arduino would crash due to memory issues. To solve this problem I added some code to limit the length of the raw text accepted, but this means that if a user enters too many symbols, the second line will be truncated. In fact, if nothing but symbols are entered into both lines, the second line will be truncated to nothing. It is an unsatisfactory solution, but it’s not an issue if the user uses symbols responsibly.

 Putting these issues aside and returning to our example, we can see that the the web page is now displaying what we entered earlier, and that the LCD also reflects the changes made on the web page.

While my Arduino is only hooked up to my local area network, it is possible to release it into the wild world of the Internet, though there is some risk (which I lack the expertise to properly assess) to doing so on your home network. There are some instructions for implementing this at involving changing the settings on your home router and utilizing DynDNS to get a readable and static domain name.

The most difficult aspect of this whole project was without a doubt managing the heavy use of strings within the Arduino’s small amount of  RAM. Lots of work went into preventing any major or obvious memory leaks, but I am not entirely sure that I have gotten them all. To ration the memory effectively, I also utilized the ability of the Arduino to store constants in flash memory for the strings of HTML code that are sent to the client’s browser. This means I have at most only a single line of HTML stored in RAM at any time.

That wraps up my first post and first major Arduino project that involved some real coding and which was more than just random experimentation and tinkering. There is still some work that could be done debugging, and I intend to work on some extreme and border conditions to see if there are any bugs remaining that will cause it to crash or do some other bad thing.

(what fun is it if you don’t try to break it?)

As a reminder, the source code for this project can be found Here.