This document discusses programming IoT gateways using JavaScript with the macchina.io toolkit. Macchina.io is an open source modular toolkit for building embedded IoT applications that connect sensors, devices, and cloud services. It uses C++ for performance but includes a JavaScript interface. The document provides an overview of macchina.io and its components, demonstrates how to use JavaScript to access sensors and cloud services, and provides an example of logging sensor data to a database and sending SMS alerts based on sensor readings.
Programming IoT Gateways in JavaScript with macchina.io
1. Programming IoT Gateways
in JavaScript with macchina.io
Günter Obiltschnig
Applied Informatics Software Engineering GmbH
guenter@appinf.com
@obiltschnig, @macchina_io
2. About Me
hard-core C++ developer (20+ years), who also likes JavaScript
“full stack++”, embedded, hardware to web frontend + cloud
POCO C++ Libraries (2004)
Applied Informatics GmbH (2006)
my-devices.net (2010)
AIS Radar for iOS (2011)
macchina.io (2013)
3. m .ioacchina
A modular open source toolkit for building embedded IoT
applications that connect sensors, devices and cloud services.
6. > open source (Apache 2.0 License)
> built in C++ for best performance and efficiency
(JavaScript for parts of web interface)
> modular and extensible
> mature, proven codebase:
POCO C++ Libraries, Google V8, Eclipse Paho, SQLite
AngularJS, jQuery, OpenLayers, Ace (text editor),
+ Applied Informatics OSP and Remoting frameworks
> C++-to-JavaScript bridge
> Raspberry Pi, Beaglebone, Edison, RED, MangOH, etc.
> prototype on Linux or OS X host, easily deploy to device
> web interface with JavaScript editor
7. Sensors & Devices Protocols Cloud Services
Temperature, Ambient Light,
Humidity, Air Pressure, etc.
HTTP AirVantage
I/O, Trigger, Rotary Encoder MQTT Bluemix
Accelerometer CoAP* Twitter
GNSS/GPS WebEvent Twilio (SMS)
Barcode Reader, RFID* WebTunnel my-devices.net
XBee (ZigBee, IEEE 802.15.4) XBee API any with HTTP/REST APIs
Serial Port Modbus*, CANopen*
* planned
8. Pro Users and Device Manufacturers
> add device specific APIs
> make devices programmable in JavaScript for partners or end users
> device specific app store (sell additional software features)
> additional frameworks (UPnP, Remoting SOAP and JSON-RPC)
> customizable web user interface
> improved user authentication and authorization
> signed bundles
> pro support
12. POCO C++ Libraries
> Started 2004
> ~300.000 LOC
> 1000+ classes
> on GitHub since 2012
1000+ stars
400+ forks
30-50 clones/day
> ~100 contributors
> Boost License
> http://pocoproject.org
POSIX, WIN32, other (RT)OS API
Foundation
C++ and C Standard LibrariesApplication
Zip
Net
Crypto
Data
SQLite
ODBC
MySQL
NetSSL
Util
Tools, Utilities and
additional Libraries
XML JSON
13. V8
> Google’s JavaScript Engine
> Used by Chrome/Chromium and node.js
> C++ library, (reasonably) easy to integrate and to extend
> Compiles JavaScript to native code (x86, ARM, MIPS)
> Great performance
> BSD License
14. Remoting
> Similar to .NET Remoting or Java RMI, but for C++
> Code generator parses annotated C++ header files and generates code
(serialization/deserialization, method dispatching, helpers)
> Supports different transports (binary TCP, SOAP, JSON-RPC)
> Used for automatic C++-to-JavaScript bridging
> Will also be used to implement sandbox mechanism
15. Open Service Platform (OSP)
> Inspired by OSGi, but for C++ (also JavaScript, Python, etc.)
> Dynamic module system based on bundles
(Zip files with metadata,
shared libs, other files)
> Dependency and
lifecycle management
> Services and service registry
> Web Server
POCO Core Libraries
(Foundation,XML,Util,Net)
Operating
System
API
Std.C/C++
Libraries
Service
Registry
Portable Runtime
Environment
Life
C
ycle
M
anagem
ent
Bundle
M
anagem
ent
Standard
Services
Bundles
install,resolve,start,stop
and uninstall bundles
provide services to other
bundles and find services
provided by other bundles
manage bundle versions
and dependencies
web server,
web- and console-
based management,
user authentication
and authorization,
preferences,etc.
application-specific
functionality and services
16. Combining POCO C++ Libraries and V8
> JavaScript is single-threaded and garbage-collected
> POCO is multithreaded (specifically web server)
> Make C++ object available to JavaScript
easy for static objects, just provide Wrapper
> Allow JavaScript code to create C++ objects
easy if you don’t care about memory/resource leaks
> Register a callback function called by GC when object is deleted
allows you to properly delete underlying c++ object
> However, V8 does not do callbacks when script ends
wrapped C++ objects won’t be deleted, leaks resulting
> Need to track every C++ object a script creates and clean up afterwards :-(
20. Service
<<generatedFrom>>
IService
ServiceProxy
Service
RemoteObject
The interface class has
all @remote methods
from the service class.
Service
Skeleton
<<invokes>>
<<generated>>
<<generated>><<generated>>
<<generated>>
Service
ServerHelper
<<generated>>
Registers
Skeleton, RemoteObject
and EventDispatcher (if
needed) with the Object
Request Broker.
Service
ProxyFactory
<<creates>>
<<generated>>
Service
ClientHelper
<<generated>>
Registers ProxyFactory
with the Object Request
Broker.
<<registers>><<registers>>
<<registers>>
Service
EventSubscriber
<<generated>>
Service
EventDispatcher
<<generated>>
<<registers>>
21. var tempSensor = ...;
tempSensor.on(‘valueChanged', function(ev) {
var temp = ev.data;
// ...
});
if (tempSensor.ready())
{
var temp = tempSensor.value();
// ...
}
28. // history.jss
var db = require(‘../database.js');
var validItems = ['temperature', 'humidity', 'illuminance'];
var data = [];
db.session.pageSize = form.maxItems ? parseInt(form.maxItems) : 20;
var item = form.item;
if (validItems.indexOf(item) > -1)
{
var recordSet = db.session.execute(
'SELECT timestamp, ' + item + ' FROM datalog ORDER BY timestamp DESC');
29. // history.jss (continued)
for (var row = 0; row < recordSet.rowCount; row++)
{
var time = recordSet.getValue('timestamp');
var value = recordSet.getValue(item);
var date = LocalDateTime('1970-01-01');
date.addSeconds(time);
data[recordSet.rowCount - row - 1] =
{
timestamp: date.format('%H:%M:%S'),
value: value
};
recordSet.moveNext();
}
recordSet.close();
}
response.contentType = 'application/json';
response.write(JSON.stringify(data));
response.send();
30. // MQTT to AirVantage
var sensors = require('sensors.js');
var mqttClientRefs = serviceRegistry.find(
'io.macchina.mqtt.serverURI == "tcp://na.airvantage.net:1883"');
if (mqttClientRefs.length > 0)
{
logger.information("MQTT Client found!");
var mqttClient = mqttClientRefs[0].instance();
setInterval(function() {
var epoch = "" + 1000*DateTime().epoch; // seconds to milliseconds
var payload = {};
payload[epoch] = {
"sensors.temperature": sensors.temperature.value(),
"sensors.humidity": sensors.humidity.value()
"sensors.illuminance": sensors.illuminance.value()
};
mqttClient.publish('JA347400060803/messages/json',
JSON.stringify(payload), 0);
}, 10000);
}
31. // Send SMS via Twilio
function sendSMS(to, message)
{
var accountSID = application.config.getString("twilio.accountSID");
var authToken = application.config.getString("twilio.authToken");
var from = application.config.getString(“twilio.from");
var twilioHttpRequest = new HTTPRequest(
"POST",
"https://api.twilio.com/2010-04-01/Accounts/"
+ accountSID
+ "/SMS/Messages"
);
twilioHttpRequest.authenticate(accountSID, authToken);
twilioHttpRequest.contentType = "application/x-www-form-urlencoded";
twilioHttpRequest.content =
"From=" + encodeURIComponent(from) +
"&To=" + encodeURIComponent(to) +
"&Body=" + encodeURIComponent(message);
twilioHttpRequest.send(function(result) {
logger.information("Twilio SMS Response: ",
result.response.status,
result.response.content);
});
}
32. var sensors = require('sensors.js');
var enableSMS = true;
sensors.illuminance.on('valueChanged', function(ev) {
logger.notice("valueChanged: " + ev.data);
if (ev.data < 10)
{
logger.warning("Lights out!");
if (enableSMS)
{
sendSMS("+436765166737", "Lights out!");
enableSMS = false;
}
}
else if (ev.data > 50)
{
enableSMS = true;
}
});