Cordova/Ionic ont les dents bleus

Bonjour,

Objectifs

Dans le cadre de projets “persos”, je me suis intéressé à la techno NFC, mais pas à la techno Bluetooth. L’objectif du jour est de corriger cela en regardant particulièrement comment cela peut fonctionner avec Cordova ou Ionic … Le titre est en référence à cette vidéo. Comme d’habitude, cette séance de travail commence par une bonne phase de lecture dont les liens intéressants sont en bas de page.

Bluetooth Vs Bluetooth Low Energy

Un premier point de “friction” : la différence entre BlueTooth & Bluetooth Low Energy. Le nom semble identique, mais au regard des différentes documentations et lectures, il semble qu’il faille les considérer comme deux technologies différentes qui n’adressent pas les mêmes besoins :

  • Bluetooth : courte distance mais haut débit,
  • Bluetooth Low Energy : plus grande distance mais faible débit.

Une autre différence qui apparaît directement dans le nom, est la consommation d’énergie. BLE consomme moins et semble avoir été pensé pour l’IoT. Le hic c’est que comme indiquer ici les deux technologies ne sont pas compatibles. Certains appareils (comme les smartphones) supportent les deux.

La question : quel impact au niveau du développement ? Je ne sais pas encore, mais la documentation Android contient deux liens différents :

Un rapide regard semble montré que les choses sont assez similaires mais suffisamment différents pour que dans la documentation ionic2, il y ait deux plugins différents :

Pour info, quelques liens comme celui-ci ou celui-là, présentent bien la différence entre les deux technos.

Création du projet

La première étape va être de créer un projet Ionic2 tout propre en ajoutant simplement le plugin en mode Low Energy :

  • ionic start –v2 bluetooth tabs

  • ionic plugin add cordova-plugin-ble-central

  • ionic serve –address 0.0.0.0 (1)

  • ionic platform add android

  • ionic build android

  • ionic run android

Voilà. Ça marche : je peux tout casser maintenant.

Mode 1 : Low Energy avec le plugin proposé par Ionic

Plugin Par défaut

Par défaut, Ionic propose le plugin suivant avec une intégration. Même s’il en existe d’autres … autant prendre celui proposé.

Dispo

Avant de communiquer, il faut scanner mais il faut surtout que le bluetooth soit actif. Pour commencer un petit test :

let that = this;
that.bleEnabled = false;

ble.isEnabled(
    function() {
        console.log("Bluetooth is enabled");
        that.message = "Bluetooth is enabled";
        that.bleEnabled = true;
    },
    function() {
        console.log("Bluetooth is *not* enabled");
        that.message = "Bluetooth is *not* enabled";
        that.bleEnabled = false;
    }
);

Scan

Maintenant que je sais que le bluetooth est actif, il est possible de lancer un scan dont le but est de rechercher les devices présent autour de mon téléphone. Le plugin propose plusieurs méthodes :

  • scan : recherche, appel un call back dès qu’un appareil est trouvé et gère elle même le temps de recherche
  • startScan : idem mais ne gère pas le time out
  • … : startScan avec options …

A noter qu’il est possible de filtrer la recherche sur les services d’un élément bluetooth. Au niveau de la norme, des services ont été définis. Un élément qui déclare offrir ce service doit respecter une norme. Cela permet de d’assurer que des éléments peuvent discuter entre-eux. Par exemple : la gestion de la batterie.

Au niveau du code :

public start() {
    console.log('start');
    let that = this;
    that.bleScanning = true;
    that.message =  "Scanning";

    this.subScan = BLE.startScan([]).subscribe(device => {
        that.devices.push(device);
    });
}

Au niveau de l’interface, j’ai juste une liste avec les éléments affichés. Bon, comme j’ai trouvé qu’un seul composant LE dans mon bureau la liste est “courte”:

Service et caractéristiques

Par défaut, les informations retournées par le device sont relativement lègères. Pour avoir plus d’infos, il faut se connecter :

/**
 * Select a device = connecting
 */
public select(device) {
   this.selectedDevice = device;
   this.deviceData = [];

   this.subDevice = BLE.connect(device.id).subscribe(data => {
        this.deviceData = data;
        BLE.disconnect(device.id);
        this.subDevice.unsubscribe();
        },
        error => { console.log(error);  }
    );
}

Ce qui donne l’écran :

Donc la montre aurait comme service (d’après ici) :

  • 1800 : Generic Access,
  • 1801 : Generic Attribute,
  • 9b012401-bc30-ce9a-e111-0f67e491abde : ??? J’ai bien trouvé quelques infos mais il s’agit de Reverse-engineering. Un peu hors sujet :)

Chaque service est couplée à des caractéristiques :

Par contre pour les éléments internes de la montre … je sais pas :).

Lecture

Même si les données n’ont pas beaucoup de sens, un test de lecture :

/**
 * Read
 */
readCharacteristic(device, c) {
    this.subDevice = BLE.connect(device.id).subscribe(data => {
        BLE.read(device.id, c.service, c.characteristic)
          .then( (value) => { alert(value); alert(this.bytesToString(value)); BLE.disconnect(device.id); } )
          .catch( (reason) => { console.error(reason); BLE.disconnect(device.id); }
       );
   });
}

