Arduino Mega JSON REST interfaces (for HTML GUIs)

I have been writing a paper for school about how JSON REST interfaces can be used to build HTML5 GUIs for microcontroller systems.

I mated two Arduino libraries Webduino and aJson and was able to create somewhat automatized CRUD.

Online version of the paper can be found here:
http://www.cs.helsinki.fi/u/ljlukkar/iot/

I also made two examples.

A back-end for TodoMVC: https://github.com/lasselukkari/Todo

Weather Station:
https://github.com/lasselukkari/Weather-Station

The idea is that Arduino serves an index page that contains references to JavaScript and CSS that are loaded from another server:

Hi llukkari!

It seems very interresting. As soon as I'll have some spare time I'd like to try your code. Would it be possible to have an example about how to manage some of the PIN's status (something like ON/OFF).

Also, I'd want to ask you what do you think about using a database in the middle and just querying the database to show the status of the Arduino PIN's (the values and the status of the PIN's would be pushed into the database using 2 services: one on the arduino and one listener on the webserver that registers all into the DB at a predefined time range).

The use of the database will avoid "overloading" with requests the Arduino board

Flx:
Would it be possible to have an example about how to manage some of the PIN's status (something like ON/OFF).

I have an 8-channel relay board and I have been planning to write an example how it could be used as a timer controlled by a web gui. I'll send you a pm once I get it done and uploaded to github.

Hi llukkari,

this sound really interesting, I've build something similar using the board just as AJAX/JSON server and loading the page locally from the device (smartphone or PC). The original files was attached in this post.

If you are planning to develop a web interface for a remote control of relays or similars, you may think about an integration with the home automation project Souliss, so that from a single point interface you may control many devices over your home. In the project there is now an Android interface.

If you are interested in, have a look at www.souliss.net or search for Souliss into that forum.

Regards,
Dario.

llukkari:

Flx:
Would it be possible to have an example about how to manage some of the PIN's status (something like ON/OFF).

I have an 8-channel relay board and I have been planning to write an example how it could be used as a timer controlled by a web gui. I'll send you a pm once I get it done and uploaded to github.

Thanks a lot !!!

H2SO4:

llukkari:

Flx:
Would it be possible to have an example about how to manage some of the PIN's status (something like ON/OFF).

I have an 8-channel relay board and I have been planning to write an example how it could be used as a timer controlled by a web gui. I'll send you a pm once I get it done and uploaded to github.

Thanks a lot !!!

I'm sorry that it has taken so long.

Here is a simple example how to control LOW/HIGH status of a pin.

Settings are saved after eatch change and loaded when the progrmam starts.

Source: https://github.com/lasselukkari/PinToggle/blob/master/pintoggle.ino

#include "SPI.h"
#include "Ethernet.h"
#include "SD.h"
#include "aJSON.h"
#include "WebServer.h"

//define range of pins you want to control
#define FROM_PIN 55
#define TO_PIN 58

byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };

WebServer webserver("", 80);
aJsonObject* pins;
File file;

void indexCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) {
	server.httpSuccess();
	if (type != WebServer::HEAD) {
		P(helloMsg) =
				"<!DOCTYPE html>\n<html lang=\"en\">\n"
				"<head>\n"
				"\t<meta charset=\"utf-8\">\n"
				"\t<title>Pin Toggle</title>\n"
				"\t<link href=\"http://www.cs.helsinki.fi/u/ljlukkar/pincontrol/css/style.css\" rel=\"stylesheet\">\n"
				"\t<link href=\'http://fonts.googleapis.com/css?family=Open+Sans+Condensed:700,300\' rel=\'stylesheet\' type=\'text/css\'/>\n"
				"</head>\n"
				"<body>\n"
				"<div class=\"container\">\n"
				"\t<div id=\"pin-list\">\n"
				"\t</div>\n"
				"</div>\n"
				"<script src=\"http://www.cs.helsinki.fi/u/ljlukkar/pincontrol/js/jquery.min.js\"></script>\n"
				"<script src=\"http://www.cs.helsinki.fi/u/ljlukkar/pincontrol/js/lodash.min.js\"></script>\n"
				"<script src=\"http://www.cs.helsinki.fi/u/ljlukkar/pincontrol/js/lib/backbone-min.js\"></script>\n"
				"<script src=\"http://www.cs.helsinki.fi/u/ljlukkar/pincontrol/js/models/pin.js\"></script>\n"
				"<script src=\"http://www.cs.helsinki.fi/u/ljlukkar/pincontrol/js/collections/pins.js\"></script>\n"
				"<script src=\"http://www.cs.helsinki.fi/u/ljlukkar/pincontrol/js/views/pins.js\"></script>\n"
				"<script src=\"http://www.cs.helsinki.fi/u/ljlukkar/pincontrol/js/views/app.js\"></script>\n"
				"<script src=\"http://www.cs.helsinki.fi/u/ljlukkar/pincontrol/js/app.js\"></script>\n</body>\n"
				"</html>";
		server.printP(helloMsg);
	}
}

