Zápisník experimentátora
Hierarchy: Node.js
V tomto článku si propojíme Arduino a HTML stránku, na které se nachází AngularJS aplikace. K propojení využijeme Node.js a komunikaci mezi serverem a stránkou zajistí Socket.IO. Arduino bude připojeno k serveru přes balíček serialport. Výsledkem bude stránka, kterou si můžete otevřít ve svém prohlížeči. Na stránce se nachází seznam sériových portů a po připojení sériového portu se začne na monitoru zobrazovat komunikace s Arduinem.
Zdrojový kód pro Arduino je zaměřen na jednoduchost. Každou sekundu zasílá nové údaje na sériový port. To nám vytváří pravidelný zdroj dat.
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println(millis());
delay(1000);
}
Úkolem Node.js serveru je zajistit komunikaci mezi Arduinem a Angular aplikací. Server se skládá z několika souborů. Základem celé aplikace je vygenerovaný zdrojový kód pro webserver Express. Ten jsem upravil tak, že jsem z generovaného kódu odstranil všechny nepotřebné soubory a nechal tam jen absolutní minimum.

Zdrojový kód vytváří HTTP server na portu 3000. Do kódu jsem doplnil pouze jeden řádek sck.connectSocket(server);, pomocí kterého přidám podporu socketů do serveru.
var app = require('../app');
var debug = require('debug')('serialhtml:server');
var http = require('http');
var sck = require('../socket');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
sck.connectSocket(server);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
Toto je kód samotné Express aplikace. Doplnil jsem jen jednu funkci, pomocí které získá AngularJS aplikace seznam sériových portů. To zajišťuje řádek app.get('/port_list', ...
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var serialport = require('serialport');
var app = express();
// uncomment after placing your favicon in /public
app.use(favicon(path.join(__dirname, 'public/images', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.get('/port_list', function (req, res) {
serialport.list(function (err, ports) {
res.json(ports);
});
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
Tento kód zabezpečuje komunikaci přes sockety. Po připojení AngularJS aplikace si otevře označený sériový port a čte z něj jednotlivé řádky, které zasílá Arduino. Ty řádky přes socket odesílá do AngularJS aplikace. A reaguje na příkazy z AngularJS aplikace, která může zaslat příkaz k připojení nebo odpojení od sériového portu.
const serialport = require('serialport');
const sp_readline = serialport.parsers.Readline;
module.exports.connectSocket = function(server) {
var io = require('socket.io')(server);
var port;
io.on('connection', function(socket){
console.log('HTML page connected.');
socket.on('disconnect', function(){
console.log('HTML page disconnected.');
});
socket.on('do_connect', function(p) {
if(port)
return;
console.log('do_connect:', p);
port = new serialport(p, {
baudRate: 9600
});
const parser = new sp_readline();
port.pipe(parser);
parser.on('data', function(data){
data=data.replace('\r','');
console.log('Arduino', data);
io.emit('serial_data',data);
});
port.on('error', function(e) {
console.error(e.message);
});
port.on('open', function() {
console.log('Serial Port Opened');
});
port.on('close', function(err) {
console.log('Serial Port Closed:', err);
});
});
socket.on('do_disconnect', function(p) {
if(port===undefined)
return;
console.log('do_disconnect:', p);
port.close(function() {
port=undefined;
});
});
});
};
Angular aplikace se skládá ze dvou souborů.

Jednoduchá stránka, která v horní části obsahuje výběr sériového portu a dvě tlačítka, která slouží k připojení a odpojení k Arduinu. Stránka je v responzívnom designu pomocí knihovny Bootstrap. Stránka si stáhne několik javascriptů. Je důležité, aby byly v daném pořadí.
V AngularJS se aplikace začíná na řádku <div ng-app="arduinoSerialApp" ng-controller="arduinoSerialCtrl">. Seznam sériových portů vytvoříte příkazem <select ng-model="p" ng_options="port.comName for port in ports"></select>. Tlačítko se vytváří pomocí kódu <button ng-click="doConnect()">Connect</button>. Údaje z Angular aplikace se zobrazují pomocí kódu {{data}}. A strukturované údaje si můžete pohodlně formátovat do čitelné podoby pomocí kódu {{ports | json}}.
Tento blog není učebnicí knihovny Angular, proto si jen všimněte, že vše co se propojuje s funkcemi nebo údaji ze zdrojového kódu v souboru arduino_serial.js je v souboru umístěno v proměnné $scope. Ve zdrojovém kódu naleznete ukázky na běžné činnosti, jako jsou zobrazování údajů a reakce na kliknutí.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Arduino Serial HTML</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
<script src="javascripts/arduino_serial.js"></script>
<link rel="icon" type="image/x-icon" href="/images/favicon.ico">
</head>
<body>
<div ng-app="arduinoSerialApp" ng-controller="arduinoSerialCtrl">
<div class="container">
<div class="row">
<h1>Arduino Serial HTML</h1>
<p>Port: <select ng-model="p" ng_options="port.comName for port in ports"></select> <button ng-click="doConnect()">Connect</button> <button ng-click="doDisconnect()">Disconnect</button></p>
</div>
<div class="row">
<div class="col-sm-6">
<pre>data: {{data}}</pre>
<pre>{{lines | json}}</pre>
</div>
<div class="col-sm-6">
<pre>{{ports | json}}</pre>
</div>
</div>
</div>
</div>
</body>
</html>
V tomto souboru se nachází zdrojový kód aplikace. Kód má jen několik řádků, protože většinu práce vykoná knihovna Angular.
var socket = io(); si vytvoříme socket do Node.js serveru. Přes něj budeme přenášet všechna data z Arduina.socket.on('serial_data', ... a přidáme si zaslaný řádek z Arduina do pole a zkrátíme jej na maximálně 20 řádků. Protože v tomto případě nepoužíváme Angular kód, musíme dát vědět Angular aplikaci, že došlo ke změnám v údajích pomocí funkce $scope.$apply();.
var app = angular.module('arduinoSerialApp', []);
app.controller('arduinoSerialCtrl', ['$scope', '$http', function($scope, $http) {
$scope.ports = [];
$scope.lines = [];
var socket = io();
socket.on('serial_data', function(data) {
$scope.data = data;
$scope.lines.push(data);
if($scope.lines.length>20)
$scope.lines.shift();
$scope.$apply();
})
$http.get('/port_list')
.then(function(response) {
console.log(response.data);
$scope.ports = response.data;
if($scope.ports.length)
$scope.p = $scope.ports[0];
}, function(error) {
console.log(error);
});
$scope.doConnect = function() {
socket.emit('do_connect', $scope.p.comName);
};
$scope.doDisconnect = function() {
socket.emit('do_disconnect', $scope.p.comName);
$scope.data = '';
$scope.lines = [];
};
}]);
Nejprve do Arduino nahrajte zdrojový kód pomocí Arduino IDE. Node.js aplikaci vytvoříte pomocí několika kroků.
Pokud máte nainstalovaný program git, můžete si zdrojové kódy nainstalovat například takto. Pokud ho nemáte, dají se stáhnout i jako zip archiv. Spusťte si konzolu příkazového řádku. Důležitý je poslední řádek, který nainstaluje všechny související knihovny, které jsou v souboru package.json.
cd d:
mkdir arduino
cd arduino
git clone https://github.com/roboulbricht/arduinoslovakia
cd arduinoslovakia\node.js\arduino_serial_html
npm install
Pomocí příkazu npm start spusťte server a v prohlížeči otevřete stránku http://localhost:3000/.
Zdrojový kód se nachází na serveru GitHub.
26.12.2017