Datos de IoT en tiempo real

Leer y mostrar datos de un módulo de IoT en tiempo real con gráficos. La idea es que podáis montaros vuestro propio proyecto de IoT y con este taller podáis montar la plataforma para leer y mostrar los datos.

Si habéis seguido las instrucciones de Introducción y preparación ya deberíais de tener todo preparado para poder seguir este taller.

Partes del sistema

Explicación y clasificación de las diferentes partes del sistema según varios criterios.

Partes del sistema

Primero que nada vamos a explicar en qué consiste este taller y cómo se conectan las piezas del mismo. Vamos a disponer principalmente de 3 componentes:

  • Hardware. Puede ser un Arduino, una Raspberry Pi, un ESP8266 o cualquier otro con el que estéis trabajando. Este lo ponéis vosotros y va a tener que comunicarse con internet ya sea mediante wifi, ethernet u otros medios.
  • Plataforma web. El código que está en un hosting que conecta las diferentes partes. Recibirá los datos del hardware, los procesa si hace falta y los muestra donde haga falta.
  • Navegador web. Con él podréis acceder a las gráficas e informes que produce la plataforma web.

Plataforma web

Vamos a ver cómo haremos la plataforma web y de qué partes se compone la misma:

  • Back-end: nombre que se le da al código que se está ejecutando en el servidor. Para este taller usaremos el lenguaje Javascript con Node.js y las librerías server y mongoose.
  • API: conjunto de llamadas y procesos accesibles desde fuera que usaremos para comunicarnos tanto con el hardware como con la página web.
  • Front-end: el código que se ejecuta en el navegador web. Usaremos la librería Chart.js para mostrar los gráficos y el protocolo de transmisión websockets.

Estructura de archivos

Vamos a crear la plataforma web. Al final del taller nos tiene que quedar esta estructura de archivos:

  • public/
    • javascript.js
    • style.css
  • .gitignore
  • index.html
  • package.json
  • server.js

Init

Primero que nada, cuando nuestra carpeta todavía está vacía, procedemos a crear el proyecto con Git y con NPM. Git es un sistema de control de versiones que nos permitirá subir nuestros archivos al hosting, y NPM es el que se encarga de que todas las librerías son correctas.

Abrimos la termina o linea de comandos y escribimos:

$ git init
$ npm init --yes   # Evitamos todas las preguntas

Una vez hecho esto, tendremos la carpeta .git (oculta) y el archivo package.json creados en nuestra carpeta.

Creamos un archivo que se llame ".gitignore" y escribimos dentro lo siguiente desde Atom:

node_modules
npm-debug.log*

Esto hará que no subamos ciertos archivos al servidor tanto por seguridad como para mejorar la velocidad de carga.

Por último instalaremos las dependencias que necesitamos para el proyecto:

npm install server mongoose --save

Mostrar la web

Una vez ya tenemos esto, procedemos a crear el archivo principal que por ahora sólo mostrará la página web inicial. Sólo vamos a mostrar una página de HTML y el resto de datos los cargaremos a través de la API más adelante:

// server.js
// Crear las variables que vamos a usar después
const server = require('server');
const { get, post } = server.router;
const { file } = server.reply;

// Enviar el index.html cuando pidan URL/
const routes = [
  get('/', file('index.html'))
];

// Lanzar el servidor con las opciones por defecto
server(routes);

Lo que hace este código es que, cuando un navegador o cualquier otro servicio hace una petición a cualquier página vamos a mostrar el index.html.

Ahora definamos nuestro html dentro de este index.html:

