Application Android pour Raspberry Pi
Dans nos précédents tutoriels sur l’IoT, nous avons vu comment configurer un Raspberry Pi et comment le connecter à Parse Server en utilisant l’ API Back4App pour sauvegarder des objets sur le serveur et pour effectuer des requêtes et des requêtes en direct.
Maintenant, nous couvrons comment reproduire tout ce qui a été fait du côté du Raspberry vers le côté de l’application. Dans ce tutoriel, nous décrivons une application Android pour interagir avec l’appareil IoT configuré précédemment. Du côté du serveur Parse, l’application effectue les mêmes tâches que le Raspberry : écriture d’objets et exécution de requêtes et de requêtes en direct. Veuillez noter que ces fonctionnalités peuvent être utiles même si vous ne prévoyez pas de développer une application IoT !
Parse s’est avéré être un cadre extraordinaire pour créer des applications IoT. En 2020, avec l’ajout du protocole API GraphQL, il offre un moyen encore meilleur de récupérer des données.
Nous fournissons nos codes comme première étape pour que vous puissiez développer les applications que vous souhaitez.
Contents
- 0.1 Conditions préalables
- 0.2 Section 1 : Bases de la création de votre application et connexion avec Back4App
- 0.3 Section 2 : Sauvegarde et récupération d’objets dans Parse et affichage dans l’application
- 0.4 Section 3 : Écouter les événements en temps réel à l’aide de Live Query et les afficher dans l’application
- 1 Comment interagir une application Android avec un appareil IoT ?
Conditions préalables
La seule condition préalable est de suivre notre tutoriel Android QuickStart. Pour cela, vous devez cliquer sur le lien mentionné ci-dessous.
https://www.back4app.com/docs/pages/android/how-to-build-an-android-app-on-back4app
Voici un tutoriel sur LiveQuery qui sera nécessaire dans la section 3 de ce tutoriel IoT Series. Il n’est cependant pas nécessaire de le terminer au préalable.
https://docs.back4app.com/docs/android/live-query/
Si vous avez des questions tout au long du tutoriel, vous pouvez les résoudre en suivant le guide officiel Parse pour Android dans le lien ci-dessous.
http://docs.parseplatform.org/android/guide/
Section 1 : Bases de la création de votre application et connexion avec Back4App
Dans ce projet, nous nommons notre classe “MainActivity.java”. Voici le code de base qui vous aidera à démarrer.
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) ; setContentView(R.layout.activity_main) ;
// Insérer une barre d'outils dans l'application. Une description plus détaillée de cette barre se trouve dans le fichier menu_main_activity.xml Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar) ; setSupportActionBar(toolbar) ; }
@Override public boolean onCreateOptionsMenu(Menu menu) { // Gonfle le menu ; cela ajoute des éléments à la barre d'action si elle est présente. getMenuInflater().inflate(R.menu.menu_main_activity, menu) ; return true; }
@Override public boolean onOptionsItemSelected(MenuItem item) { // Gérer les clics sur les éléments de la barre d'action ici. La barre d'action // automatiquement les clics sur le bouton Home/Up, tant que vous // que vous spécifiez une activité parente dans AndroidManifest.xml. int id = item.getItemId() ;
//noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; }
return super.onOptionsItemSelected(item) ; } }
Vous devrez peut-être ajouter les importations suivantes :
import android.os.Bundle ; import android.support.design.widget.FloatingActionButton ; import android.support.design.widget.Snackbar ; import android.support.v7.app.AppCompatActivity ; import android.support.v7.widget.Toolbar ; import android.view.View ; import android.view.Menu ; import android.view.MenuItem ; import android.widget.EditText ; import android.widget.Button ; import android.widget.ToggleButton ; import android.view.View.OnClickListener ;
Dans votre fichier XML de mise en page, assurez-vous d’ajouter le code suivant :
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/myCoordinatorLayout" tools:context="com.example.guilherme.myiotapplication.MainActivity">
<includelayout="@layout/content_main_activity" />
<android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
Pour connecter votre application à Back4App, ajoutez le code suivant dans la fonction onCreate() de votre classe.
// Initialisation du serveur Parse Parse.initialize(new Parse.Configuration.Builder(this) .applicationId("YOUR_APP_ID") // à partir de Core Settings, sur "Features" (caractéristiques) .clientKey("YOUR_CLIENT_KEY") // from Core Settings, on "Features" .server("https://parseapi.back4app.com/") .build() ) ;
N’oubliez pas de suivre les instructions étape par étape de QuickStart pour accorder l’accès à Internet à votre application.
Section 2 : Sauvegarde et récupération d’objets dans Parse et affichage dans l’application
Commencez par ajouter les importations ci-dessous
import com.parse.FindCallback ; import com.parse.Parse ; import com.parse.ParseException ; import com.parse.ParseObject ; import com.parse.ParseQuery ; import com.parse.SaveCallback ; import org.json.JSONException ; import org.json.JSONObject ; import java.util.List ;
Dans cette section, nous sauvegardons les objets de la classe “CommandGPIO1” en cliquant sur un bouton. Chaque Parse Object possède trois attributs par défaut : objectId, createdAt et updatedAt. Notre classe aura deux attributs supplémentaires : content et destination. Les valeurs pour le contenu seront soit “on”, soit “off”, et représenteront un état à envoyer à une DEL. Pour la destination, tous les objets contiendront “command”, mais nous pourrions définir des chaînes de caractères différentes pour les différentes DEL.
Par exemple, nous créons deux boutons :
Nous créons deux boutons, et chacun d’entre eux écrit une valeur différente sur le contenu. Pour créer un bouton, il suffit d’ajouter le code suivant à votre fichier XML de mise en page :
<Button android:id="@+id/buttonSendOn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|start" android:layout_marginBottom="0dp" android:layout_marginTop="120dp" android:layout_marginEnd="0dp" android:layout_marginRight="0dp" android:layout_marginLeft="16dp" android:layout_marginStart="16dp" android:text="Envoyer ON"/>
Dans votre fichier de classe, la création du bouton est aussi simple que de l’écrire :
Button buttonOn = (Button) findViewById(R.id.buttonSendOn) ;
Si vous êtes un débutant en programmation Android, veuillez noter que l’argument “findViewById” contient “buttonSendOn”, et qu’il s’agit de l’Android:Id que nous avons défini dans le XML.
Nous voulons que ce bouton enregistre un objet sur Parse Server lorsqu’il est cliqué. Pour ce faire, ajoutez le code suivant :
buttonOn.setOnClickListener( new OnClickListener(){ @Override public void onClick(final View view) {
// Création d'un nouvel objet et attribution des attributs appropriés ParseObject command = new ParseObject("CommandGPIO1") ;
command.put("content","on") ; command.put("destination", "command") ; command.saveInBackground(new SaveCallback(){ @Override public void done(ParseException e){ Snackbar.make(view, "Sent ON to Output", Snackbar.LENGTH_LONG ) .setAction("Action", null).show() ; } }) ; }) ;
Notez que nous pouvons ajouter n’importe quelle fonction que nous voulons exécuter dans la fonction de rappel onClick. Ici, nous créons un objet de classe “CommandGPIO1”, définissons le contenu comme “on” et la destination comme “command”.
Il est utile de savoir que nous n’avons PAS besoin de définir cette classe au préalable sur le tableau de bord de Parse ! Si vous décidez plus tard d’ajouter un nouvel attribut, ou de changer le nom de votre classe, vous n’avez qu’à travailler sur votre code, et les changements seront automatiquement mis à jour sur le tableau de bord.
A ce stade, il est préférable de tester votre application et de vérifier que l’objet est bien créé !
Le SaveCallback affiche un snackbar, qui est un feedback léger en bas de votre écran, comme le montre la figure ci-dessus.
Copiez ce code pour créer un bouton qui écrit des objets avec “off”. Modifiez les lignes suivantes dans le fichier XML de présentation
android:id="@+id/buttonSendOff" android:layout_gravity="top|center" android:layout_marginLeft="0dp" android:layout_marginStart="0dp" android:text="Envoyer OFF"/>
Créez un nouveau bouton et modifiez la ligne où le contenu est écrit :
Bouton buttonOff = (Button) findViewById(R.id.buttonSendOff) ;
command.put("content","off") ;
Maintenant que les choses fonctionnent correctement sur Parse Server, nous voulons fournir un feedback permanent à l’utilisateur de l’application. Pour cela, nous allons utiliser l’élément EditText, comme indiqué ci-dessous.
Définissez cet élément dans le fichier XML de présentation :
<EditText android:id="@+id/status1Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|start" android:maxLines = "2" android:ems="10" android:singleLine="false" app:layout_behavior="@string/appbar_scrolling_view_behavior" />
Dans la portée onCreate() de votre classe, ajoutez le code suivant :
// Définition d'un élément de texte pour afficher l'état de la broche de sortie, qui reçoit des commandes de l'application // Les commandes sont envoyées par des boutons, qui sont décrits plus loin dans ce code. final EditText textOutPin = (EditText) findViewById(R.id.status1Text) ; textOutPin.setFocusable(false) ; textOutPin.setClickable(true) ; textOutPin.setText("Chargement de l'état de la broche de sortie...") ;
La première ligne crée l’objet. La deuxième ligne le rend non modifiable pour les utilisateurs. La troisième ligne le rend cliquable, de sorte qu’il puisse être copié et collé. La quatrième ligne définit le texte initial.
Nous voulons écrire le champ de contenu du dernier objet “CommandGPIO1” sauvegardé sur Parse dans cet objet EditText. Bien que nous puissions le faire en utilisant une certaine logique dans le code, nous allons en fait récupérer des objets de Parse en effectuant une ParseQuery, car cela fournit des résultats plus réalistes et plus robustes.
Le code ci-dessous déclare, définit les paramètres et imprime les résultats d’une requête Parse.
ParseQuery<ParseObject> queryOut = ParseQuery.getQuery("CommandGPIO1") ; queryOut.whereEqualTo("destination", "commande") ; queryOut.addDescendingOrder("createdAt") ; queryOut.setLimit(1) ;
queryOut.findInBackground(new FindCallback<ParseObject>() { @Override public void done(List<ParseObject> objects, ParseException e) { if (e == null){ textOutPin.setText("La sortie est " + objects.get(0).getString("content")) ; } else{ textOutPin.setText("Erreur : " + e.getMessage()) ; } } }) ;
La première ligne crée la requête. La deuxième ajoute une contrainte pour sélectionner uniquement les objets dont le champ de destination contient “command”. La troisième ligne sélectionne les objets en commençant par les plus récents. La quatrième ligne limite les résultats à un seul, ce qui garantit la récupération de l’objet le plus récent.
La cinquième ligne active la requête et appelle la fonction de rappel lorsqu’elle est terminée. Ici, nous écrivons le contenu de l’objet récupéré dans l’objet EditText défini précédemment.
Nous ajouterons ce morceau de code juste après la déclaration de l’objet EditText, afin qu’une requête soit effectuée à l’ouverture de l’application, et également dans la fonction SaveCallback lors de l’enregistrement d’un nouvel objet sur Parse, afin de mettre à jour le texte automatiquement lors de la création de nouveaux objets.
À ce stade, votre application devrait fonctionner comme illustré dans les captures d’écran suivantes.
Enfin, nous ajoutons un bouton d’actualisation pour permettre aux utilisateurs d’effectuer la requête ci-dessus quand ils le souhaitent. Cela sera fait avec un style de bouton différent, c’est-à-dire un bouton d’action flottant.
Ajoutez ce code au fichier XML de présentation :
<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|end" android:layout_marginBottom="0dp" android:layout_marginTop="120dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:layout_marginLeft="0dp" android:layout_marginStart="0dp" app:srcCompat="@android:drawable/ic_popup_sync" />
Maintenant, dans la portée onCreate() de la classe, ajoutez le code suivant :
// Bouton de rafraîchissement pour obtenir l'état de la sortie à la demande. // La même requête que la première est effectuée ici FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab) ; fab.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(final View view){ ParseQuery<ParseObject> queryOut = ParseQuery.getQuery("CommandGPIO1") ; queryOut.whereEqualTo("destination", "commande") ; queryOut.addDescendingOrder("createdAt") ; queryOut.setLimit(1) ; queryOut.findInBackground(new FindCallback<ParseObject>() { @Override public void done(List<ParseObject> objects, ParseException e) { if (e == null){ textOutPin.setText("La sortie est " + objects.get(0).getString("content")) ; } else{ textOutPin.setText("Erreur : " + e.getMessage()) ; } Snackbar.make(view, "Updated status of Output", Snackbar.LENGTH_LONG ).setAction("Action", null).show() ; } }) ; } }) ;
À ce stade, nous pouvons maintenant enregistrer des objets sur Parse Server, récupérer des informations à partir de ces objets et les afficher dans votre application ! Votre application devrait fonctionner comme le montre la figure ci-dessous :
Section 3 : Écouter les événements en temps réel à l’aide de Live Query et les afficher dans l’application
Dans cette section, nous surveillons en temps réel la classe “InputGPIO” dans le Parse Dashboard, et affichons le “contenu” des objets pour l’utilisateur.
Commencez par définir un nouvel EditText dans le XML de présentation :
<EditText android:id="@+id/status2Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center|start" android:layout_marginBottom="0dp" android:maxLines = "2" android:ems="10" android:singleLine="false" app:layout_behavior="@string/appbar_scrolling_view_behavior" />
Dans la portée de onCreate(), créez un nouvel objet EditText et effectuez une requête d’initialisation (pas encore en ligne) immédiatement après.
final EditText textInPin = (EditText) findViewById(R.id.status2Text) ; textInPin.setFocusable(false) ; textInPin.setClickable(true) ; textInPin.setText("Chargement de l'état de la broche d'entrée...") ;
// Requête initiale (non en direct) pour obtenir le dernier état stocké de l'épingle ParseQuery<ParseObject> queryIn = ParseQuery.getQuery("InputGPIO") ; queryIn.whereEqualTo("type", "interrupt") ; queryIn.addDescendingOrder("createdAt") ; queryIn.setLimit(1) ;
queryIn.findInBackground(new FindCallback<ParseObject>() { @Override public void done(List<ParseObject> objects, ParseException e) { if (e == null){ textInPin.setText("L'entrée est " + objects.get(0).getString("content")) ; } else{ textInPin.setText("Erreur : " + e.getMessage()) ; } } }) ;
Notez que les attributs de “InputGPIO” sont le contenu et le type, ce dernier jouant le rôle de destination sur la classe “CommandGPIO”.
À ce stade, votre application devrait ressembler à la figure ci-dessous.
Nous allons maintenant implémenter la requête Live. Le tutoriel mentionné précédemment est nécessaire ici.
https://docs.back4app.com/docs/android/live-query/
Assurez-vous de suivre l’étape 1 de notre tutoriel Live Query pour sélectionner les classes dans lesquelles Live Query sera activé et définir un nom de sous-domaine. Il se peut que vous deviez créer la classe “InputGPIO” (si elle n’existe pas déjà) pour activer la fonctionnalité sur cette classe. Suivez l’étape 2 du tutoriel pour configurer le client Live Query sur Android.
N’oubliez pas d’ajouter les importations suivantes :
import tgio.parselivequery.BaseQuery ; import tgio.parselivequery.LiveQueryClient ; import tgio.parselivequery.LiveQueryEvent ; import tgio.parselivequery.Subscription ; import tgio.parselivequery.interfaces.OnListener ;
Ajoutez le code suivant pour initialiser LiveQuery et définir ses paramètres.
// Initialisation de LiveQuery LiveQueryClient.init("wss:YOUR_SUBDOMAIN_NAME.back4app.io", "YOUR_APP_ID", true ) ; LiveQueryClient.connect() ;
// Définition des attributs de LiveQuery Subscription subIn = new BaseQuery.Builder("InputGPIO") .where("type","interrupt") .addField("content") .build() .subscribe() ;
Lors de la définition de l’abonnement, nous ne prenons que les éléments dont l’attribut type est “interrupt” et nous récupérons le contenu du champ.
Maintenant, ajoutez le code suivant pour répondre chaque fois qu’un objet défini par l’abonnement est créé au niveau de Parse Server.
// Commencer à écouter les événements CREATE de LiveQuery, récupérer son contenu et écrire subIn.on(LiveQueryEvent.CREATE, new OnListener() { @Override public void on(JSONObject object) { try { final String subInContent = (String) ((JSONObject) object.get("object")).get("content") ; runOnUiThread(new Runnable() { @Override public void run() { textInPin.setText("L'entrée est " + subInContent) ; Snackbar.make(findViewById(R.id.myCoordinatorLayout), "Input pin was changed to " + subInContent.toUpperCase(), Snackbar.LENGTH_LONG ).setAction("Action", null).show() ; } }) ; } catch (JSONException e){ e.printStackTrace() ; } } }) ;
Le champ de contenu de l’objet sera affiché dans notre nouvel élément EditText.
Ce que nous avons fait jusqu’à présent est suffisant pour afficher toute entrée envoyée par l’appareil IoT. Cependant, pour vérifier le fonctionnement de l’application, nous allons implémenter un autre type de bouton, c’est-à-dire un bouton à bascule qui créera un objet de classe “InputGPIO” et le sauvegardera sur le serveur Parse.
Ajoutez le code suivant au fichier XML de présentation :
<ToggleButton android:id="@+id/toggleTest" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center|end" android:layout_marginBottom="0dp" android:layout_marginTop="100dp" android:layout_marginEnd="0dp" android:layout_marginRight="0dp" android:layout_marginLeft="0dp" android:layout_marginStart="0dp" android:textOn = "entrée activée" android:textOff = "input off"/>
Ajoutez le code mentionné ci-dessous dans la portée de la fonction onCreate(), dans la classe MainActivity.
// Le bouton Toggle est ici juste pour émuler les objets que le matériel créerait ToggleButton toggleTest = (ToggleButton) findViewById(R.id.toggleTest) ; toggleTest.setOnClickListener( new OnClickListener(){ @Override public void onClick(final View view) { ParseObject command = new ParseObject("InputGPIO") ; if(gpioStatusTest.equals("off"))) gpioStatusTest = "on"; sinon gpioStatusTest = "off";
command.put("type","interrupt") ; command.put("content", "From Toggle : " + gpioStatusTest) ; command.saveInBackground(new SaveCallback(){ @Override public void done(ParseException e){ Snackbar.make(view, "Changed input pin", Snackbar.LENGTH_LONG ).setAction("Action", null).show() ; } }) ; } }) ;
Déclarez également la chaîne dans la portée MainActivity.
Chaîne gpioStatusTest = "off";
Nous avons terminé l’application ! À la fin de ce tutoriel, votre application devrait ressembler à la figure ci-dessous.
Si vous souhaitez continuer à développer notre application IoT, nous vous invitons à lire notre série IoT, un guide étape par étape, qui vous enseigne les bases de la configuration d’un Raspberry Pi et des applications spécifiques, telles que la récupération et l’enregistrement d’objets à partir de Parse Server à l’aide de JavaScript.
Nos codes sont disponibles sur le lien suivant :
https://github.com/back4app/iot-raspberry-node
Références
Connexion du Raspberry Pi à Parse Server
Comment interagir une application Android avec un appareil IoT ?
Section 1 : Notions de base pour créer votre application et vous connecter au serveur.
Section 2 : Enregistrement et récupération d’objets lors de l’Parse et affichage dans l’application.
Section 3 : Écoute d’événements en temps réel via une requête en direct et affichage dans l’application.