Angular javascript aplikácia a Arduino

Zápisník experimentátora

Hierarchy: Node.js

V tomto článku si prepojíme Arduino a HTML stránku, na ktorej sa nachádza AngularJS aplikácia. Na prepojenie využijeme Node.js a komunikáciu medzi serverom a stránkou zabezpečí Socket.IO. Arduino bude pripojené k serveru cez balíček serialport. Výsledkom bude stránka, ktorú si môžete otvoriť vo svojom prehliadači. Na stránke sa nachádza zoznam sériových portov a po pripojení sériového portu sa začne na monitore zobrazovať komunikácia s Arduinom.

Arduino

Zdrojový kód pre Arduino je zameraný na jednoduchosť. Každú sekundu zasiela nové údaje na sériový port. To nám vytvára pravideľný zdroj údajov.

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.println(millis());
  delay(1000);
}

Node.js

Úlohou Node.js servera je zabezpečiť komunikáciu medzi Arduinom a Angular aplikáciou. Server sa skladá z niekoľkých súborov. Základom celej aplikácie je vygenerovaný zdrojový kód pre webserver Express. Ten som upravil tak, že som z generovaného kódu odstránil všetky nepotrebné súbory a nechal tam len absolútne minimum.

bin/www

Zdrojový kód vytvára HTTP server na porte 3000. Do kódu som doplnil iba jeden riadok sck.connectSocket(server);, pomocou ktorého pridám podporu socketov do servera.

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 samotnej Express aplikácie. Doplnil som iba jednu funkciu, pomocou ktorej získa AngularJS aplikácia zoznam sériových portov. To zabezpečuje riadok 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 komunikáciu cez sockety. Po pripojení AngularJS aplikácie si otvorí označený sériový port a číta z neho jednotlivé riadky, ktoré zasiela Arduino. Tie riadky cez socket odosiela do AngularJS aplikácie. A reaguje na príkazy z AngularJS aplikácie, ktorá môže zaslať príkaz na pripojenie alebo odpojenie 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 aplikácia sa skladá z dvoch súborov.

public/index.html

Jednoduchá stránka, ktorá v hornej časti obsahuje výber sériového portu a dve tlačidlá, ktoré slúžia na pripojenie a odpojenie k Arduinu. Stránka je v responzívnom dizajne pomocou knižnice Bootstrap. Stránka si stiahne niekoľko javascriptov. Je dôležité, aby boli v danom poradí.

  • AngularJS - Framework, pomocou ktorého sa ľahko vytvárajú HTML aplikácie.
  • Socket.IO - Sockety pre HTML stránky.
  • arduino_serial.js - Kód aplikácie, ktorá komunikuje s Node.js serverom pomocou socketov.

V AngularJS sa aplikácia začína na riadku <div ng-app="arduinoSerialApp" ng-controller="arduinoSerialCtrl">. Zoznam sériových portov vytvoríte príkazom <select ng-model="p" ng_options="port.comName for port in ports"></select>. Tlačidlo sa vytvára pomocou kódu <button ng-click="doConnect()">Connect</button>. Údaje z Angular aplikácie sa zobrazujú pomocou kódu {{data}}. A štruktúrované údaje si môžete pohodlne formátovať do čitateľnej podoby pomocou kódu {{ports | json}}.

Tento blog nie je učebnicou knižnice Angular, preto si len všimnite, že všetko čo sa prepája s funkciami alebo údajmi zo zdrojového kódu v súbore arduino_serial.js je v súbore umiestnené v premennej $scope. V zdrojovom kóde nájdete ukážky na bežné činnosti, ako sú zobrazovanie údajov a reakcie na kliknutie.

<!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 súbore sa nachádza zdrojový kód aplikácie. Kód má len niekoľko riadkov, pretože väčšinu práce vykoná knižnica Angular.

  • Pomocou kódu var socket = io(); si vytvoríme socket do Node.js servera. Cezeň budeme prenášať všetky údaje z Arduina.
  • Sockety zasielajú údaje ako správy a vy musíte reagovať na konkrétnu správu. V tomto prípade reagujeme na správu socket.on('serial_data', ... a pridáme si zaslaný riadok z Arduina do poľa a skrátime ho na maximálne 20 riadkov. Pretože v tomto prípade nepoužívame Angular kód, musíme dať vedieť Angular aplikácii, že došlo ku zmenám v údajoch pomocou funkcie $scope.$apply();.
  • Pri štarte si ešte vyžiadame aktuálny zoznam sériových portov a posledné dve funkcie ukazujú, ako cez socket zasielame správu, na ktorú bude reagovať 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 = [];
  };

}]);

Inštalácia

Najprv do Arduina nahrajte zdrojový kód pomocou Arduino IDE. Node.js aplikáciu vytvoríte pomocou niekoľkých krokov.

Ak máte nainštalovaný program git, môžete si zdrojové kódy nainštalovať napríklad takto. Ak ho nemáte, dajú sa stiahnuť aj ako zip archív. Spustite si konzolu príkazového riadku. Dôležitý je posledný riadok, ktorý nainštaluje všetky súvisiace knižnice, ktoré sú v súbore package.json.

cd d:
mkdir arduino
cd arduino
git clone https://github.com/roboulbricht/arduinoslovakia
cd arduinoslovakia\node.js\arduino_serial_html
npm install

Spustenie

Pomocou príkazu npm start spustite server a v prehliadači otvorte stránku http://localhost:3000/.

Zdrojový kód

Zdrojový kód sa nachádza na serveri GitHub.


24.12.2017


Menu