Web-based control of an RGB LED via the Nanode, an Arduino compatible microcontroller

February 12, 2012 | Arduino, electronics | By: Mark VandeWettering

Okay, I finally got some time to record a video about a simple little webserver project I hacked together earlier this week. My wife Carmen got me a Nanode kit from Wicked Device around Christmas. These are Arduino compatible development boards which include a Microchip ENC28J60 Ethernet device to enable web connectivity. One slight drawback is that they are not compatible with the Ethernet shields which are based upon the Wiznet 5100 chipsets, so you’ll need different drivers and different code. I’ve been experimenting with the Jeelabs EtherCard drivers, which seem to work well and are well supported, although they are limited in their TCP implementation (it limits TCP responses to just a single TCP/IP packet). Nevertheless, I implemented a simple webserver to control the I/O pins of the Nanode, based upon the ideas (but not the code) of Jason Gullickson’s RESTduino. I then did a short demo of how you could use it to control a strip of RGB LEDs from a simple jQuery based interface. Watch:



Here’s the sketch. Feel free to swipe it and any ideas for any web enabled projects you need (nothing here is worth copyrighting or licensing at all, consider it public domain).

#include <EtherCard.h>
#include <NanodeUNIO.h>

//
// remote by Mark VandeWettering
//
// a reimplementation of the "RESTduino" idea, first published at:
//     http://jasongullickson.posterous.com/restduino-arduino-hacking-for-the-rest-of-us
//
// I decided to rewrite this application because:
//   1. originally, it was written for the Wiznet based Ethernet Shield, but I have 
//      mostly Nanodes, based upon the ENC28J60
//   2. It was ported to the Ethershield library by Andrew Lindsay, but Andy has decided
//      that he's decided not to continue development of that library, deferring to the 
//      EtherCard library being developed by JeeLabs
//   3. I thought there was a lot of cool additions that could be made!  Okay, I haven't
//      implemented any at the moment, but I will!
//

#define DEBUG 		1
#define BUFFER_SIZE 	(900)

byte Ethernet::buffer[BUFFER_SIZE] ;
BufferFiller bfill;

char okHeader[] PROGMEM = 
    "HTTP/1.0 200 OK\r\n"
    "Content-Type: text/html\r\n"
    "Pragma: no-cache\r\n"
    "\r\n" ;
    
char responseHeader[] PROGMEM = 
    "HTTP/1.1 200 OK\r\n"
    "Content-Type: text/html\r\n"
    "Access-Control-Allow-Origin: *\r\n"
    "\r\n" ;

void
homepage(BufferFiller& buf)
{
  buf.emit_p(PSTR(
	"$F"
	"<html>"
	"<head>"
	"<title>webserver remote</title>"
	"<style type=\"text/css\">"
	"body 	{ width: 640px ; }"
	"</style>"
	"</head>"
	"<body>"
	"<h1>webserver remote</h1>"
	"<p>"
	"This webserver is running on a <a href=\"http://nanode.eu\">Nanode</a>, an "
	"Arduino-compatible microcontroller with web connectivity. It uses the <a"
	"href=\"https://github.com/jcw/ethercard\">EtherCard library</a> written by "
	"<a href=\"http://jeelabs.net/projects/cafe/wiki/EtherCard\">JeeLabs</a>.  "
        "Inspired by Jason Gullickson's <a href=\"http://www.youtube.com/watch?v=X-s2se-34-g\">RESTduino</a>."
	"</p>"
	"<hr/>"
	"<p style=\"text-align: right;\">Written by Mark VandeWettering</p>"
	"</body>"
	"</html>"
    ), okHeader) ;
}

#define CODE_ERROR          (-1)
#define CODE_WRITE          (0)
#define CODE_READ           (1)
#define CODE_ANALOG_WRITE   (2)
#define CODE_INDEX          (3)

