Control and Monitor Arduino Over the Web Using ENC28J60

NanodeUIP Webserver Screens

The NanodeUIP web server provides a mobile-optimized web page to control and monitor your Ethernet-connected Arduino. All you need is an Arduino and an ENC28J60-based Ethernet shield, or get a Nanode which combines the two together.

The home page shows the three functions the webserver can handle:

  • Monitor Buttons
  • Control Lights
  • Monitor Sensors

Buttons

The ‘Buttons’ page shows the current state, HIGH or LOW, of a pre-defined set of digital input pins. See ‘Configuring’ below for details on how to change which pins are reported and what they’re labeled.

Press the ‘Refresh’ button to update the current state.

Lights

The ‘Lights’ page allows control of a pre-defined set of digital output pins. While they’re called lights, they could really control anything like a motor or camera or anything the Arduino can do.

Sensors

The ‘Sensors’ page shows the current value of a pre-defined set of analog input pins. Press the ‘Refresh’ button to update the current state.

jQuery Mobile

The UI relies on jQuery Mobile for a pretty interface that’s optimized for a mobile phone. All the CSS and JavaScript files are hosted by jQuery, so the Arduino web server just needs to point the browser at the right place to go get them. The extra markup doubles the size of the HTML, but there is still plenty of flash memory to spare.

NanodeUIP

NanodeUIP is an Arduino-compatible library for ENC28J60-based Ethernet hardware, like Nanode and EtherShield. It’s based on uIP, a mature, stable, well-documented TCP/IP stack. The library was originally created by Stephen Early based on Adam Dunkels’ uIP and Guido Socher’s enc28j60 driver. My fork adds numerous examples, including this one.

Getting it

This web server is included in my NanodeUIP fork as an example, see examples/webserver.

Using a Nanode, you can upload this sketch straight away to your unit and all is well. You can watch the boot-up messages in the serial monitor, and this will tell you what IP address it used. Load up that IP address in your browser, touch “Lights”, and toggle the “Red Light” switch. You should see the Nanode LED turning on and off as you do it.

The default buttons are on pins 2,3 and 4. Try connecting those to ground and refreshing the ‘Buttons’. The default sensors are on analog 0 and 1, so likewise try connecting those to ground or VCC and refresh to see the values change.

Using on EtherShield

For an EtherShield, it’s almost as easy. There is a line right at the top of the sketch you’ll want to un-comment. After modification, the first line of code says this:

#define ETHERSHIELD // uncomment to run on EtherShield

For a regular EtherShield, with the chip select pin on #10, that is all you need to change. If you’ve got something custom with a special CS pin, change the networking bringup section in setup(). Change the ‘SS’ in this line to the pin you need.

uip.init(macaddr,SS);

Configuration

The main thing a person needs to change in order to make the web server useful is to describe the pins which need monitoring and control. This is done in the webserver.ino file. Look for the “Pin Descriptions” section.

/**************************************************************************/
/**
* @defgroup pins Pin Descriptions
*
* The main purpose of this app is to control or monitor a set of pins
* from a web server.  This sections defines which pins are controlled
* and what they are called.
*
* @{
*/

The first code block lists the human-readable labels of all the pins. What do you want to call them? Each label needs its own line here, because it must individually be placed into program (flash) memory.

/**
* Easy-to-remember names of pins used in this sketch
*/
const char led_red_str[] PROGMEM = "Red Light";
const char led_yellow_str[] PROGMEM = "Yellow Light";
const char led_green_str[] PROGMEM = "Green Light";
const char button_a_str[] PROGMEM = "Button A";
const char button_b_str[] PROGMEM = "Button B";
const char button_c_str[] PROGMEM = "Button C";
const char analog_0_str[] PROGMEM = "Analog 0";
const char analog_1_str[] PROGMEM = "Analog 1";

The following block defines which digital output pins are controllable through the ‘Lights’ page.

/**
* Lights controlled by the web server
*/
const pin_def_t lights[] PROGMEM =
{
  { led_red_str, led_red },
  { led_yellow_str, led_yellow },
  { led_green_str, led_green },
  { 0,0 } // terminator
};

The following block defines which digital input pins are monitored on the ‘Buttons’ page.

/**
* Buttons monitored by the web server
*/
const pin_def_t buttons[] PROGMEM =
{
  { button_a_str, button_a },
  { button_b_str, button_b },
  { button_c_str, button_c },
  { 0,0 } // terminator
};

The following block defines which analog input pins are monitored on the ‘Sensors’ page.

/**
* Sensors monitored by the web server
*/
const pin_def_t sensors[] PROGMEM =
{
  { analog_0_str, 0 },
  { analog_1_str, 1 },
  { 0,0 } // terminator
};

