Comment débuter avec des modules Yoctopuce sur Android

Comment débuter avec des modules Yoctopuce sur Android

L'article de cette semaine est consacré à Android. Nous avons déjà plusieurs fois réalisé des projets avec Android, mais cette semaine nous allons nous concentrer sur la partie software. Nous allons écrire une petite application qui affiche la valeur d'un capteur de température branché par USB.




Note: une version plus récente de cet article est disponible: Comment débuter avec la lib Yoctopuce pour Android en 2022.

Bien que cette article s'adresse aux débutants, nous partons du principe que vous avez déjà quelques notions de Java et de l'architecture Android. Si vous n'avez jamais écrit d'application Android, nous vous conseillons de commencer par le tutoriel de Google. De la même manière, si vous n'avez jamais utilisé de module Yoctopuce, il serait judicieux de commencer par les premiers articles de la série "Pour les débutants", plus particulièrement celui sur la structure logique des modules Yoctopuce.

Qu'est ce qui est supporté


La librairie Yoctopuce peut être utilisée sur n'importe quel téléphone ou tablette depuis Android 4.0. Toutefois, pour pouvoir accéder au port USB, il faut que le téléphone ou la tablette supporte le mode USB OTG (pour USB on the Go). Dans ce mode, c'est la tablette qui alimente le périphérique connecté. Bien que cette fonctionnalité soit de plus en plus répandue, un grand nombre de systèmes Android ne permettent pas de fonctionner dans ce mode. Il n'y a pas de moyen software de déterminer si votre système supporte ce mode. Parfois c'est indiqué dans les spécification du constructeur, si ce n'est pas le cas, il faut contacter le constructeur ou écumer les forums pour vérifier.

Créer le projet


Dans cet article, nous avons utilisé Android Studio 2.3.3. Si vous utilisez Éclipse ou d'autres éditeurs, certaines étapes seront différentes mais les principes et le code devraient rester proche.

Nous allons commencer par créer un nouveau projet qui contient une activité et un layout de base.

Nous partons du projet de base d'Android Studio
Nous partons du projet de base d'Android Studio



On en profite pour modifier le layout: On attribue l'ID temperature au champ texte et on change sa taille pour qu'il soit plus lisible.

...
<TextView
   android:id="@+id/temperature"
   android:text="Temperature"
   android:textSize="48sp"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   app:layout_constraintBottom_toBottomOf="parent"
   app:layout_constraintLeft_toLeftOf="parent"
   app:layout_constraintRight_toRightOf="parent"
   app:layout_constraintTop_toTopOf="parent"/>
...




Ajouter la librairie Yoctopuce


Avant de pouvoir accéder aux modules Yoctopuce, il faut ajouter la librairie Yoctopuce pour Android au projet. La méthode la plus simple consiste à ajouter une référence à la librairie dans le fichier build.gradle de l'application. Lors de la compilation, Android Studio va automatiquement télécharger et ajouter la dernière version de la librairie Yoctopuce.

...
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    compile 'com.android.support:design:25.3.1'
    compile 'com.yoctopuce.android:YoctoLib:+'
    testCompile 'junit:junit:4.12'
}



Modifier le fichier AndroidManifest.xml


Pour qu'une application Android soit autorisée à accéder au port USB, il faut que cet usage soit déclaré dans le manifeste. Pour ce faire, il faut ajouter la ligne <uses-feature android:name="android.hardware.usb.host"/> dans la section manifiest du fichier AndroidManifest.xml.

Il faut aussi ajouter un intent-filter "android.hardware.usb.action.USB_DEVICE_ATTACHED" pour que l'activité principale soit automatiquement démarrée lors du branchement d'un module sur le port USB. Cet intent-filter a besoin d'une section meta-data qui pointe sur un fichier xml qui liste les périphériques USB qui vont démarrer cette application.

Par défaut Android crée une nouvelle instance de l'activité chaque fois qu'un module Yoctopuce est détecté, ce qui complique passablement la gestion de l’initialisation de l'API. Pour éviter ce comportement, il suffit d'ajouter l'attribut android:launchMode="singleInstance" à l'activité qui doit être démarrée automatiquement.

Il faut donc modifier le fichier AndroidManifest.xml pour qu'il ressemble à ceci:

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.yoctopuce.myapplication"
       xmlns:android="http://schemas.android.com/apk/res/android">

  <uses-feature android:name="android.hardware.usb.host"/>
  <application
   android:allowBackup="true"
   android:icon="@mipmap/ic_launcher"
   android:label="@string/app_name"
   android:roundIcon="@mipmap/ic_launcher_round"
   android:supportsRtl="true"
   android:theme="@style/AppTheme">
    <activity
     android:name=".MainActivity"
     android:label="@string/app_name"
     android:launchMode="singleInstance"
     android:theme="@style/AppTheme.NoActionBar">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
      <intent-filter>
        <action
         android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
      </intent-filter>
      <meta-data
       android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
       android:resource="@xml/device_filter"/>
    </activity>
  </application>

</manifest>



Et créer un fichier device_filter.xml avec le contenu ci-dessous dans le répertoire /src/main/res/xml de votre projet.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-device vendor-id="9440" />
</resources>



Une fois ces modifications faites, Android va automatiquement proposer de lancer l'application quand un module Yoctopuce est branché sur le port USB du téléphone.

Lorsqu'un module Yoctopuce est branché, Android propose de démarrer automatiquement l'application
Lorsqu'un module Yoctopuce est branché, Android propose de démarrer automatiquement l'application




Initialiser l'API Yoctopuce


Il faut implémenter les méthodes onStart() et onStop() de l'activité pour démarrer et stopper la librairie.