<!DOCTYPE html>
<html>
  <head>
    <!-- Estándar para las páginas web. NO LO CAMBIES -->
    <meta charset="utf-8" />

    <!-- Nombre en la pestaña y en la barra del navegador -->
    <title>Gráficos en tiempo real</title>

    <!--Importar materialize.css y la fuente de iconos de Google-->
    <link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.2/css/materialize.min.css" rel="stylesheet">

    <!-- Nuestra propia hoja de estilos (public/style.css) -->
    <link type="text/css" rel="stylesheet" href="style.css" media="screen,projection"/>

    <!-- Este sitio está optimizado para móvil -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  </head>

  <body>

    <!-- Pondremos todo nuestro HTML dentro de main -->
    <main>

      <!-- Cada section va a ser un gráfico con su explicación -->
      <section>
        <h1>Casa</h1>

        <h2>Temperatura</h2>
        <canvas id="temperature" width="800" height="200"></canvas>

        <h2>Luz</h2>
        <canvas id="light" width="800" height="200"></canvas>
      </section>
    </main>

    <!-- Librerías externas de Javascript a cargar -->
    <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.2/js/materialize.min.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/socket.io.slim.min.js"></script>
    <script src="https://unpkg.com/[email protected]"></script>

    <!-- Nuestra lógica en javascript para el navegador (public/javascript.js) -->
    <script src="javascript.js"></script>
  </body>
</html>

Estamos referenciando un par de archivos que todavía no existen y que veremos en el próximo capítulo, style.css (que se encuentra en public/style.css) y javascript.js (que se encuentra en public/javascript.js).

Esta referencia es correcta; cuando un servidor está ejecutándose, normalmente se ponen los archivos estáticos (imágenes, CSS y Javascript para front-end) para que puedan ser incluidos desde un directorio.

En Node.js se suele usar la carpeta public, así que cualquier archivo con el nombre "NombreDelArchivo.EXTENSION" dentro de esta carpeta será accesible directamente desde "http://URL.com/NombreDelArchivo.EXTENSION".

Hoja de estilos

En cuanto a estilo simplemente vamos a hacer una columna, añadirle un ancho máximo y hacer que las gráficas ocupen el 100% de este ancho:

main {
  /* Darle un ancho máximo (y 100% para pantallas pequeñas) */
  width: 100%;
  max-width: 900px;

  /* Para centrar la columna. Viva CSS! */
  margin: 0 auto 300px;
}

/* Las gráficas ocupan el 100% de ancho del main */
canvas {
  width: 100%;
}

Recibir datos

El protocolo HTTP acepta varios métodos de transporte de datos (relacionado: REST - Representational State Transfer), entre ellos el GET y POST. El método GET ya lo hemos usado cuando en el server.js escribíamos esto:

const routes = [
  get('*', file('index.html'))
];

Ese get() representa al navegador haciendo una petición de datos (get state). Cuando el navegador o cualquier otro dispositivo quiere enviar datos se usa normalmente el método "POST" y estos datos se envían en el body.

Formulario

Vamos a recibir datos de un formulario. Esto es un ejercicio aparte, pero también interesante. Escribiremos esto en nuestro server.js:

const server = require('server');
const { get, post } = server.router;
const { file } = server.reply;

const recordData = ctx => {
  console.log(ctx.req.body);
  return 'Hecho!';
};

const routes = [
  get('*', file('index.html')),
  post('/', recordData)
];

const options = { connect: { csrf: false } };

server(options, routes);

En recordData() estamos haciendo un console.log() que lo que hace es mostrar por la terminal los datos que se envían y devolviendo un mensaje corto al navegador.

La opción que estamos pasando no hace falta entenderla ahora mismo, es un elemento de seguridad estándar de web pero para IoT usaremos otro método.

En index.html crearemos un formulario típico de contacto que nos pide el nombre, email y un mensaje. Metemos esto dentro del <body>:

<form method="POST" action="/">
  <input name="nombre" placeholder="Nombre" type="text">
  <input name="email" placeholder="Email" type="email">
  <textarea name="texto" placeholder="Nombre"></textarea>
  <button>Send!</button>
</form>

 

Ejemplos

Tiempo real

Websockets

Gráficas

Front-end

Subirlo a internet

Heroku

Git

Extra

Seguridad y acceso

Persistencia

Fin!