Finally, the following function is called at startup to set the pins to the right input/output direction, and to register the pins with the CGI handler.

/**
* Ensure the pins are set correctly, and register them with the
* web server.
*/
void setup_pins()
{
  // led's are outputs
  pinMode(led_red,OUTPUT);
  pinMode(led_yellow,OUTPUT);
  pinMode(led_green,OUTPUT);
 
  // buttons are inputs
  pinMode(button_a,INPUT);
  digitalWrite(button_a,HIGH);
  pinMode(button_b,INPUT);
  digitalWrite(button_b,HIGH);
  pinMode(button_c,INPUT);
  digitalWrite(button_c,HIGH);
 
  // Tell the CGI which pins we care about
  cgi_register(buttons,lights,sensors);
}
/** @} */

Flash Filesystem

The web server builds a simple file system in program memory (flash), so it can serve those files back out to clients who request them. It is not necessary to modify the files on the flash filesystem under normal use. For more advanced use, it’s interesting to look under the covers. For a good explanation of what’s involved, let’s take a look at the Makefile in the fs directory.

The purpose of the Makefile is to create three files in the root directory. fs.cpp will contain the files themselves, transalated into C arrays so they can be processed by the compiler. files.h lists those files out, so they can be referenced by dir.h which works as a directory listing of files.

OUTDIR = ..
ALLFILES = $(OUTDIR)/fs.cpp $(OUTDIR)/files.h $(OUTDIR)/dir.h
all: $(ALLFILES)

The INFILES are the files from disk which will be compiled into the flash filesystem.

INFILES = $(wildcard *.html) $(wildcard *.shtml) $(wildcard *.txt) $(wildcard *.js)

The first step is to run xxd on all the input files, turning them into C files. These C files are collected in the xxd directory. They are run through an awk script to put them into program memory.

XXDDIR = xxd
XXDFILES = $(addprefix $(XXDDIR)/, $(addsuffix .cpp, $(INFILES)))
$(XXDDIR)/%.cpp : %
       xxd -i $< | gawk -f ../xxd.awk > $@

Next, the files are scanned into a directory structure using another pair of awk scripts. dir.h contains entries for each file, telling us the name of each file, where it’s located in memory, and how large it is.

$(OUTDIR)/files.h : $(XXDFILES)
       gawk -f ../files.awk $^ > $@
 
$(OUTDIR)/dir.h : $(XXDFILES)
       gawk -f ../dir.awk $^ > $@

Finally, all the C files are collapsed into a single CPP file, and copied up to the sketch folder.

$(OUTDIR)/fs.cpp : $(XXDFILES)
       cat $(XXDFILES) > $@

Common Gateway Interface (CGI)

CGI is the old-time term for web pages generated by code. This harkens back to a day when it was something new, because people actually hand-wrote HTML pages. Sounds shocking now! πŸ™‚

uIP differentiates between three sources of HTML:

  • HTML files (served by the httpd_fs file system)
  • Server-generated HTML files (also served by the file system, but processed for special commands)
  • Scripts (created by the httpd_cgi subsystem)

A file with an .html extention will simply be written straight out to the client when it’s requested.

An .shtml file is an html file with special handling. From an SHTML file, you can include other files, or run scripts.

A script causes one of the registered CGI handlers in httpd_cgi to be called. This is what’s used to generate the dynamic portion of the HTML result, including the current state of pins, and of course selecting which pins to display at all.

18 Comments

Filed under Arduino, Ethernet

