Tag Archives: android

Comandos por BLE – (Parte 2 de 2) – El Transmisor (TX)

22 Jul , 2017,
Jose Nunez
, , , , , ,
No Comments

Como lo prometido es deuda, acá está la 2da entrega referente a controlar cosas con telefonos celulares mediante Bluetooth Low Energy (BLE) usando IONIC 3.

En esta segunda parte, implementaremos una App (Android y IOS) usando IONIC 3 que le envía comandos al micro-controlador mediante el sistema BLE del teléfono.

Para ver la Parte 1 haz clic acá

(!) Antes de seguir estas instrucciones asegúrese de entender las Condiciones de Uso de nuestro sitio.

Programando el App

En resumen:

  1. IONIC 3 es un sistema para crear aplicaciones móviles usando tecnologías híbridas (cordova, angularjs 4, typescript, html, css, etc.)
  2. Necesitaremos configurar IONIC3 en nuestro computador; esto incluye NodeJS, NVM, NPM y IONIC.
  3. También necesitaremos un muy buen editor de código para TypeScript. Puedo recomendarles Visual Studio Code o Atom.
  4. Descargamos el código del app de ejemplo de mi repositorio de Github y lo abrimos con el IDE de ARDUINO.git clone https://github.com/janunezc/robotics.gitEl código se ubica en ~/robotics/intel_curie/ble_command_app/
  5. En el celular podemos utilizar el sistema Ionic View (descargado del App Store o Google Play) y previsualizar el app 03B50F88Las siguientes imágenes muestran la operación del app

 

Seguidamente mostramos las partes más importantes del código original del programa con los comentarios detallados sobre su funcionamiento. Es realmente sencillo; podemos resumirlo en:

  1. El tab “SCAN” implementa un proceso básico de búsqueda de dispositivos Bluetooth. No vamos a detallar mucho en este tutorial sobre ese tab.
  2. El tab “LED” implementa la búsqueda de un dispositivo específico y una vez que lo encuentra permite interactuar con el LED para encenderlo y apagarlo.
    1. Los componentes (tabs) de la aplicación IONIC3 se dividen en 3 elementos principales: una vista o view (led.html), un controlador (led.ts) y un modelo que corresponde a los datos que va a manejar la aplicación y que se ubican en el objeto this y sus propiedades públicas.
    2. El archivo led.html es realmente simple. Aparte de los párrafos de texto expicativo y títulos, posee un botón que tiene algunas características importantes de AngularJS como lo son el atributo [disabled] ligado a una variable del modelo denominada “ejecutandoComando”. Cuando la variable tiene un valor verdadero (true) el atributo disabled se activa y el botón se deshabilita. A su vez tiene una directiva (click) que ejecuta la función “Command()” del controlador.
    3. Una parte importante del archivo led.html es el último párrafo en la línea 11. Este utiliza una directiva *ngFor para iterar sobre la variable “messages” y mostrar cada mensaje como un párrafo ( <p> ) individual. Esto nos permite mostrar mensajes de aclaración conforme se ejecutan los procesos en el controlador.
  3. Exploremos ahora el archivo led.ts. Se trata de un script escrito en TypeScript. Un pseudo-lenguaje basado en JavaScript que permite una orientación a objetos más elaborada.
    1. Lo primero que se realiza en led.ts es importar las librerías necesarias. Dos de las más notorias serán BLE y ApplicationRef. La primera permite comunicación BLE, y la segunda permite manipular el framework de Angular para refrescar la pantalla cuando sea necesario.
    2. La siguiente parte super importante es el constructor, el cual implementa una serie de inyecciones de dependencia que vale la pena mencionar.
    3. Nótese la forma en que se inyectan en el constructor tanto la librería BLE como la librería de Angular ApplicationRef. En el constructor también se inicializan variables importantes tales como el ID de servicio y el ID de Característica que se van a manipular.
    4. Las funciones más interesantes en este archivo son:
      1. findDevice() que busca un dispositivo particular y una vez que lo encuentra cambia el modo de comandos para encender o apagar un LED. Para esto usa la funcion de BLE nativo denominada ble.scan()
      2. txData() que utiliza ble.connect y ble.write para conectarse al dispositivo y hacerle llegar un mensaje.
      3. SetMessage() tambien merece ser mecionado como la forma que tenemos de agregar mensajes de control que nos permitan entender cómo se está ejectuando el programa.

