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