-
Notifications
You must be signed in to change notification settings - Fork 6
Hardware Abstraction with firmata
Get ready for using Firmata protocol communicate to the MPU and MCU in MediaTek Linkit 7688 Duo with Node.js.
Set up the MCU by launching Arduino IDE 1.6.5 on your computer.
Open the Arduino IDE and on the File menu click Preferences. In Additional Boards Manager URL add:
http://download.labs.mediatek.com/package_mtk_linkit_smart_7688_index.json
In the Tools menu point to Board then click Boards Manager, a LinkIt Smart 7688 Duo item should appear in the boards list. Select the version and click Install. After the installation, your can find Linkit Smart 7688 Duo in Board and select it.
Copy the sketch code to the Arduino IDE. The sketch used in this example is from: https://gist.github.com/edgarsilva/e73c15a019396d6aaef2
In the Tools menu point to Port then you can find Linkit Smart 7688 Duo in Serial Ports and Network Ports. Please select the port in Network Ports. Now, you can clink upload!
While you can install firmata
using NPM on LinkIt 7688 Duo, the process is a bit long. So you’ll need to install firmata
on your computer and then use SCP to transfer the compressed file to the Linkit 7688 Duo.
mkdir testfirmata && cd testfirmata
npm install [email protected]
We need to remove it because serialport is already available on LinkIt Smart 7688 system image.
rm -rf node_modules/firmata/node_modules/serialport/
tar -cvf firmata.tar node_modules/firmata
scp firmata.tar [email protected]:/root # replace the host name or ip with yours Linkit 7688 Duo
ssh [email protected] # replace the host name or ip with yours Linkit 7688 Duo
mkdir app
tar -xvf firmata.tar -C app/
Let's abstract a led and a push button into a smart object with firmata
.
cd app
touch smartobj.js
npm install smartobject
- [1] SmartObject Constructor
- [2] Light Control Object Instance code template
- [3] Create an Object Instance
- [4] Use
firmata
to communicate with Linkit 7688 Duo
var Board = require('firmata'),
SmartObject = require('smartobject');
var board = new Board("/dev/ttyS0");
// hardware initialization, see [1]
var so = new SmartObject({
board: board,
ledPin: 2 // Make sure your LED is connected to pin D2 on Linkit 7688 Duo
});
// Create a Light Control Object Instance in so, see [2] and [3]
// Use firmata to communicate with Linkit 7688 Duo, see [4]
so.init('lightCtrl', 0, {
onOff: {
read: function (cb) {
var board = this.parent.hal.board,
ledPin = this.parent.hal.ledPin,
ledState = board.pins[ledPin].value;
process.nextTick(function () {
cb(null, ledState);
});
},
write: function (val, cb) {
var board = this.parent.hal.board,
ledPin = this.parent.hal.ledPin,
ledState = val ? board.HIGH : board.LOW;
board.digitalWrite(ledPin, ledState);
process.nextTick(function () {
cb(null, board.pins[ledPin].value);
});
}
}
});
// Use so.read() and so.write() methods to blink the led for few times
// We will use a led blink driver to replace this snippet later
var blinkLed = function () {
var times = 20,
blinker = setInterval(function () {
times -= 1;
if (times === 0)
clearInterval(blinker);
so.read('lightCtrl', 0, 'onOff', function (err, data) {
if (err)
return console.log(err);
so.write('lightCtrl', 0, 'onOff', !data, function (err, val) {
if (err)
return console.log(err);
});
});
}, 200);
}
board.on("ready", function() {
console.log('>> Arduino is ready to communicate');
// Set a mode for a pin
board.pinMode(so.hal.ledPin, board.MODES.OUTPUT);
// Blink our led
blinkLed();
});
node smartobj.js
var Board = require('firmata'),
SmartObject = require('smartobject');
var board = new Board("/dev/ttyS0");
// Add our led blink driver to hal
var so = new SmartObject({
board: board,
ledPin: 2,
blinkLed: null // we'll implement the driver in the setup function
}, function () {
this.hal.blinkLed = function (times) {
times = 2 * times;
var blinker = setInterval(function () {
times -= 1;
if (times === 0)
clearInterval(blinker);
so.read('lightCtrl', 0, 'onOff', function (err, data) {
if (err)
return console.log(err);
so.write('lightCtrl', 0, 'onOff', !data, function (err, val) {
if (err)
return console.log(err);
});
});
}, 200);
}
});
so.init('lightCtrl', 0, {
// ... code remains the same
});
board.on("ready", function() {
console.log('>> Arduino is ready to communicate');
// Set a mode for a pin
board.pinMode(so.hal.ledPin, board.MODES.OUTPUT);
// Blink our led by the driver
so.hal.blinkLed(10);
});
node smartobj.js
var Board = require('firmata'),
SmartObject = require('smartobject');
var board = new Board("/dev/ttyS0");
// Add our led blink driver to hal
var so = new SmartObject({
board: board,
ledPin: 2,
buttonPin: 3, // Make sure your push button is connected to pin D3 on Linkit 7688 Duo
blinkLed: null,
pollButton: null // we'll implement the driver in the setup function
}, function () {
this.hal.blinkLed = function (times) {
// ... code remains the same
}
this.hal.pollButton = function () {
setInterval(function () {
so.read('pushButton', 0, 'dInState', function (err, data) {
var buttonState = data;
if (err)
return console.log(err);
// Led will light up acoording to the button state
so.write('lightCtrl', 0, 'onOff', buttonState ? 1 : 0, function () {});
});
}, 100);
};
});
so.init('lightCtrl', 0, {
// ... code remains the same
});
// see [1]
so.init('pushButton', 0, {
dInState: {
read: function (cb) {
var board = this.parent.hal.board,
buttonPin = this.parent.hal.buttonPin,
buttonState = board.pins[buttonPin].value;
process.nextTick(function () {
cb(null, buttonState);
});
}
}
});
board.on("ready", function() {
console.log('>> Arduino is ready to communicate');
// Set a mode for a pin
board.pinMode(so.hal.ledPin, board.MODES.OUTPUT);
board.pinMode(so.hal.buttonPin, board.MODES.INPUT);
// Register to get the digital value
board.digitalRead(so.hal.buttonPin, function(value) {});
// Poll the button
so.hal.pollButton();
});
node smartobj.js
In this section, we will use lightweight M2M (LWM2M) to build the IoT network. At server side, we'll use coap-shepherd
to create the LWM2M server, and use coap-node
on our Linkit 7688 Duo machine to create the LWM2M client. You can build a MQTT network with mqtt-shepherd
and mqtt-node
as well, they follow the similar pattern with smartobject module.
Abstracting the hardware is kind of boring, but once we've packaged it up, we can ship it and resuse it anytime (npm publish it, and use it everywhere)! And you know what? To access the hardware is pretty simple with the smartobject read/write interfaces. This is the best practice of the node.js small surface area philosophy.
In step 7@section B, we've build a nice smart object in smartobj.js, let's add the following line to the end of the file:
var Board = require('firmata'),
SmartObject = require('smartobject');
// ... code remains the same
// take off this line for our demo can go nicely
// (since we'll let the led blink when machine booted up, but the led is also controlled by the button. That conflicts.)
// so.hal.pollButton();
// export our so
module.exports = so;
npm install coap-node
var so = require('./smartobj.js'),
CoapNode = require('coap-node');
// see [1]
var coapNode = new CoapNode('my_cnode', so);
coapNode.on('registered', function () {
console.log('>> CoAP node is registered to a server');
});
// Register to the server after Arduino ready to communicate
so.hal.board.on('ready', function () {
console.log('>> Blink the led for few times');
so.hal.blinkLed(10);
// Connect to the server 5 seconds later
setTimeout(function () {
console.log('>> Register to a server...');
// replace the ip with yours server-side machine
coapNode.register('192.168.1.115', 5683, function (err, msg) {
console.log(msg);
});
// Poll the button
so.hal.pollButton();
}, 5000);
});
mkdir cserver && cd cserver
touch server.js
npm install coap-shepherd
var cserver = require('coap-shepherd');
// see [1]
cserver.on('ready', function () {
console.log('>> coap-shepherd server start!');
console.log('>> Permit devices joining for 180 seconds');
cserver.permitJoin(180);
});
cserver.start(function (err) {
if (err)
console.log(err);
});
// see [2]
cserver.on('ind', function (msg) {
switch (msg.type) {
case 'devIncoming':
// When our 'my_cnode' comes in, we read the led and button current states back
var cnode = msg.cnode;
if (cnode.clientName === 'my_cnode') {
cnode.readReq('lightCtrl/0/onOff', function (err, rsp) {
if (!err)
console.log('>> Current led state at machine: ' + rsp.data); // rsp = { status, data }
});
cnode.readReq('pushButton/0/dInState', function (err, rsp) {
if (!err)
console.log('>> Current button state at machine: ' + rsp.data); // rsp = { status, data }
});
}
break;
case 'devStatus':
// When 'my_cnode' is online, we tell the machine to report the led change to server
var cnode = msg.cnode;
if (cnode.clientName === 'my_cnode' && msg.data === 'online') {
// setting for notification of led state reporting
cnode.observeReq('lightCtrl/0/onOff', function (err, rsp) {
console.log('>> Led observation starts: ');
console.log(rsp);
});
}
break;
case 'devNotify':
var data = msg.data;
if (data && data.path === '/lightCtrl/0/onOff') {
console.log('>> Led state at machine changed: ');
console.log(' ' + data.value);
}
break;
default:
// Not deal with other msg.type in this example
break;
}
});
node server
node client