18 responses to “Control and Monitor Arduino Over the Web Using ENC28J60

  1. nilrog

    Interresting library compared the the EtherCard even though it might not be as user friendly. But I noticed that it has one huge drawback. I just tried using it for a “simple” web client, pretty much the example code, and it results in huge hex files. I haven’t had time to dig into the library yet but it looks like the whole library is linked to the project regardless of what features you are actually using (just based upon the size of the final hex file). Or it could actually be that the web client part of the library is that huge. But I hope that it doesn’t require that much code.

  2. nilrog

    Yeah, I figured so. It’s either power or small footprint. I have compiled a few of the examples to see the codesize and it looks like I have to ditch uIP even though I like it. It’s just to big and i’m not sure it’s worth it to try and trim it down. I was hoping to have room for both a webclient and webserver but the result is just too big.

    If I just compile a webclient with DHCP the uIP version is almost 100% bigger than the EtherCard version. Even though I have to write the webserver code myself for the EtherCard library I think my code will fit in a 328. With uIP i’m like 2-3k over the limit after turning off all debugging and logging. Btw, NanodeUIP doesn’t seem compile if I turn off logging in uip-conf.h.

    • Combining webserver and client would fit.  I just put the two together, turned off logging, and it came it at 31626, AND that includes a web server with 4400 bytes of files in flash memory.

      P/S Thanks for the bug report about failing with logging off.

      ________________________________

      • nilrog

        Ok, I will try to reduce the size of the files for the webserver and see what I can come down to. For simplicity I just took your files from this project and copied them into my project (I didn’t copy your code in the .ino file for the buttons etc.). That test came in at almost 33000 with all logging turned off.

        My project is really simple, it’s just a webclient polling a server and based upon the answer it will turn on/off a relay. So the amount of code that is specific for this project is not much. I don’t have my figures here now but I think that the project code without webserver and with logging turned on was ~23500. And I can probably reduce that by ~500 by turning of the project logging I have added.

        I will try that tonight and see what the end result will be.

  3. nilrog

    I’m back on track again. Without debug/log code i’m currently ~30000 but there are more things I need in the webif so the code will grow slightly more. But I also discovered that I need to change bootloader because i’m currently limited to ~30700. So i’m going to try to load OptiBoot and see what the limit will be then.The only drawback I see now using uIP is that you cannot use much debugging output. You need to be sparse on that, or test partial bits of your code, but other than that this library rocks if you need to use the ENC28J60 πŸ™‚

    • Yeah, it’s true.  A reasonable solution is to get each component working individually by itself, so there’s more room for logging.

      ________________________________

  4. I am trying to understand how to assign a fixed IP address to this web server. Many options around, in the different *.h files …but which one is the right one ?

    • This part – implementing static IP in the mess of .h files – is the most tricky. Simple stating “1” in UIP_FIXEDADDR results in compiling errors. So, it will be REALLY nice to advice us HOW can we get rid of all this useless DHCP stuff and where shall we state fixed IP so that it will compile. The project runs well on DHCP and I like it _very much_, but I am not skilled in C anough to turn off all unneeded stuff without ruining compilation. PLEASE response.

      • Michele Volpe

        I was able, at last, to find a workaround. In the webserver file, call the set_ip_addr( …) function in the dhcp_status (…) function.

        The code will loook like this:

        static void dhcp_status(int s,const uint16_t *dnsaddr)
        {
        char buf[20];
        if (s==DHCP_STATUS_OK)
        {
        resolv_conf(dnsaddr);
        uip.set_ip_addr(192,168,1,153); //// SET IP ADDRESS !!!!!!!!
        uip.get_ip_addr_str(buf); /// Just to check the IP address
        printf_P(PSTR(“%lu: IP:%s\r\n”),millis(),buf);
        app_flags.have_ip = 1;
        }
        }

        Let me know if that’s OK
        Regards
        Michele

      • Looks pretty good! I hope it worked for you. The original examples from NanodeUIP may be of use, because before I forked it all the examples used static IP setting.

  5. Firts of all thanks for sharing this code. It works on my mega witch (cheap-ass) ethernet module. And is the first one thats stable…
    I’m new to arduino and C(++) , but i managed to control my arduino via the usb on apache, with jquery and somen cgi scripts.
    I want to use/port my own website this way (via the ethernet) and started to edit yours, with no success. What ever i try it dous not compile and i get errors.
    How can I edit the website? or maybe what dependecies do i need, on ubuntu???

  6. I tried to use the nanodeUIP library. I got the following errors.What am I missing?

    uip_hello_world.cpp: In function ‘void setup()’:
    uip_hello_world.pde:-1: error: ‘NanodeUNIO’ was not declared in this scope
    uip_hello_world.pde:-1: error: expected `;’ before ‘unio’
    uip_hello_world.pde:-1: error: ‘unio’ was not declared in this scope
    uip_hello_world.pde:-1: error: ‘NANODE_MAC_ADDRESS’ was not declared in this scope

    jim

  7. Ger

    Hi,
    Thanks for sharing the code…. It looks really well (if I could get it running)
    Im running on Arduino 1.0.1, and when I upload the webserver sketch it fails to compile – with error that NanodeUNIO was not declared. I’m somewhat new to Arduino’s so please forgive me if the answer to this is obvious, but I think it is due to the fact that the nanode has failed to load Nanodeunio.h – as I cannot see this header file anywhere in the downloaded ZIP file.
    I assume this file should be there – Maybe you can shed some light.
    Many thanks in anticipation
    Ger

    • As it says in the README for the NanodeUIP library, this code depends on the NanodeUNIO library. Or, if you are NOT using a Nanode, and instead using an EtherShield or some variant, please see the first line of code in the webserver.ino example. Un-comment that, and it will skip the inclusion of the NanodeUNIO library.

  8. milos

    Hello maniacbug,

    Great job on the library. I have a question. Can the default port 80 of webserver be changed to other port?

    Thank you for reply

Leave a comment