Difference between revisions of "ECE497 Project: Node.js Weather Station"

From eLinux.org
Jump to: navigation, search
m (Moved to Winter1112 category)
 
(43 intermediate revisions by 3 users not shown)
Line 1: Line 1:
[[Category:ECE497]]
+
[[Category:ECE497Winter1112]]
  
 
Team members: [[user:Caogecym | Yuming Cao]], [[user:Ziyi Zhang | Ziyi Zhang]]
 
Team members: [[user:Caogecym | Yuming Cao]], [[user:Ziyi Zhang | Ziyi Zhang]]
  
== Executive Summary ==
+
== Grading ==
  
Give two sentence intro to the project.
+
Very well done.  I had no trouble '''git'''ting your files and running the code.  It works! Your documentation is very good.  I'm able to follow what you did.
  
Give two sentences telling what works.
+
Some things I want to try are:
 +
# Have the current LED trigger selected on the web page.
 +
# Have the new temperature pushed to the page rather than have the page keep polling.
 +
# Only update the part of the page that needs updating.
  
Give two sentences telling what isn't working.
+
Wiki:    95
 +
Project: 95
 +
'''Total:'''  95
  
End with a two sentence conclusion.
+
== Executive Summary ==
 +
 
 +
Our project aims to implement the emerging node.js technique (the server side javascript) as a lightweight web server on the beagleboard and accomplish a series of remote monitoring and control of hardware pins like gpio/led/i2c on the beagleboard. We also tried to make our web server send automatic email when temperature goes too high. There are a lot of things we can do on the node.js web server, but we try to make it related to our beagleboard hardware interface.
 +
 
 +
The automatic email sender is not working correctly. More work needs to be done to set a proper configuration to the SMTP server 'postfix'.
  
The sentence count is approximate and only to give an idea of the expected length.
+
This turns out to be a very fun application-level project and node.js is really a very friendly, easy-to-use emerging technology that should be promoted to wider industry use.
  
 
== Installation Instructions ==
 
== Installation Instructions ==
 +
1. Here's our [https://github.com/ github] path that includes everything useful to our project: [https://github.com/caoy1/Project https://github.com/caoy1/Project]. 
  
Give step by step instructions on how to install your project on the SPEd2 image.
+
2. Additional packages installed via '''opkg''':
  
