Angular javascript app and Arduino

Zápisník experimentátora

Hierarchy: Node.js

In this article we'll link the Arduino and HTML page that includes AngularJS application. We will use Node.js to connect and the communication between the server and the site will be handled by Socket.IO. Arduino will be connected to the server via the serialport package. The result will be a page you can open in your browser. A list of serial ports is on the page, and when the serial port is connected, the Arduino output will begin to appear on the monitor.

Arduino

The source code for Arduino is focused on simplicity. Every second sends new data to the serial port. This creates a regular data source.

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

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

Node.js

The role of the Node.js server is to ensure communication between the Arduino and the Angular application. The server consists of several files. The base of the entire application is the generated source code for the Web server Express. I modified it by removing all unnecessary files from the generated code and leaving only an absolute minimum.

bin/www

The source code builds an HTTP server on port 3000. I added only one line sck.connectSocket(server);, to add socket support to the server.

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

This is the code of the Express application itself. I've just added one function to get the AngularJS application's list of serial ports. This is ensured by the line 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

This code provides communication via sockets. When connected, the AngularJS application opens the selected serial port and reads the individual rows sent by Arduino. These rows are sent through the socket to the AngularJS application. And this code responds to commands from the AngularJS application, which can send a command to connect or disconnect from the serial port.

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 application consists of two files.

public/index.html

A simple page that contains a serial port selection and two buttons that connect and disconnect to Arduin at the top. The page is in a responsive design using the Bootstrap library. The page will download several javascripts. It is important to be in a given order.

  • AngularJS - A framework that makes HTML applications easy to create.
  • Socket.IO - Sockets for HTML pages.
  • arduino_serial.js - An application code that communicates with the Node.js server using the sockets.

In AngularJS, the application starts at line <div ng-app="arduinoSerialApp" ng-controller="arduinoSerialCtrl">. To create a list of serial ports use command <select ng-model="p" ng_options="port.comName for port in ports"></select>. The button is created using the code  <button ng-click="doConnect()">Connect</button>. Angular app data is displayed using {{data}}. And structured data can be conveniently formatted in readable form using {{ports | json}}.

This blog is not a manual for the Angular library, so just note that everything associated with the functions or data from the source code in the arduino_serial.js file is located in the file in the variable $scope. The source code provides previews of common activities such as data display and click-to-action.

<!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

This file contains the application source code. The code has only a few lines because most of the work is done by the Angular library.

  • Using the code var socket = io(); we create a socket into the Node.js server. Through the socket we will transmit all the data from Arduino.
  • Sockets send data as messages, and you must respond to a specific message. In this case, we respond to socket.on('serial_data', ... and we add the sent Arduino line to the array and shorten it to a maximum of 20 items. Because we do not use Angular code in this case, we need to know the Angular application to change data using the function $scope.$apply();.
  • At startup, we require the current list of serial ports and the last two functions show how we send a message to the Node.js server via the socket.
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 = [];
  };

}]);

Installation

First, upload Arduino source code using Arduino IDE. You can create the Node.js application in a few steps.

If you have a git installed, you can install the source code as follows. If you do not have it, you can also download zip archives. Run the command line console. Important is the last line that installs all the related libraries that are in package.json.

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

The start

Use the npm start command to start the server and open http://localhost:3000/ in the browser.

Source code

The source code is located on the GitHub server.


25.12.2017


Menu