Dans la méthode onStart(), il faut appeler la méthode YAPI.EnableUSBHost() et passer à cette fonction un objet qui hérite de la classe Context. Nous pouvons utiliser directement l'Activité courante car la classe Activity hérite de la classe Context.

Il faut ensuite appeler la méthode RegisterHub("usb") pour démarrer la détection des modules sur le port USB. Finalement, on programme l’exécution différée de l'objet _periodicUpdate qui sera détaillé plus bas.

@Override
protected void onStart()
{
    super.onStart();
    try {
        YAPI.EnableUSBHost(this);
        YAPI.RegisterHub("usb");
    } catch (YAPI_Exception e) {
        Snackbar.make(_temperatureTextView,
            "Error:" + e.getLocalizedMessage(),
            Snackbar.LENGTH_INDEFINITE).show();
    }
     _handler.postDelayed(_periodicUpdate, 100);
}




Dans la méthode onStop(), il faut supprimer les exécutions programmées de l'objet _periodicUpdate et appeler la méthode YAPI.FreeAPI() pour stopper la librairie et libérer les ressources qui étaient verrouillées par la librairie.

@Override
protected void onStop()
{
    _handler.removeCallbacks(_periodicUpdate);
    YAPI.FreeAPI();
    super.onStop();
}



Mettre à jour l'interface


Pour mettre à jour l'interface, nous avons besoin de garder une référence sur le champ texte de notre layout et nous avons aussi besoin d'un Handler qui va nous permettre d'appeler périodiquement un objet Runnable.

private TextView _temperatureTextView;
private Handler _handler;

@Override
protected void onCreate(Bundle savedInstanceState)
{
   ...
    _temperatureTextView = (TextView) findViewById(R.id.temperature);
    _handler = new Handler();
}



Maintenant que tout est en place, il ne reste plus qu'à écrire l'objet _periodicUpdate. La méthode run() de cet objet doit:

  1. Appeler YAPI.UpdateDeviceList une fois toutes les trois secondes, ce qui force l'API a énumérer les modules Yoctopuce. C'est un processus assez lourd et il faut éviter de le faire trop souvent.
  2. Si aucun capteur n'est déjà utilisé, rechercher le premier capteur de température détecté à l'aide de la méthode YTemperature.FirstTemperature().
  3. Mettre à jour le champ texte avec la valeur courante du capteur de température. Dans un premier temps, on vérifie que le module est toujours connecté à l'aide de la méthode isOnline(), et si c'est le cas on récupère la température courante avec get_currentValue().
  4. Programmer la prochaine exécution de cette méthode à l'aide le la méthode postDelayed.


Ce qui nous donne le code suivant:

private double _hardwaredetect;
private YTemperature _sensor;

private Runnable _periodicUpdate = new Runnable()
{
  @Override
  public void run()
  {
    try {
      if (_hardwaredetect == 0) {
        YAPI.UpdateDeviceList();
      }
      _hardwaredetect = (_hardwaredetect + 1) % 6;
      if (_sensor == null) {
        _sensor = YTemperature.FirstTemperature();
      }
      if (_sensor != null && _sensor.isOnline()) {
        final String text = String.format(Locale.US, "%.2f %s",
            _sensor.get_currentValue(), _sensor.get_unit());
        _temperatureTextView.setText(text);
      } else {
        _temperatureTextView.setText("OFFLINE");
        _sensor = null;
      }
    } catch (YAPI_Exception e) {
      Snackbar.make(_temperatureTextView,
          "Error:" + e.getLocalizedMessage(),
          Snackbar.LENGTH_INDEFINITE).show();
    }
    _handler.postDelayed(_periodicUpdate, 500);
  }
};



Une fois toutes ces modifications faites, l'application peut être compilée et utilisée sur n'importe quel téléphone ou tablette qui a le support USB on the Go.

Et oui, il fait chaud dans les bureaux de Yoctopuce :-)
Et oui, il fait chaud dans les bureaux de Yoctopuce :-)



Le code source de cet application est disponible à cette adresse: https://github.com/yoctopuce-examples/android_template.

Quelques remarques


Nous avons volontairement gardé cet exemple très basique afin de ne pas rendre cet article trop long et permettre aux débutants de démarrer facilement, mais pour une application plus complète il est préférable de ne pas utiliser la librairie Yoctopuce depuis le thread principal. Comme nous l'avons expliqué en détails dans cet article, il est plus efficace de déporter le code qui utilise la librairie dans un thread qui s’exécute en arrière plan. C'est même une obligation si vous devez utiliser un module Yoctopuce qui est connecté à un YoctoHub. Malheureusement, c'est aussi beaucoup plus compliqué mais vous pouvez vous inspirer de la solution que nous avons utilisée dans ce même article.

La plupart des téléphones n'ont qu'un seul port USB, du coup il n'est pas possible de débugger l'application par USB quand le module Yoctopuce est branché. Heureusement, il existe une astuce peu connue: Débugger l'application par le réseau WiFi. La procédure est expliquée sur cette page.

De tous les OS que nous supportons, Android est clairement le plus difficile à appréhender. La taille limitée de l'écran, les différentes versions d'Android, les ressources limitées, la gestion des différents états de l’application et sa difficulté à débugger rendent le développement d'une application long et complexe. Prévoyez tout de suite un moyen pour afficher et exporter les logs de debug, cela vous facilitera la vie le jour où vous serez en pleine nature et l'application ne fonctionnera plus. C'est du vécu :-)

Commenter aucun commentaire Retour au blog












Yoctopuce, get your stuff connected.