Comment débuter avec la lib Yoctopuce pour Android en 2022

Comment débuter avec la lib Yoctopuce pour Android en 2022

Cette semaine, nous mettons à jour notre tutoriel sur Android. Avec les différentes mises à jour Android, et plus particulièrement du SDK, certaines choses n'étaient plus à jour. Comme la dernière fois nous allons écrire une petite application qui affiche la valeur d'un capteur de température branché par USB.




Bien que cet 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 ce représente la quasi-totalité du marché Android. 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écifications 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 version 2021.1.1. 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 inclure la librairie Yoctopuce pour Android au projet. La méthode la plus simple est d'ajouter une référence à notre librairie dans la section dependencies du fichier fichier build.gradle. Lors de la compilation, Android Studio va automatiquement télécharger et inclure la librairie Yoctopuce.

...

dependencies {

    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    implementation 'androidx.navigation:navigation-fragment:2.4.1'
    implementation 'androidx.navigation:navigation-ui:2.4.1'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation 'com.yoctopuce.android:YoctoLib:46606'
}



Il est recommandé de spécifier le numéro de version exacte de la librairie à utiliser, cela assure que toutes les machines qui compilent l'application utilisent la même version de notre librairie. Notez que l'inspecteur d'Android Studio affiche si une nouvelle version de notre librairie est disponible.


Android Studio avertit si une nouvelle version de la librairie est disponible


Attention, depuis la fermeture de JCenter il faut utiliser le repository mavenCentral. Google à mis à jour les exemples et les projets par défaut d'Android Studio, mais si vous partez d'un ancien projet assurez vous que mavenCentral est présent dans la liste des repostories:

Extrait du fichier build.gradle:

 repositories {
        google()
        mavenCentral()
    }




Notez que si vous ne voulez pas dépendre d'un package manager, il est possible d'ajouter manuellement les fichiers de notre librairie à votre projet. Il suffit de copier le contenu du sous-répertoire YoctoLib de notre librairie dans votre projet.

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 est aussi possible d'ajouter un intent-filter nommé "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() du fragment 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.

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
public void onStart()
{
  super.onStart();
  try {
      YAPI.EnableUSBHost(getContext());
      YAPI.RegisterHub("usb");
  } catch (YAPI_Exception e) {
      Snackbar.make(binding.temperature,
              "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
public 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 FragmentFirstBinding binding;
private Handler _handler;

@Override
public View onCreateView(
        LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState)
{
  _handler = new Handler();
  binding = FragmentFirstBinding.inflate(inflater, container, false);
  return binding.getRoot();

}



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());
          binding.temperature.setText(text);
      } else {
          binding.temperature.setText("OFFLINE");
          _sensor = null;
      }
    } catch (YAPI_Exception e) {
      Snackbar.make(binding.temperature,
              "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.

l'application démo de Google modifiée pour utiliser un module Yoctopuce
l'application démo de Google modifiée pour utiliser un module Yoctopuce



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

Kotlin vs Java


Dans cet article nous avons utilisé le langage Java mais il est aussi possible d'utiliser Kotlin. L’utilisation de la librairie Yoctopuce est identique. Le seul piège est la gestion des exceptions. Le compilateur Kotlin ne vérifie pas si les Checked Exceptions (aussi appelées "exceptions explicites" en français) sont traitées par l'appelant. Il est donc plus facile d'oublier de gérer les erreurs, comme une déconnexion d'un module, lors de l'utilisation de notre librairie. Pour info, nous avons un article dédié à l'utilisation de notre librairie avec Kotlin.

Débugger par WiFi


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.

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.

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.