// this method is performed before the reguest is prosessessed and default responce is generated
// server.setPreventDefault(true); disables the default prosessing and responce once
void switchBeforeResposeCmd(WebServer &server, WebServer::ConnectionType type, char * tail, bool tailComplete,
		aJsonObject ** collection, aJsonObject ** model) {
	if (type == WebServer::PUT) {
		if (model != NULL) {
			pinMode(aJson.getObjectItem(*model, "pin")->valueint, (bool) aJson.getObjectItem(*model, "low")->valuebool);
		}
	}

}

// this method is performed after the responce is sent so it doens't block it anymore
void switchAfterResposeCmd(WebServer::ConnectionType type, aJsonObject ** collection, aJsonObject ** model) {
	pinMode(53, OUTPUT);

	if (SD.exists("pin.txt")) {
		SD.remove("pin.txt");
	}

	file = SD.open("pin.txt", FILE_WRITE);
	aJson.print(&**collection, file);
	file.close();
}

void setup() {
	Serial.begin(9600);
	pinMode(53, OUTPUT);

	if (!SD.begin(4)) {
		Serial.println("SD initialization failed!");
	}

	if (SD.exists("pin.txt")) { //load settings file if found
		file = SD.open("pin.txt");
		pins = aJson.parse(file);
		file.close();

		aJsonObject* current = pins->child;

		while (current) {
			pinMode(aJson.getObjectItem(current, "pin")->valueint,
					(bool) aJson.getObjectItem(current, "low")->valuebool);
			current = current->next;
		}

	} else { //if not found populate dta
		pins = aJson.createArray();

		for (int i = FROM_PIN; i < TO_PIN + 1; i++) {
			pinMode(i, LOW);
			pinMode(i, OUTPUT);
			aJsonObject* pin = aJson.createObject();
			aJson.addItemToObject(pin, "id", aJson.createItem(i - FROM_PIN + 1));
			aJson.addItemToObject(pin, "pin", aJson.createItem(i));
			aJson.addItemToObject(pin, "low", aJson.createItem(false));
			aJson.addItemToArray(pins, pin);
		}
	}

	//add binding to webserver and set actions
	webserver.addJSONBinding("pins", &pins);
	webserver.setBeforeRequest("pins", &switchBeforeResposeCmd);
	webserver.setAfterRequest("pins", &switchAfterResposeCmd);

	webserver.setDefaultCommand(&indexCmd);

	Ethernet.begin(mac);
	webserver.begin();

	for (byte i = 0; i < 4; i++) {
		Serial.print(Ethernet.localIP()[i]);
		if (i < 3) {
			Serial.print('.');
		}
	}
	Serial.println();
}

void loop() {
	webserver.processConnection();
}

Dummy UI: http://www.cs.helsinki.fi/u/ljlukkar/pincontrol/

I made a another example.
https://github.com/lasselukkari/Arduino-RC-Timer-Switch

This time the UI is controlling RC switches like these:

Features

  • Controll cheap 433mHz AC switches with arduino and web UI
  • No computer needed as a mediator
  • Multipurpose JSON REST interface
  • Teach devices to arduino by pushing the remote controller buttons
  • Remote controller actions are tracked by the arduino
  • Timers
  • Settings saved to a SD card

Dummy test UI: http://www.cs.helsinki.fi/u/ljlukkar/rcswitch

Setup:

Hi llukkari,

I am trying to do a project very similar to your weather station project. However I do not have access to a remote server that can host css or javascript.

My project box will be located in some remote wooded areas that do not have cell phone coverage or internet access. The project box has an Arduino, Ethernet shield and a wifi router used to create a local network. My project would require the javascript files for graphing to be hosted on the Arduino.