Pour la première cela marche nickel car la montre retourne bien de l’ASCII mais pour les autres cela donne pas de bons résultats car la conversion utilisée n’est pas la bonne.

Bilan

Un peu mitigé : j’ai réussi à mettre en place des choses mais j’ai l’impression que sans documentation précise du matériel cible c’est finalement assez compliqué. Après peut-être que la montre utilisée est un peu trop fermée … A voir …

Mode 2 : Classic

Pareil : le plugin par défaut

Il est là : ici.

Une première différence

Une première différence notable : pas de scan. D’après la documentation du plugin, la connexion doit d’abord être initié par le smartphone. Le point qui me gêne : Will not connect Android to Android. Alors après plusieurs tests : effectivement la connexion ne semble pas se faire. J’ai testé avec un IPhone, la connexion se fait mais rien de plus …

List

La liste est assez simple à obtenir :

BluetoothSerial
       .list()
           .then( (value) => { this.devices = value; console.log(this.devices); })
           .catch((reason) => {alert(reason); });

Comme indiqué et contrairement à l’autre principe : c’est le téléphone qui retourne la liste.

Connexion : Aie …

La connexion avec un autre téléphone ne fonctionne et c’est normal au regard de la documentation. Pour réussir à faire se connecter les deux téléphones, j’ai suivi les explications données ici. Le code contient un exemple d’application que j’ai installé sur les deux téléphones et la communication fonctionne. Par contre, pas moyen de se connecter à autre chose. Sur la base de l’exemple fourni, j’ai pu me connecter :

this.subConnect = BluetoothSerial.connect(this.connectedDevice.address)
 .subscribe(
 infos => { 
   this.messages.push('Connect : Connected');
   this.isConnected = true;
   this.send('conn');
}

et envoyer des messages entre les deux téléphones.

Réception

Pour la réception de message, il faut s’abonner à une fonction :

BluetoothSerial.subscribe('').subscribe(
    data => {
        //alert(data.substring(data.length,4));
        data.substring(0, data.length-4);
        this.messages.push('Received : ' + data.substring(0, data.length-4));

        if( data.substring(0, data.length-4) == 'conn') {
            this.isConnected = true;
        }
    }
);

Pour faire communiquer les deux applications, j’ai repris le “protocole” de communication. La réception du message dans mon application fonctionne bien par contre, je n’ai pas trouvé de moyen de savoir d’où venait le message … Ce qui est un peu compliqué quand même …

Bilan

Encore une fois, c’est peut-être un manque de connaissance de ma part dans le domaine (c’est même certainement cela) mais je suis pas trop convaincu par le fonctionnement proposé. Même avec l’application exemple, la communication est très limitée et je n’ai jamais réussi à me connecter à autre chose que les téléphones.

Autre plugin

Plus bas niveau ?

J’ai essayé un dernier plugin pour la route : ici. Le plugin semble plus bas niveau, il est limité à Android.


Suivre la doc :)

Alors pour le coup, la documentation des exemples de codes est très bien faîtes. Il suffit de suivre les exemples et cela (semble) marcher. Je dis semble car lister les devices, avoir les infos, mais le reste : difficile de savoir.

Connecter et après ?

Par exemple, voici le code de la connexion :

networking.bluetooth.connect(this.deviceInfo.address, uuid, function (socketId) {
    that.socketId = socketId;
    that.uuidSelected = uuid;
    alert('Connection success: ' + that.socketId);

    networking.bluetooth.listenUsingRfcomm(uuid, function (serverSocketId) {
        // Keep a handle to the serverSocketId so that you can later accept connections (onAccept) from this socket. 
        alert('listenUsingRfcomm OK');
        that.serverSocketId = serverSocketId;

    }, function (errorMessage) {
        alert('listenUsigRfcomm Error: ' + errorMessage);
    });

    networking.bluetooth.send(that.socketId, "conn"
        , (data) => { alert('Send after Connect : OK'); console.log('Send after Connect : OK'); console.log(data); }
        , (error) => { alert('Send after Connect : ERROR'); console.log('Send after Connect : OK'); console.log(error); }
    );

}, function (errorMessage) {
    alert('Connection failed: ' + errorMessage);
});

Après une connexion réussie, j’ai essayé de d’envoyer l’information de connexion dans l’espoir de communiquer avec l’application exemple déjà utilisée plus haut mais rien à faire. Pourtant, dans certaines connexions, le téléphone cible reçoit bien quelque choses car j’ai des demandes d’autorisation :

Par contre, j’ai bon répondre oui : je ne reçois rien … Pourtant les listeners semblent être OK : en tout, j’ai des succes dans les callback

Bilan

Pas mieux …

Bilan Général

Frustré et content :) :

  • Content : j’ai essayé, bidouillé, parfois réussi, souvent raté, continuer à apprendre Ionic & Angular, …
  • Frustré : je sens bien qu’il me manque quelques choses, des informations pour réussir à avancer.

Avec le recul, je me dis que plutôt de commencer par lire des choses sur la norme puis le développement, j’aurais du passer plus de temps sur l’espace entre la norme et le développement. Par exemple, à quoi correspondent les différents UUIDs ? comment échanger de l’information ? Sous quelle forme ? Mais ce sera pour un autre demain

Liens