Espero que este artículo les sea de utilidad junto con el anterior para aprender a enviar comandos a un dispositivo BLE basado en Intel Curie, mediante el teléfono celular.

Los dejo con estos enlaces de utilidad:

  1. Aprender TypeScript: https://www.typescriptlang.org/docs/home.html
  2. Aprender Ionic 3: https://ionicframework.com/
  3. Aprender Angular 4: https://www.youtube.com/watch?v=kFTmoLm9Jwg

Que estén bien!


led.html

<ion-header>
   <ion-navbar>
   <ion-title>LED</ion-title>
   <p>Acá podemos operar el LED que está implementado en TinyTILE</p>
   <button ion-button [disabled]="ejecutandoComando" (click)="Command()">{{ComandoTXT}}</button>
</ion-navbar>
</ion-header>
<ion-content padding>
   <p>Histórico de Ejecución: ({{myCount}} | {{test}})</p>
   <hr />
   <p *ngFor="let message of messages">{{message}}</p>
</ion-content>

led.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { BLE } from '@ionic-native/ble';
import { ApplicationRef } from '@angular/core';

@Component({
  selector: 'page-led',
  templateUrl: 'led.html'
})

export class LEDPage {

  public messages = []; //Histórico de ejecución
  public ejecutandoComando = false;
  public BLE;
  public ComandoTXT = "";
  public Value = "";
  public TargetDevice;
  private appRef;

  /**
   * CONSTANTES que serán usadas a lo largo del controlador
   */
  public constants  = {
      DEVICE_NAME: "COMANDO LED",
      CMD_FIND_DEVICE:"FIND DEVICE",
      CMD_STOP_SCAN:"Escaneando... (Clic para parar)",
      CMD_TOGGLE_LED:"CAMBIAR LED",
      ON:"ON",
      OFF:"OFF"
    };

  /**
   * Constructor para el controlador. Todo comienza acá...
   */
  constructor(public navCtrl: NavController, private ble: BLE /*Librería BLE nativa*/, private applicationRef : ApplicationRef /*Librería ApplicationRef de Angular*/ ) {
    this.appRef = applicationRef; //Referencia a la aplicación ANGULAR
    this.SetMessage("Constructor: Iniciado!");
    this.BLE = ble; //Acceso a la librería BLE Nativa
    this.ComandoTXT = this.constants.CMD_FIND_DEVICE;
    this.Value = "ON"; //VALOR PARA ENVIAR
    this.TargetDevice = {}; //Dispositivo al cual conectarse. Inicialmente vacío... se llenará cuando se encuentre el dispositivo en el escaneo.
    this['service_id'] = "db938b80-f010-44b6-8aa9-1835adf9419a"; //Identificador del servicio BLE a conectarse
    this['characteristic_id'] = "9906064e-9bbe-4eba-b415-bbd223f7d3d9"; //Identificador de la característica BLE a conectarse
    this.SetMessage("Constructor: Finalizado!");
  }

  /**
   * This is triggered when the button is clicked.
   */
  public Command(){
    this.ejecutandoComando = true;
    this.SetMessage("Command() - Comando recibido!");
    this.SetMessage(this["ComandoTXT"]);
    this.SetMessage("Interpretando comando...");

    if(this['ComandoTXT']===this.constants.CMD_FIND_DEVICE) {
      this.SetMessage("Ejecutando Comando: FIND DEVICE");
      this['ComandoTXT'] = this.constants.CMD_STOP_SCAN;
      this.findDevice();

    } else if (this['ComandoTXT']===this.constants.CMD_STOP_SCAN) {
      this.SetMessage("Ejecutando Comando: STOP SCAN");
      clearInterval(this['intervalHandle']);
      this['ComandoTXT'] = this.constants.CMD_FIND_DEVICE;
      this.ejecutandoComando = false;

    } else if(this['ComandoTXT']=== this.constants.CMD_TOGGLE_LED){
      this.SetMessage("Ejecutando Comando: TOGGLE LED: " + this.Value);
      this.SetMessage("Acá es donde enviamos un valor al dispositivo BLE");
      this.txData();
    }
  }