Have you tried to do this project with the css and javascript files embedded on the arduino somehow? Do you have any suggestions on how that might be done?

Thanks,
Tom

Tom,

I understand you will have a local network, and that the Arduino is connected to this via WiFi?

On this local network do you have a file server, maybe even a NAS hard drive (networked hdd)?
If so, then you can store your javascript and css files on this.
In your index html fie on your Arduino, just point to those as external resources.

Or if you have a local web server, then you can set it up to serve from there and have the client request data direct from the Arduino or have the Arduino push data to the server database.

I haven't had the Arduino serve up files such as html, css or js from the onboard SD card, but I believe it can be done albeit slowly and some care is needed around how you do this.

There are many ways to do this, so hopefully one of these ways might provide you with an idea.
Keep us informed if you can, I'd like to see how you progress.

Take a look at my site, in my signature. I am actively developing it at present, up until 2:30am this morning working out SQL commands.

Kind regards,
Paul

Files can be served off of an SD card like below, including picture and .js files. Not terribly fast, but so far seems to work.

//zoomkat 1/25/13
//SD server slider test code
//open serial monitor to see what the arduino receives
//address will look like http://192.168.1.102:84/servosld.htm when submited
//for use with W5100 based ethernet shields
//put the servosld.htm, slider.js, bluev_sl.gif,
//and bluev_bg.gif on the SD card
//files at http://web.comporium.net/~shb/servoslider.htm page


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

byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 
  192, 168, 1, 102 }; // ip in lan
byte gateway[] = { 
  192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 
  255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port
String readString; 

//////////////////////

void setup(){

  Serial.begin(9600);

  // disable w5100 while setting up SD
  pinMode(10,OUTPUT);
  digitalWrite(10,HIGH);
  Serial.print("Starting SD..");
  if(!SD.begin(4)) Serial.println("failed");
  else Serial.println("ok");

  Ethernet.begin(mac, ip, gateway, gateway, subnet);

  //delay(2000);
  server.begin();
  Serial.println("Ready");

}

void loop(){
  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {
          //store characters to string 
          readString += c; 
          //Serial.print(c);
        } 
        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString); //print to serial monitor for debuging 

          client.println("HTTP/1.1 200 OK"); //send new page
          if(readString.indexOf("servosld") >=0) {
          client.println("Content-Type: text/html");
          client.println(); }

          if(readString.indexOf("slider") >=0) {
          client.println("Content-Type: application/x-javascript");
          client.println(); }
          
          if(readString.indexOf("bluev") >=0) {
          client.println("Content-Type: image/gif");
          client.println(); }

          if(readString.indexOf("servosld") >=0) {
            File myFile = SD.open("SERVOSLD.HTM");
            if (myFile) {
              while (myFile.available()) {
                client.write(myFile.read());
              }
              myFile.close();
            }
          }

          if(readString.indexOf("slider") >=0) {
            File myFile = SD.open("slider.js");
            if (myFile) {
              while (myFile.available()) {
                client.write(myFile.read());
              }
              myFile.close();
            }
          }

          if(readString.indexOf("bluev_sl") >=0) {
            File myFile = SD.open("bluev_sl.gif");
            if (myFile) {
              while (myFile.available()) {
                client.write(myFile.read());
              }
              myFile.close();
            }
          }

          if(readString.indexOf("bluev_bg") >=0) {
            File myFile = SD.open("bluev_bg.gif");
            if (myFile) {
              while (myFile.available()) {
                client.write(myFile.read());
              }
              myFile.close();
            }
          }

          delay(1);
          //stopping client
          client.stop();
          readString="";

        }
      }
    }
  } 
}

tjgerow:
Hi llukkari,

Have you tried to do this project with the css and javascript files embedded on the arduino somehow? Do you have any suggestions on how that might be done?

Thanks,
Tom

You could probably use the sd card. In my hydrobot project, http://www.cs.helsinki.fi/u/ljlukkar/hydrobot/, I was able to use quite large csv files. I can't see any reason why it wouldn't work for other files as well.

You should try to set the http Cache-Control header so that the browser caches the responce and doesn't request it again.

Backbone.js, the MV* framework I have used in all the examples, is one of the most light weight out of them all.

Hi, I really am interested in your hydrobot project. Will you be posting the code for that. I want to attemt to use it in my system.

Thanks
Tom