int
cmdparse(const char *cmd, int &pin, int &val)
{
    const char *cp = cmd+4 ;

    if (*cp++ == '/') {
	if (*cp == ' ') {
	    return CODE_INDEX ;
	} else if (isdigit(*cp)) {
	    pin = atoi(cp) ;
	    while (isdigit(*cp)) cp++ ;
	    if (*cp == ' ') {
		return CODE_READ ;
	    } else if (*cp == '/') {
		cp++ ;
		if (isdigit(*cp)) {
		    val = atoi(cp) ;
		    while (isdigit(*cp)) cp++ ;
		    if (*cp == ' ') 
			return CODE_ANALOG_WRITE ;
		    else
			return CODE_ERROR ;
		} else if (strncmp(cp, "HIGH", 4) == 0) {
		    cp += 4 ;
		    if (*cp == ' ') {
			val = 1 ;
			return CODE_WRITE ;
		    } else 
			return CODE_ERROR ;
		} else if (strncmp(cp, "LOW", 3) == 0) {
		    cp += 3 ;
		    if (*cp == ' ') {
			val = 0 ;
			return CODE_WRITE ;
		    } else 
			return CODE_ERROR ;
		}
	    } else
		return CODE_ERROR ;
	} else {
	    return CODE_ERROR ;
	}
    } else {
	return CODE_ERROR ;
    }
}


void
setup()
{
    byte mac[6] ;

    Serial.begin(9600) ;
    Serial.println("\nbrainwagon remote webserver\n") ;

    NanodeUNIO unio(NANODE_MAC_DEVICE) ;
    unio.read(mac, NANODE_MAC_ADDRESS, 6) ;

    if (ether.begin(sizeof Ethernet::buffer, mac) == 0) {
	Serial.println("Failed to access Ethernet controller.") ;
	for (;;) ;
    }

    if (ether.dhcpSetup()) {
	ether.printIp("IP:  ", ether.myip) ;
	ether.printIp("GW:  ", ether.gwip) ;
	ether.printIp("DNS: ", ether.dnsip) ;
    } else {
	Serial.println("DHCP failed.\n") ;
	for (;;) ;
    }
}

void
loop()
{
  word len = ether.packetReceive() ;
  word pos = ether.packetLoop(len) ;
  int pin, val ;
  
  if (pos) {
    bfill = ether.tcpOffset() ;
    char *data = (char *) Ethernet::buffer + pos ;
    Serial.println(data) ;
    switch (cmdparse(data, pin, val)) {
    case CODE_READ:
        pinMode(pin, INPUT) ;
        bfill.emit_p(PSTR(
          "$F"
          "{\"$D\" : \"$S\"}"),
          responseHeader,
          pin, val ? "HIGH" : "LOW") ;
        break ;
    case CODE_WRITE:
        pinMode(pin, OUTPUT) ;
        digitalWrite(pin, val ? HIGH : LOW) ;
        bfill.emit_p(PSTR(
            "$F"), responseHeader) ;
        break ;
    case CODE_ANALOG_WRITE:
        pinMode(pin, OUTPUT) ;
        analogWrite(pin, val) ;
        bfill.emit_p(PSTR(
            "$F"), responseHeader) ;
        break ;
    case CODE_INDEX:
        homepage(bfill) ;
        break ;
    case CODE_ERROR:
    default:
        bfill.emit_p(PSTR(
            "HTTP/1.0 404 Not Found\r\n"
            "Content-Type: text/html\r\n"
            "\r\n"
            "<html><body><h1>404 Not Found</h1></body></html>")) ;
        break ;
    }
    ether.httpServerReply(bfill.position()) ;
  }
}

Comments

Pingback from WickedDevice Blog » Blog Archive » Nanode video by Mark VandeWettering
Time 8/9/2012 at 12:38 pm

[…] One of our customers, Mark VandeWettering, has a pretty nifty video of the Nanode acting as a webserver to change LED strip colors. The blog post is here. […]

Comment from Leon Wright
Time 2/26/2013 at 6:07 am

Mark, is this on github or something like that? Made some minor adjustments to make the JSON easier to handle and also return a { “result” : “success” } after setting a pin.

Aside from the code works nicely and will integrate with the web app I’m working for the Raspberry Pi!

Comment from Awais
Time 6/5/2016 at 7:07 am

Hi Mark,

This is Awais. I am working with Nanode and would like to do the same, controlling a RGB LED with sliders and camera video on the same web page. How can I accomplish this? Can you help me in this matter?
Waiting for your reply.
With kind regards,
Awais

Pingback from [Arduino] Upravljanje relejev prek spleta – David Bajda
Time 9/13/2016 at 2:24 pm

[…] Koda je v originalu spisana za Nanode, ki ima ta ?ip že vgrajen na ploš?i, koda pa je na voljo tukaj: [Brainwagon.org] Web-based control of an RGB LED via the Nanode … […]