  /**
   * This searches for DEVICE in the AIR using SCAN technique.
   */
  private findDevice(){
      this.SetMessage("FindDevice() - INICIO!");
      let  ble = this.BLE;

      this.SetMessage("FindDevice() - ENABLE!");
      ble.enable();

      this.SetMessage("FindDevice() - SET INTERVAL!");
      this['intervalHandle'] = setInterval(() => { // PROGRAMAR UN TIMER DE 2.1 SEGUNDOS
        this.SetMessage("INTERVAL: INICIO! LLAMANDO A BLE SCAN...");
        ble.scan([], 2 /*seconds (0) */).subscribe( data => { //REALIZAR SCAN BLE POR 2 SEGUNDOS
          this.SetMessage("BLE SCAN CALLBACK: " + data['id'] + ' | ' + data['name'] + ' | ' + data['rssi']);
          if(data['name']==this.constants.DEVICE_NAME){
            this.SetMessage(this.constants.DEVICE_NAME + " HA SIDO ENCONTRADO!!");
            clearInterval(this["intervalHandle"]);
            this.TargetDevice = data;
            this["ComandoTXT"] = this.constants.CMD_TOGGLE_LED;
            this.ejecutandoComando = false;
          }
          this.appRef.tick();
        });
      },2100);//FIN DE LA DEFINICIÓN DEL TIMER
      this.ejecutandoComando = false;
    }

  /**
   * Transmitir datos al dispositivo BLE
   */
  private txData(){
    this.SetMessage("txData(): INICIO! Llamando a BLE CONNECT...");

    let id = this.TargetDevice.id;
    this.SetMessage("ID DE DISPOSITIVO: " + id);

    this.ble.connect(id).subscribe(datos=>{
      this.SetMessage("BLE CONNECT CALLBACK: INICIO!. Llamando a BLE WRITE..." + this.Value);

      this.ble.write(this.TargetDevice.id, this['service_id'],this['characteristic_id'], this.StringToBytes(this.Value) ).then(()=>{
        this.SetMessage("BLE WRITE CALLBACK: INICIO! Cambiando valor... " + this.Value);
        if(this.Value==this.constants.ON){
          this.Value = this.constants.OFF;
        } else {
          this.Value = this.constants.ON;
        }
        this.SetMessage("Nuevo valor... " + this.Value);

        this.SetMessage("Llamando a BLE DISCONNECT...");
        this.ble.disconnect(id);
        this.ejecutandoComando = false;
        this.appRef.tick();
      },(error)=>{
        this.SetMessage("BLE Write ERROR!");
        this.SetMessage(error);
        this.SetMessage("Llamando a BLE DISCONNECT...");
        this.ble.disconnect(id);
        this.ejecutandoComando = false;
        this.appRef.tick();
      });
    },error=>{
      this.SetMessage("BLE Connect ERROR!");
      this.SetMessage(error.message);
      this.ejecutandoComando = false;
      this.appRef.tick();
    });
  }

  /**
   * Agrega mensajes de ejecución al histórico
   */
  public SetMessage(message){
    var count = this['messages'].length;
    message = count + ':' + message;
    this['messages'].unshift(message);

    /*
      ESTA LINEA ES IMPORTANTE PARA REFRESCAR LA PANTALLA CUANDO SE GENERAN EVENTOS
      FUERA DEL CONTROL DE ANGULAR. POR EJEMPLO CUANDO SE LLAMA A UN CALLBACK EN
      UN PROCESO DE BLE SCAN.
    */
    this.appRef.tick();
  }

  // ASCII only. Convierte el valor a escribir a un arreglo de BYTES
  public StringToBytes(string) {
     let array = new Uint8Array(string.length);
     for (let i = 0, l = string.length; i < l; i++) {
         array[i] = string.charCodeAt(i);
      }
      return array.buffer;
  }

  // ASCII only
  public BytesToString(buffer) {
      return String.fromCharCode.apply(null, new Uint8Array(buffer));
  }
}

Mis primeros pasos con ionic

5 Jun , 2017,
Jose Nunez
, , , ,
No Comments

Bueno, como nos pasa a todos, un día desperté con esa imperiosa necesidad de crear una app para teléfonos inteligentes; y pues, comenzar no es fácil si tomamos en cuenta que las principales plataformas de desarrollo (IOS y Android) tienen sus costos y complejidades.

(!) Antes de seguir estas instrucciones asegúrese de entender las Condiciones de Uso de nuestro sitio.

