Angular javascript aplikace a Arduino

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.

Arduino

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);
}

Node.js

Ú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.

bin/www

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);
}

app.js

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;

socket.js

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

Angular aplikace se skládá ze dvou souborů.

public/index.html

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í.

  • AngularJS - Framework, pomocí kterého se snadno vytvářejí HTML aplikace.
  • Socket.IO - Sockety pro HTML stránky.
  • arduino_serial.js - Kód aplikace, která komunikuje s Node.js serverem pomocí socketů.

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>

public/javascripts/arduino_serial.js

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.

  • Pomocí kódu var socket = io(); si vytvoříme socket do Node.js serveru. Přes něj budeme přenášet všechna data z Arduina.
  • Sockety předávají údaje jako zprávy a vy musíte reagovat na konkrétní zprávu. V tomto případě reagujeme na zprávu 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();.
  • Při startu si ještě vyžádáme aktuální seznam sériových portů a poslední dvě funkce ukazují, jak přes socket zasíláme zprávu, na kterou bude reagovat Node.js server.
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 = [];
  };

}]);

Instalace

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

Spuštění

Pomocí příkazu npm start spusťte server a v prohlížeči otevřete stránku http://localhost:3000/.

Zdrojový kód

Zdrojový kód se nachází na serveru GitHub.


26.12.2017


Menu