* Include your [https://github.com/ github] path as a link like this:  [https://github.com/MarkAYoder/gitLearn https://github.com/MarkAYoder/gitLearn]. 
+
* install node.js
* Include any additional packages installed via '''opkg'''.
+
  
0. install nodejs
+
beagle$ '''opkg update'''
<pre>
+
beagle$ '''opkg install nodejs'''    (about a minute)
opkg update
+
beagle$ '''opkg install nodejs-dev''' (about a minute)
opkg install nodejs
+
opkg install nodejs-dev
+
</pre>
+
  
 +
* install curl and the node package manager
  
1. install node package manager
+
beagle$ '''opkg install curl'''  (about a 1 minute to install)
: curl http://npmjs.org/install.sh | sh
+
beagle$ '''curl http://npmjs.org/install.sh | sh''' (about a minute)
  
 +
* install socket.io module
  
 +
beagle$ '''npm install socket.io'''  (45 seconds, but I get an error)
  
2. install socket.io module
+
* install binary module
: npm install socket.io
+
  
 +
beagle$ '''npm install binary'''    (30 seconds)
  
 +
* install mailer module
  
3. install binary module
+
beagle$ '''npm install mailer'''    (5 seconds)
- npm install binary
+
 
 +
* install SMTP server postfix
 +
Go to [http://postfix.energybeam.com/source/index.html] download the source of postfix, then unzip the .gz file, then run 'make', then run
 +
<pre>
 +
sh postfix-install
 +
</pre>
  
* Include kernel mods.
 
* If there is extra hardware needed, include links to where it can be obtained.
 
 
* Here's the guideline to install Cloud9 easily: [https://github.com/jadonk/cloud9/blob/master/README.md]
 
* Here's the guideline to install Cloud9 easily: [https://github.com/jadonk/cloud9/blob/master/README.md]
 +
 
== User Instructions ==
 
== User Instructions ==
  
Once everything is installed, how do you use the program?  Give details here, so if you have a long user manual, link to it here.
+
Go to our github address above, our final work resides in the "/Project/new" [https://github.com/caoy1/Project/tree/master/new directory]. Pull everything out in that folder, then go into this "new" directory on your host computer, executes the index.js program, then you are good to go:
 +
<pre>
 +
node index.js
 +
</pre>
 +
On the console, "Server has started" will be printed out.
 +
Fire up any of your browser, and visit the beagleboard's IP address:3001(port number). Currently the address is:
 +
<pre>
 +
http://137.112.101.67:3001
 +
</pre>
 +
The webpage will show up and the temperature is real-time updated. You can also select the LED toggle mode on the page, and when you hit "select", the LED light on the board will toggle in the way you specify on the page.
  
 
== Highlights ==
 
== Highlights ==
  
Here is where you brag about what your project can do.
+
Remote monitoring parameters like room temperature, if sensors are equipped on board, and control hardware pins on board, at anywhere, anytime, when you have access to the Internet.
 
+
Consider including a [http://www.youtube.com/ YouTube] demo.
+
  
 
== Theory of Operation ==
 
== Theory of Operation ==
 +
To build a web server, we need, in essence, both client side and server side scripts. Client side scripts mainly just involve ordinary javascripts that can be embedded in HTML file; while at the server-side, we adopt the emerging node.js, which is a really simple server side script that accomplishes equivalent tasks like ASP, PHP will do, but with even simpler implementation details.
 +
== Work Breakdown ==
 +
1. We've already successfully using JavaScript read and write data to the linux gpio file.
 +
<pre>
 +
fs.writeFileSync("/sys/class/leds/beagleboard::usr0/trigger", "heartbeat");
 +
</pre>
  
Give a high level overview of the structure of your software.  Are you using GStreamer?  Show a diagram of the pipeline.  Are you running multiple tasks?  Show what they do and how they interact.
+
<pre>
 +
fs.writeFileSync("/sys/class/gpio/export", "+5");
 +
</pre>
  
== Work Breakdown ==
+
2. Even though we can access to the gpio in the above way, we still need to find someway to access the I2C information. In the I2C exercises, from the C code provided in exercise 5, we can think of the following two options:
  
List the major tasks in your project and who did what.
+
a) To translate tons of the C codes into JavaScript...including rewriting the union structures like i2c_smbus_write_byte() and i2c_smbus_read_byte(), which requires far more knowledge in understanding how to translate the underlying hardware detail into upper-level script languages...
 +
 
 +
b) Try to run the ./myi2c excutable file directly inside node.js script...
 +
 
 +
* It seems that the second way is easier... We've already found a ActiveXObject.run method... We're working on this... turns out this does not work for some reason.
 +
 
 +
* At last we successfully excute the ./myi2c file inside our javascript using the following code, the big idea is create another child process to handle it:
 +
 
 +
<pre>
 +
var exec  = require('child_process').exec,
 +
    child;
 +
child = exec('./myi2c',
 +
  function (error, stdout, stderr) {
 +
    console.log('stdout:', stdout);
 +
    console.log('stderr:', stderr);
 +
    if (error !== null) {
 +
      console.log('exec error:', error);
 +
    }
 +
});
 +
</pre>
 +
 
 +
And the terminal will show the temperature.
 +
 
 +
3. Now we are going to working on how to show this temperature information on the website so that we can visit it from any place!!!
 +
 
 +
* I've successfully done it!!! The pics are already uploaded to the github. Check the following code:
 +
<pre>
 +
var fs = require("fs");
 +
var http = require("http");
 +
 
 +
try {
 +
http.createServer(function(request, response) {
 +
 
 +
response.writeHead(200, {"Content-Type": "text/plain"});
 +
var exec = require('child_process').exec, child;
 +
child = exec('./myi2c',
 +
    function (error, stdout, stderr) {
 +
response.write(stdout);
 +
console.log('stdout:', stdout);
 +
console.log('stderr:', stderr);
 +
response.end();
 +
if(error != null) {
 +
console.log('exec error:', error);
 +
}
 +
}
 +
);
 +
}).listen(8888);
 +
} catch(ex3) {
 +
console.log("sb");
 +
}
 +
</pre>
 +
 
 +
4. The next thing we will do is to refresh the website every other time...
 +
 
 +
* We are now able to load html file inside node.js scripts so that we can put normal HTML stuff inside the web page and load it from server scripts. The following code snippet can accomplish this (borrowed and modified based on Jadon's code):
 +
<pre>
 +
function loadHTMLFile(uri, res, temp) {
 +
var filename = path.join(process.cwd(), uri);
 +
path.exists(
 +
  filename,
 +
  function(exists) {
 +
  if(!exists) {
 +
    res.writeHead(404, {"Content-Type": "text/plain"});
 +
    res.write("404 Not Found\n");
 +
    res.end();
 +
    return;
 +
  }
 +
 
 +
  fs.readFile(
 +
    filename,
 +
    encoding='utf8',
 +
    function(err, file) {
 +
    if(err) {
 +
      res.writeHead(500, {"Content-Type": "text/plain"});
 +
      res.write(err + "\n");
 +
      res.end();
 +
      return;
 +
    }
 +
    res.writeHead(200, {"Content-Type": "text/html"});
 +
    var str = ("" + file).replace("<!--%OUTPUT%-->", temp);
 +
    res.write(str);
 +
    res.end();
 +
    }
 +
  );
 +
  }
 +
);
 +
}
 +
</pre>
 +
And outside the above function, the main routine, we have similar code as listed in 3, but insert the function call:
 +
<pre>
 +
child = exec('./myi2c',
 +
    function (error, stdout, stderr) {
 +
console.log('stdout:', stdout);
 +
console.log('stderr:', stderr);
 +
loadHTMLFile('/index.html', response, stdout);
 +
if(error != null) {
 +
console.log('exec error:', error);
 +
}
 +
}
 +
);
 +
</pre>
 +
* Now we can update the temperature information on the website, both triggered by a button on the page, and by just automatically refreshing the temperature value whenever the temperature changes. Here is the simple client script for accomplishing this:
 +
<pre>
 +
<html>
 +
<head>
 +
<script language="javascript">
 +
setInterval("document.forms[0].submit()",5000);
 +
</script>
 +
</head>
 +
<body>
 +
<center>
 +
<h1>Welcome to the world of Node.js</h1>
 +
 
 +
<p>Here is the demo of the weather station.</p>
 +
<h2>Current temperature:</h2>
 +
<pre>
 +
<span id="temperature output"><!--%OUTPUT%--></span>
 +
<form action = "/test.js" method="post">
 +
<input type="submit" value="get temperature">
 +
</form>
 +
</center>
 +
</body>
 +
</html>
 +
</pre>
 +
 
 +
5. Now we are trying to do something fancy! We want to send the user an automatic warning email when the temperature is too high. Here's the Javascript code using node.js mailer module. This code use SMTP server to send email to the destination.
 +
 
 +
<pre>
 +
var email = require('mailer');
 +
 
 +
email.SMTP = {
 +
    host: 'smtp.gmail.com',
 +
    port: 587,
 +
    ssl: false,
 +
    use_authentication: false,
 +
}
 +
 
 +
email.send({
 +
    to : "caoy1@rose-hulman.edu",
 +
    from : "obama@whitehouse.gov",
 +
    subject : "I love beagleboard!",
 +
    body: "Hello beagle world.",
 +
},
 +
 
 +
function(err, result) {
 +
    if(err) {
 +
console.log(err);
 +
    }
 +
});
 +
</pre>
 +
 
 +
* However, the configuration on our beagle has some problem. The server has no response to mailer's request. We've tried telnet localhost 587, and connection can be connected. This means the 587 port is already opened. Then I tried the same code under my ubuntu system, it turns out everything works fine, the caoy1@rose-hulman.edu can receive the e-mail, except some client-side warning error when running the mail.js file. I think the reason it works on ubuntu is because when installing postfix, there's an automatic configuration step, which doesn't exist on our beagle installation. So there must be some problem with the postfix configuration of beagleboard. I've tried to copy all the configuration files under /etc/postfix to our beagleboard, but there's just the same problem. If I have more time, or someone else who's interested in continuing this project, I suggest you have some research on the configuration thing. Everytime you make some config changes, just run
 +
<pre>
 +
postfix reload
 +
</pre>
 +
to apply the change.
 +
 
 +
5. Our next goal is to toggle LED via web page, whose control flow structure is essentially similar to the i2c communications, plus, we have already learned the way to write to the led pin via node.js script(see note1 above).
 +
* Before actually doing that, let's first organize our code in a more hierarchical and reasonable way.Thanks to Mr. Manuel Kiessling's excellent introductory tutorial "[http://www.nodebeginner.org The Node Beginner Book]", which gives a pretty decent explanation on how a formal web server architecture should be constructed using node.js. Basically, we should maintain an index.js that starts all of our code, have a server.js to actually create the server and listen on the port for any posts from the client side, and pass the request and response to the router.js to handle different requests. In requestHandlers.js, each function then does the real work based on the request information and execute corresponding server responses.
 +
* The following is what's in index.js:
 +
<pre>
 +
var server = require("./server");
 +
var router = require("./router");
 +
var requestHandlers = require("./requestHandlers");
 +
 
 +
var handle = {}
 +
handle["/"] = requestHandlers.i2c;
 +
handle["/led"] = requestHandlers.led;
 +
handle["/i2c"] = requestHandlers.i2c;
 +
 
 +
server.start(router.route, handle);
 +
</pre>
 +
* And we abstract the server.js to be a general form so that it is independent of what will actually be done at the server, such details are left for requestHandlers.js to accomplish:
 +
<pre>
 +
var http = require("http");
 +
var url = require("url");
 +
var sys = require('sys');
 +
var fs = require('fs');
 +
var path = require('path');
 +
var events = require('events');
 +
 
 +
function start(route, handle) {
 +
function onRequest(request, response){
 +
      var pathname = url.parse(request.url).pathname;
 +
      route(handle, pathname, response, request);
 +
  }
 +
 
 +
  http.createServer(onRequest).listen(3001);
 +
  console.log("Server has started.");
 +
}
 +
 
 +
exports.start = start;
 +
</pre>
 +
* router.js gets the request's pathname and call different functions according to different request url:
 +
<pre>
 +
function route(handle, pathname, response, request) {
 +
  console.log("About to route a request for " + pathname);
 +
  if (typeof handle[pathname] === 'function') {
 +
    handle[pathname](response, request);
 +
  } else {
 +
    console.log("No request handler found for " + pathname);
 +
    response.writeHead(404, {"Content-Type": "text/plain"});
 +
    response.write("404 Not found");
 +
    response.end();
 +
  }
 +
}
 +
 
 +
exports.route = route;
 +
</pre>
 +
* In requestHandlers.js, we need two functions, one for handle i2c request, another for led toggling request:
 +
<pre>
 +
function i2c(response, request) {
 +
  var uri = url.parse(request.url).pathname;
 +
  ...//same as described above, including calling loadHTMLFile function that's also in requestHandlers.js
 +
}
 +
 
 +
function led(response, request) {
 +
  ...//details in below
 +
}
 +
</pre>
 +
 
 +
6. Now it's time for us to think about how to toggle LED via web page. Turns out it's very similar to how i2c works. However, this time we need to not only parse the uri of the request, but also get the posted data from the request, parse them and write them into the appropriate file system. The event-driven node.js utilizes "addListener" function to achieve the task, with parameter "data" to receive the data and "end" to handle and process them. The data received(in type of string) should be converted to object class by the "querystring" module.
 +
 
 +
On the client side, we use exclusive radio button to allow user to choose the toggle mode of LED:
 +
<pre>
 +
<form action = "/led" method="post" size="30">
 +
            <input type="radio" name="mode" value="nand-disk" >nand-disk
 +
            <input type="radio" name="mode" value="mmc0" />mmc0
 +
            <input type="radio" name="mode" value="heartbeat" >heartbeat
 +
            <input type="radio" name="mode" value="none" >none
 +
            <input type="submit" value="Select"  onclick="xmlhttpPost()">
 +
</form>
 +
</pre>
 +
The corresponding javascript just submits this form to /led, which then be handled by router to execute led() function:
 +
<pre>
 +
function xmlhttpPost() {
 +
documents.forms[1].submit();
 +
}
 +
</pre>
 +
The following is the server side function code in requestHandlers.js:
 +
<pre>
 +
var qs = require('querystring');
 +
var url = require('url');
 +
var sys = require('sys');
 +
var fs = require('fs');
 +
var path = require('path');
 +
function led(response, request) {
 +
  var postData = "";
 +
  var pathname = url.parse(request.url).pathname;
 +
  console.log("Request for " + pathname + " received.");
 +
 
 +
    request.setEncoding("utf8");
 +
 
 +
    request.addListener("data", function(postDataChunk) {
 +
      postData += postDataChunk;
 +
      console.log("Received POST data chunk '"+
 +
      postDataChunk + "'.");
 +
    });
 +
 
 +
    request.addListener("end", function() {
 +
      var ledtype = qs.parse(postData);
 +
          console.log(ledtype.mode);
 +
          fs.writeFileSync("/sys/class/leds/beagleboard::usr0/trigger", ledtype.mode);
 +
    });
 +
}
 +
</pre>
 +
Another thing to notice is, in order for this requestHandler for led.js not to jump to a brand new web page but stay on the current one, the normal "request.end()" code, which indicates the completion of an intact .js page, should be left out.
  
Also list here what doesn't work yet and when you think it will be finished and who is finishing it.
+
7. Eventually we did a little bit beautification on the html page --- somehow CSS does not work as intended, we simply change some font and color of the output to make it a bit neater.
  
 
== Conclusions ==
 
== Conclusions ==
  
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.
+
This project dabbles in the possibility of setting a web-server written in node.js on the embedded environment and accomplishes the proposed idea of interacting with underlying hardware pins and system via upper-level user interface. node.js is very simple and easy to implement, its non-blocking, event-driven and single-threaded mechanism is exactly suitable for the embedded system and our project gives a good instance of its application. Our demo system can be scaled up with far more features in remote monitoring and smart control industry. If, for example, the nodemailer can work well on the Angstrom (apparently it's working on Ubuntu, but somehow does not seem to be compatible with Angstrom), or other embedded Linux distribution, it can become as powerful as intelligent agent in monitoring various parameters, like temperature, moisture, so on so forth.

Latest revision as of 13:57, 16 July 2012


Team members: Yuming Cao, Ziyi Zhang

Grading

Very well done. I had no trouble gitting your files and running the code. It works! Your documentation is very good. I'm able to follow what you did.

Some things I want to try are:

  1. Have the current LED trigger selected on the web page.
  2. Have the new temperature pushed to the page rather than have the page keep polling.
  3. Only update the part of the page that needs updating.
Wiki:    95
Project: 95
Total:   95

Executive Summary

Our project aims to implement the emerging node.js technique (the server side javascript) as a lightweight web server on the beagleboard and accomplish a series of remote monitoring and control of hardware pins like gpio/led/i2c on the beagleboard. We also tried to make our web server send automatic email when temperature goes too high. There are a lot of things we can do on the node.js web server, but we try to make it related to our beagleboard hardware interface.

The automatic email sender is not working correctly. More work needs to be done to set a proper configuration to the SMTP server 'postfix'.

This turns out to be a very fun application-level project and node.js is really a very friendly, easy-to-use emerging technology that should be promoted to wider industry use.

Installation Instructions

1. Here's our github path that includes everything useful to our project: https://github.com/caoy1/Project.

2. Additional packages installed via opkg:

  • install node.js
beagle$ opkg update
beagle$ opkg install nodejs     (about a minute)
beagle$ opkg install nodejs-dev (about a minute)
  • install curl and the node package manager
beagle$ opkg install curl  (about a 1 minute to install)
beagle$ curl http://npmjs.org/install.sh | sh (about a minute)
  • install socket.io module
beagle$ npm install socket.io  (45 seconds, but I get an error)
  • install binary module
beagle$ npm install binary     (30 seconds)
  • install mailer module
beagle$ npm install mailer     (5 seconds)
  • install SMTP server postfix

Go to [1] download the source of postfix, then unzip the .gz file, then run 'make', then run

sh postfix-install
  • Here's the guideline to install Cloud9 easily: [2]

User Instructions

Go to our github address above, our final work resides in the "/Project/new" directory. Pull everything out in that folder, then go into this "new" directory on your host computer, executes the index.js program, then you are good to go:

node index.js

On the console, "Server has started" will be printed out. Fire up any of your browser, and visit the beagleboard's IP address:3001(port number). Currently the address is:

http://137.112.101.67:3001

The webpage will show up and the temperature is real-time updated. You can also select the LED toggle mode on the page, and when you hit "select", the LED light on the board will toggle in the way you specify on the page.

Highlights

Remote monitoring parameters like room temperature, if sensors are equipped on board, and control hardware pins on board, at anywhere, anytime, when you have access to the Internet.

Theory of Operation

To build a web server, we need, in essence, both client side and server side scripts. Client side scripts mainly just involve ordinary javascripts that can be embedded in HTML file; while at the server-side, we adopt the emerging node.js, which is a really simple server side script that accomplishes equivalent tasks like ASP, PHP will do, but with even simpler implementation details.

Work Breakdown

1. We've already successfully using JavaScript read and write data to the linux gpio file.

fs.writeFileSync("/sys/class/leds/beagleboard::usr0/trigger", "heartbeat");
fs.writeFileSync("/sys/class/gpio/export", "+5");

2. Even though we can access to the gpio in the above way, we still need to find someway to access the I2C information. In the I2C exercises, from the C code provided in exercise 5, we can think of the following two options:

a) To translate tons of the C codes into JavaScript...including rewriting the union structures like i2c_smbus_write_byte() and i2c_smbus_read_byte(), which requires far more knowledge in understanding how to translate the underlying hardware detail into upper-level script languages...

b) Try to run the ./myi2c excutable file directly inside node.js script...

  • It seems that the second way is easier... We've already found a ActiveXObject.run method... We're working on this... turns out this does not work for some reason.
  • At last we successfully excute the ./myi2c file inside our javascript using the following code, the big idea is create another child process to handle it:
var exec  = require('child_process').exec,
    child;
child = exec('./myi2c',
  function (error, stdout, stderr) {
    console.log('stdout:', stdout);
    console.log('stderr:', stderr);
    if (error !== null) {
      console.log('exec error:', error);
    }
});

And the terminal will show the temperature.

3. Now we are going to working on how to show this temperature information on the website so that we can visit it from any place!!!

  • I've successfully done it!!! The pics are already uploaded to the github. Check the following code:
var fs = require("fs");
var http = require("http");

try {
	http.createServer(function(request, response) {

		response.writeHead(200, {"Content-Type": "text/plain"});
		var exec = require('child_process').exec, child;
		child = exec('./myi2c',
	    		function (error, stdout, stderr) {
				response.write(stdout);
				console.log('stdout:', stdout);
				console.log('stderr:', stderr);
				response.end();
				if(error != null) {
					console.log('exec error:', error);
				}
			}
		);
	}).listen(8888);
} catch(ex3) {
	console.log("sb");
}

4. The next thing we will do is to refresh the website every other time...

  • We are now able to load html file inside node.js scripts so that we can put normal HTML stuff inside the web page and load it from server scripts. The following code snippet can accomplish this (borrowed and modified based on Jadon's code):
function loadHTMLFile(uri, res, temp) {
 var filename = path.join(process.cwd(), uri);
 path.exists(
  filename,
  function(exists) {
   if(!exists) {
    res.writeHead(404, {"Content-Type": "text/plain"});
    res.write("404 Not Found\n");
    res.end();
    return;
   }

   fs.readFile(
    filename,
    encoding='utf8',
    function(err, file) {
     if(err) {
      res.writeHead(500, {"Content-Type": "text/plain"});
      res.write(err + "\n");
      res.end();
      return;
     }
     res.writeHead(200, {"Content-Type": "text/html"});
     var str = ("" + file).replace("<!--%OUTPUT%-->", temp);
     res.write(str);
     res.end();
    }
   );
  }
 );
}

And outside the above function, the main routine, we have similar code as listed in 3, but insert the function call:

child = exec('./myi2c',
	    		function (error, stdout, stderr) {
				console.log('stdout:', stdout);
				console.log('stderr:', stderr);
				loadHTMLFile('/index.html', response, stdout);
				if(error != null) {
					console.log('exec error:', error);
				}
			}
		);
  • Now we can update the temperature information on the website, both triggered by a button on the page, and by just automatically refreshing the temperature value whenever the temperature changes. Here is the simple client script for accomplishing this:
<html>
<head>
<script language="javascript">
setInterval("document.forms[0].submit()",5000);
</script>
</head>
<body>
<center>
<h1>Welcome to the world of Node.js</h1>

<p>Here is the demo of the weather station.</p>
<h2>Current temperature:</h2>
<pre>
<span id="temperature output"><!--%OUTPUT%--></span>
<form action = "/test.js" method="post">
<input type="submit" value="get temperature">
</form>
</center>
</body>
</html>

5. Now we are trying to do something fancy! We want to send the user an automatic warning email when the temperature is too high. Here's the Javascript code using node.js mailer module. This code use SMTP server to send email to the destination.

var email = require('mailer');

email.SMTP = {
    host: 'smtp.gmail.com',
    port: 587,
    ssl: false,
    use_authentication: false,
}

email.send({
    to : "caoy1@rose-hulman.edu",
    from : "obama@whitehouse.gov",
    subject : "I love beagleboard!",
    body: "Hello beagle world.",
}, 

function(err, result) {
    if(err) {
	console.log(err);
    }
});
  • However, the configuration on our beagle has some problem. The server has no response to mailer's request. We've tried telnet localhost 587, and connection can be connected. This means the 587 port is already opened. Then I tried the same code under my ubuntu system, it turns out everything works fine, the caoy1@rose-hulman.edu can receive the e-mail, except some client-side warning error when running the mail.js file. I think the reason it works on ubuntu is because when installing postfix, there's an automatic configuration step, which doesn't exist on our beagle installation. So there must be some problem with the postfix configuration of beagleboard. I've tried to copy all the configuration files under /etc/postfix to our beagleboard, but there's just the same problem. If I have more time, or someone else who's interested in continuing this project, I suggest you have some research on the configuration thing. Everytime you make some config changes, just run
postfix reload

to apply the change.

5. Our next goal is to toggle LED via web page, whose control flow structure is essentially similar to the i2c communications, plus, we have already learned the way to write to the led pin via node.js script(see note1 above).

  • Before actually doing that, let's first organize our code in a more hierarchical and reasonable way.Thanks to Mr. Manuel Kiessling's excellent introductory tutorial "The Node Beginner Book", which gives a pretty decent explanation on how a formal web server architecture should be constructed using node.js. Basically, we should maintain an index.js that starts all of our code, have a server.js to actually create the server and listen on the port for any posts from the client side, and pass the request and response to the router.js to handle different requests. In requestHandlers.js, each function then does the real work based on the request information and execute corresponding server responses.
  • The following is what's in index.js:
var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {}
handle["/"] = requestHandlers.i2c;
handle["/led"] = requestHandlers.led;
handle["/i2c"] = requestHandlers.i2c;

server.start(router.route, handle);
  • And we abstract the server.js to be a general form so that it is independent of what will actually be done at the server, such details are left for requestHandlers.js to accomplish:
var http = require("http");
var url = require("url");
var sys = require('sys'); 
var fs = require('fs');
var path = require('path');
var events = require('events');

function start(route, handle) {
	function onRequest(request, response){
      var pathname = url.parse(request.url).pathname;
      route(handle, pathname, response, request);
  }

  http.createServer(onRequest).listen(3001);
  console.log("Server has started.");
}

exports.start = start;
  • router.js gets the request's pathname and call different functions according to different request url:
function route(handle, pathname, response, request) {
  console.log("About to route a request for " + pathname);
  if (typeof handle[pathname] === 'function') {
    handle[pathname](response, request);
  } else {
    console.log("No request handler found for " + pathname);
    response.writeHead(404, {"Content-Type": "text/plain"});
    response.write("404 Not found");
    response.end();
  }
}

exports.route = route;
  • In requestHandlers.js, we need two functions, one for handle i2c request, another for led toggling request:
function i2c(response, request) {
  var uri = url.parse(request.url).pathname;
  ...//same as described above, including calling loadHTMLFile function that's also in requestHandlers.js
 }

function led(response, request) {
  ...//details in below
}

6. Now it's time for us to think about how to toggle LED via web page. Turns out it's very similar to how i2c works. However, this time we need to not only parse the uri of the request, but also get the posted data from the request, parse them and write them into the appropriate file system. The event-driven node.js utilizes "addListener" function to achieve the task, with parameter "data" to receive the data and "end" to handle and process them. The data received(in type of string) should be converted to object class by the "querystring" module.

On the client side, we use exclusive radio button to allow user to choose the toggle mode of LED:

<form action = "/led" method="post" size="30">
            <input type="radio" name="mode" value="nand-disk" >nand-disk
            <input type="radio" name="mode" value="mmc0" />mmc0
            <input type="radio" name="mode" value="heartbeat" >heartbeat
            <input type="radio" name="mode" value="none" >none
            <input type="submit" value="Select"  onclick="xmlhttpPost()">
</form>

The corresponding javascript just submits this form to /led, which then be handled by router to execute led() function:

function xmlhttpPost() {
documents.forms[1].submit();
}

The following is the server side function code in requestHandlers.js:

var qs = require('querystring');
var url = require('url');
var sys = require('sys');
var fs = require('fs');
var path = require('path');
function led(response, request) {
  var postData = "";
   var pathname = url.parse(request.url).pathname;
   console.log("Request for " + pathname + " received.");

    request.setEncoding("utf8");

    request.addListener("data", function(postDataChunk) {
      postData += postDataChunk;
      console.log("Received POST data chunk '"+
      postDataChunk + "'.");
    });

    request.addListener("end", function() {
      var ledtype = qs.parse(postData);
          console.log(ledtype.mode);
          fs.writeFileSync("/sys/class/leds/beagleboard::usr0/trigger", ledtype.mode);
    });
}

Another thing to notice is, in order for this requestHandler for led.js not to jump to a brand new web page but stay on the current one, the normal "request.end()" code, which indicates the completion of an intact .js page, should be left out.

7. Eventually we did a little bit beautification on the html page --- somehow CSS does not work as intended, we simply change some font and color of the output to make it a bit neater.

Conclusions

This project dabbles in the possibility of setting a web-server written in node.js on the embedded environment and accomplishes the proposed idea of interacting with underlying hardware pins and system via upper-level user interface. node.js is very simple and easy to implement, its non-blocking, event-driven and single-threaded mechanism is exactly suitable for the embedded system and our project gives a good instance of its application. Our demo system can be scaled up with far more features in remote monitoring and smart control industry. If, for example, the nodemailer can work well on the Angstrom (apparently it's working on Ubuntu, but somehow does not seem to be compatible with Angstrom), or other embedded Linux distribution, it can become as powerful as intelligent agent in monitoring various parameters, like temperature, moisture, so on so forth.