Afortunadamente, mis compañeros del centro de innovación habían estado realizando una investigación sobre algo similar, y me hablaron del Ionic Framework, un sistema para desarrollar aplicaciones móviles usando tecnologías web (HTML, CSS, JS/TS, ANGULARJS) etc que luego pueden implementarse tanto en Android como IOS.

Dentro de las bondades de Ionic podemos listar las siguientes:

  1. Manejo de plantillas de proyecto predefinidas
  2. Acceso a características específicas de hardware (bluetooth, gps, camara, etc)
  3. Pre-visualizacion de las apps usando IonicView directamente en los dispositivos móviles tanto para Android como para IOS

Pero bueno, he querido comenzar con el pie derecho, configurando una estación de trabajo LINUX UBUNTU 16.04 así que aquí van algunos lineamientos básicos para comenzar:


#1 – NodeJS

Es necesario instalar nodeJS y npm. Para Ubuntuo 16.04 he encontrado esta guía que me ha parecido muy completa. En ella se discuten diferentes métodos. Yo elegí usar el método de nvm (node version manager) que se puede resumir en los siguientes comandos uno por uno.

Antes de iniciar vaya a https://nodejs.org/en/download/ y determine cual es la versión LTS (en nuestro caso es la 6.11)

cd
sudo apt-get update
sudo apt-get install build-essential libssl-dev
curl -sL https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh -o install_nvm.sh
bash install_nvm.sh
source ~/.profile
Opcionalmente: nvm ls-remote | grep Latest
nvm install  6.11
node -v
npm -v

#2 – Instalación de IONIC

Ahora, de acuerdo con la guía de inicio de IONIC procedemos con el siguiente comando (puede tardar muchos minutos):

npm install -g cordova ionic --verbose

Luego de unos minutos (como 15 con mi conexion 3G) y de mucho texto en la consola, podemos usar el comando ionic -v para determinar la versión que hemos instalado.

En este punto es importante crear una cuenta personal en https://apps.ionic.io/signup para poder visualizar las aplicaciones con Ionic View. Una vez creada la cuenta podemos usar el siguiente comando para que nuestra instalación de ionic quede conectada a nuestra cuenta.

ionic login

#3 Creación de un nuevo proyecto

(!) Antes de crear un nuevo proyecto, es importante asegurarse que su GIT local esté instalado y configurado.

Al momento de escribir este artículo, la guía de inicio hace mención de 3 tipos de proyecto: blank, tabs y sidemenu (aunque hay todo un mercado de tipos de proyecto en la red). La verdad nos gustó más el de tabs, así que usamos estos comandos:

cd
mkdir myIonicTests
cd myIonicTests
ionic start myApp tabs

Dado que ya habíamos hecho login en el paso anterior, podemos decirle que si (Y) a la pregunta de la terminal “Link this app to your Ionic Dashboard to use tools like Ionic View? (Y/n)“. Al decir que sí el sistema termina de crear el app y nos lleva al navegador de Internet a la página de ionic para crear nuevas apps para el visor. Más tarde será necesario utilizar el comando ionic link para conectar el código de nuestra app local con su correspondiente entrada en Ionic View.

(!) Notas: Algo importante de resaltar es que este template de una vez crea el proyecto como un repositorio GIT. Reconectar un repositorio GIT a nuestra cuenta GIT de preferencia es una tarea importante a  futuro, pero eso lo dejaremos para otro artículo.
Otra cosa importante es que el comando start de ionic necesitará una conexión a Internet estable para poder descargar todos los archivos, bibliotecas y dependencias necesarias.

En este punto ya podemos ejecutar el comando ionic serve para pre-visualizar el app en nuestro navegador local.


#4 Ejecutar la aplicación en un celular

Nótese que al crear la aplicación en el sitio web de ionic, se generó un código de identificación de la app. En la aplicación IonicViewer de Android o IOS se puede acceder con sus credenciales de ionic, o usando el código de identificación de la aplicación.

Antes de subir el código a Ionic View, necesitamos enlazarlo con nuestra nueva app; usando el comando ionic link. Se usan las flechas arriba y abajo del teclado para seleccionar el app.

Para subir el código más reciente utilizamos el comando ionic upload; luego en la aplicacion de celular Ionic View usamos la opción “Clear App Data” y luego “View App”

En una próxima entrada estaremos discutiendo cómo modificar el código.