Mastering the IoT With JavaScript and C++ - Günter Obiltschnig
-
Upload
withthebest -
Category
Technology
-
view
134 -
download
3
Transcript of Mastering the IoT With JavaScript and C++ - Günter Obiltschnig
Mastering The IoT With JavaScript And C++
Günter Obiltschnig Applied Informatics Software Engineering GmbH [email protected] @obiltschnig, @macchina_io
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)
m .ioacchina|‘makkina| – A modular open source toolkit for building
embedded IoT applications that connect sensors, devices and cloud services.
IoT Gateway
my-devices.netCloud Services
AirVantage, Amazon, Bluemix, Carriots, etc.
HTTP(S), REST MQTT
Remote Access my-devices.net
device apps local “business logic”
web services web visualization
database discoverability
Mobile/Web Clients
Devices/Sensor Networks
CoAP, IEEE 802.15.4, Modbus, USB, Bluetooth LE,
RS-232
POCOC++ LIBRARIES
platform abstraction, multithreading, XML and JSON processing, filesystem access,stream, datagram and multicast sockets, HTTP server and client, crypto, SSL/TLS, etc.
Remoting serialization, remote methods and events, IPC
Open Service Platform dynamic module system service registry web application server user authentication/authorization
Platform
JavaScript V8 JavaScript engine and C++ bindings/bridging
Devices and Sensors Sensors, Serial Port, GNSS/GPS, Accelerometer, I/O, …
Protocols MQTT, CoAP, Modbus, Bluetooth LE, XBee, …
Services WebEvent, DeviceStatus, NetworkEnvironment, …
WebUI Login, Launcher, Bundles, Playground, etc.
IoT Components
> 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, SQLiteAngularJS, 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
Why JavaScript?
> Popular and widely known*
> Standardized
> Easy to integrate into an existing framework
> Multiple embeddable engines available
> Good performance (for a scripting language)
> I like it ;-)
* but not necessarily universally liked
Sensors & Devices Protocols Cloud Services
Temperature, Ambient Light, Humidity, Air Pressure, etc.
HTTP, HTTPS AirVantage
I/O, LED, Trigger, Rotary Encoder
MQTT Bluemix
Accelerometer CoAP* Twitter
GNSS/GPS WebEvent Amazon IoT
Barcode Reader, RFID* WebTunnel Twilio (SMS)
XBee (ZigBee, IEEE 802.15.4) XBee API my-devices.net
TI SensorTag Bluetooth LE any with MQTT API
Serial Port Modbus any with HTTP/REST API
* planned
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 (Shell, UPnP, Remoting SOAP and JSON-RPC)
> customizable web user interface
> improved user authentication and authorization
> signed bundles
> pro support
Usage Examples
> Building Automation
> Vehicle Telematics/Connected Cars
> Energy Manager(photovoltaics and battery controller)
> Industrial Internet of Things Platform
Demo
Inside macchina.io
POCOC++ LIBRARIES
platform abstraction, multithreading, XML and JSON processing, filesystem access,stream, datagram and multicast sockets, HTTP server and client, crypto, SSL/TLS, etc.
Remoting serialization, remote methods and events, IPC
Open Service Platform dynamic module system service registry web application server user authentication/authorization
Platform
JavaScript V8 JavaScript engine and C++ bindings/bridging
Devices and Sensors Sensors, Serial Port, GNSS/GPS, Accelerometer, I/O, …
Protocols MQTT, CoAP, Modbus, Bluetooth LE, XBee, …
Services WebEvent, DeviceStatus, NetworkEnvironment, …
WebUI Login, Launcher, Bundles, Playground, etc.
IoT Components
POCO C++ Libraries
> Started 2004
> ~300.000 LOC
> 1000+ classes
> on GitHub since 2012 1730+ stars640+ forks40-200 clones/day
> ~100 contributors
> Boost License
> http://pocoproject.org
POSIX, WIN32, other (RT)OS API
Foundation
C++ and C Standard LibrariesAp
plic
atio
n
Zip
Net
Crypto
Data
SQLite
ODBC
MySQL
NetSSL
Util
Tools, Utilities and additional Libraries
XML JSON
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
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
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)
OperatingSystem
API
Std. C/C++ LibrariesService Registry
Portable Runtime Environment
Life
Cyc
le
Man
agem
ent
Bundle Managem
ent
Stand
ard
Service
s
Bundles
install, resolve, start, stop and uninstall bundles
provide services to other bundles and find services
provided by other bundles
manage bundle versionsand dependencies
web server, web- and console-
based management, user authentication and authorization,
preferences, etc.
application-specific functionality and services
Combining POCO C++ Libraries and V8
> JavaScript is single-threaded and garbage-collected
> POCO is multithreaded (specifically web server)
> Make C++ object available to JavaScripteasy for static objects, just provide Wrapper
> Allow JavaScript code to create C++ objectseasy if you don’t care about memory/resource leaks
> Register a callback function called by GC when object is deletedallows you to properly delete underlying c++ object
> However, V8 does not do callbacks when script endswrapped C++ objects won’t be deleted, leaks resulting
> Need to track every C++ object a script creates and clean up afterwards :-(
Automatic JavaScript Wrappers for C++ Objects
// Sensor.h
//@ remote class Sensor: public Device { public: Poco::BasicEvent<const double> valueChanged;
virtual double value() const = 0; virtual bool ready() const = 0; };
Sensor.h
RemoteGen
lots of generated source files
RemoteGen.xml
Service<<generatedFrom>>
IService
ServiceProxyServiceRemoteObject
The interface class has all @remote methods from the service class.
ServiceSkeleton
<<invokes>>
<<generated>>
<<generated>><<generated>>
<<generated>>Service
ServerHelper
<<generated>>
Registers Skeleton, RemoteObject and EventDispatcher (if needed) with the Object
Request Broker.
ServiceProxyFactory
<<creates>>
<<generated>>Service
ClientHelper
<<generated>>
Registers ProxyFactory with the Object Request
Broker.
<<registers>><<registers>>
<<registers>>
ServiceEventSubscriber
<<generated>>Service
EventDispatcher
<<generated>>
<<registers>>
var tempSensor = ...;
tempSensor.on(‘valueChanged', function(ev) { var temp = ev.data; // ... });
if (tempSensor.ready()) { var temp = tempSensor.value(); // ... }
JavaScript Samples
// sensors.js (search sensors by physical quantity)
var sensors = {};
var illuminanceRefs = serviceRegistry.find( 'io.macchina.physicalQuantity == "illuminance"'); if (illuminanceRefs.length > 0) { sensors.illuminance = illuminanceRefs[0].instance(); }
var temperatureRefs = serviceRegistry.find( 'io.macchina.physicalQuantity == "temperature"'); if (temperatureRefs.length > 0) { sensors.temperature = temperatureRefs[0].instance(); }
var humidityRefs = serviceRegistry.find( 'io.macchina.physicalQuantity == "humidity"'); if (humidityRefs.length > 0) { sensors.humidity = humidityRefs[0].instance(); }
module.exports = sensors;
// sensors.js (search sensors by ID)
var sensors = {};
var illuminanceRef = serviceRegistry.findByName( 'io.macchina.xbee.sensor.illuminance#0013A20040A4D7F7'); if (illuminanceRef) { sensors.illuminance = illuminanceRef.instance(); }
var temperatureRef = serviceRegistry.findByName( 'io.macchina.xbee.sensor.temperature#0013A20040A4D7F7'); if (temperatureRef) { sensors.temperature = temperatureRef.instance(); }
var humidityRef = serviceRegistry.findByName( 'io.macchina.xbee.sensor.humidity#0013A20040A4D7F7'); if (humidityRef) { sensors.humidity = humidityRef.instance(); }
module.exports = sensors;
// database.js
var data = require('data');
module.exports = {
path: bundle.persistentDirectory + 'logger.db',
session: new data.Session('SQLite', database.path),
logIntervalSeconds: application.config.getInt( 'datalogger.intervalSeconds', 30),
keepDataSeconds: application.config.getInt( 'datalogger.keepDataSeconds', 3600) };
// logger.js
var sensors = require('sensors'); var db = require('database');
db.session.execute('PRAGMA journal_mode=WAL'); db.session.execute('CREATE TABLE IF NOT EXISTS datalog ( \ timestamp INTEGER, \ illuminance FLOAT, \ temperature FLOAT, \ humidity FLOAT \ )');
setInterval( function() { db.session.execute('INSERT INTO datalog VALUES (?, ?, ?, ?)', DateTime().epoch, sensors.illuminance.value(), sensors.temperature.value(), sensors.humidity.value()); }, db.logIntervalSeconds*1000);
// logger.js (continued)
setInterval( function() { var cutoffTime = DateTime().epoch - db.keepDataSeconds; db.session.execute('DELETE FROM datalog WHERE timestamp < ?', cutoffTime); }, db.keepDataSeconds*1000);
// history.jss - GET /history.jss?item=temperature&maxItems=10
var db = require('database');
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');
// history.jss (continued)
for (var row = 0; row < recordSet.rowCount; row++) { var time = recordSet.getValue('timestamp'); var value = recordSet.getValue(item); var date = new LocalDateTime(1970, 1, 1); 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();
// MQTT to AirVantage
var sensors = require('sensors');
var mqttClientRefs = serviceRegistry.find('io.macchina.mqtt.id == "airvantage"'); if (mqttClientRefs.length > 0) { console.log('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);
// twilio.js
var net = require('net');
module.exports = {
sendSMS: function(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 net.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) { console.log("Twilio SMS Response: %O", result); });
};
var sensors = require('sensors'); var twilio = require('twilio');
var enableSMS = true;
sensors.illuminance.on('valueChanged', function(ev) {
logger.notice("valueChanged: " + ev.data);
if (ev.data < 10) { logger.warning("Lights out!"); if (enableSMS) { twilio.sendSMS("+4367612345678", "Lights out!"); enableSMS = false; } } else if (ev.data > 50) { enableSMS = true; } });
Q&A
[email protected] | @obiltschnig | obiltschnig.com macchina.io | my-devices.net | pocoproject.org | www.appinf.com