Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Mise en oeuvre d'une télécommande bluetooth

Accès rapide :
Présentation du véhicule à commander
Création d'un projet Android Studio
Mise en oeuvre de l'IHM de la télécommande
Le code de votre activité

Présentation du véhicule à commander

Nous allons dans ce tutoriel voir comment coder une télécommande permettant de contrôler un périphérique bluetooth. Histoire de rendre ce tutoriel ludique, je vous propose de travailler avec un petit véhicule développé à partir de briques de construction et de matériel Arduino. Les spécifications de ce véhicule peuvent être consultées sur cette page : la LegoduinoCar.

Ce véhicule embarque un module bluetooth de type HC-05.

Création d'un projet Android Studio

Nous allons commencer par produire un nouveau projet via Android Studio. Personnellement, j'ai appelé ce projet « LegoduinoCarRemote ». Nous allons y réaliser des communications bluetooth, il est donc nécessaire d'y ajouter la permission associée. Voici le contenu du fichier de manifeste de l'application après ajout de l'autorisation.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="fr.koor.legoduinoremote">

    <uses-permission android:name="android.permission.BLUETOOTH" />

    <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:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Le fichier de manifeste de l'application
j'ai aussi bloqué la rotation de l'activité principale de l'application en ajoutant l'attribut screenOrientation="portrait" en ligne 15.

Mise en oeuvre de l'IHM de la télécommande

Nous allons maintenant réaliser l'interface graphique de la télécommande. J'ai volontairement simplifié au maximum les choses : l'interface sera fonctionnelle. Voici une capture d'écran qui montre cette interface graphique dans Android Studio.

à vous de poursuivre son développement pour la rendre superbe, du point de vue du style. Je lance un petit concours à qui postera la plus belle IHM dans les commentaires Facebook en bas de la page ;-). A gagner : « toutes mes félicitations ».

Voici un exemple de définition XML du layout de notre activité.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
 76 
 77 
 78 
 79 
 80 
 81 
 82 
 83 
 84 
 85 
 86 
 87 
 88 
 89 
 90 
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/deviceList"
        android:layout_width="395dp"
        android:layout_height="180dp"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/lblConnectedDevice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:textAppearance="@android:style/TextAppearance.Large"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="Connected device is ..." />

    <Button
        android:id="@+id/btnForward"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="12dp"
        android:layout_marginEnd="8dp"
        android:text="Forward"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/lblConnectedDevice" />

    <Button
        android:id="@+id/btnStop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="4dp"
        android:layout_marginEnd="8dp"
        android:text="Stop"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btnForward" />

    <Button
        android:id="@+id/btnBackward"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:text="Backward"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btnStop" />

    <Button
        android:id="@+id/btnLeft"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="104dp"
        android:layout_marginEnd="16dp"
        android:text="Left"
        app:layout_constraintEnd_toStartOf="@+id/btnStop"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnRight"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="24dp"
        android:layout_marginTop="104dp"
        android:text="Right"
        app:layout_constraintStart_toEndOf="@+id/btnStop"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
Le layout de notre activité

Le code de votre activité

Et voici maintenant le code de l'activité permettant de communiquer par bluetooth.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
 76 
 77 
 78 
 79 
 80 
 81 
 82 
 83 
 84 
 85 
 86 
 87 
 88 
 89 
 90 
 91 
 92 
 93 
 94 
 95 
 96 
 97 
 98 
 99 
 100 
 101 
 102 
 103 
 104 
 105 
 106 
 107 
 108 
 109 
 110 
 111 
 112 
 113 
 114 
 115 
 116 
 117 
 118 
 119 
 120 
 121 
 122 
 123 
 124 
 125 
 126 
 127 
 128 
 129 
 130 
 131 
 132 
 133 
 134 
 135 
 136 
 137 
 138 
 139 
 140 
 141 
 142 
 143 
 144 
 145 
 146 
 147 
 148 
 149 
 150 
 151 
 152 
 153 
 154 
 155 
 156 
 157 
 158 
 159 
 160 
package fr.koor.legoduinoremote;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;


public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_ENABLE_BT = 456;

    private BluetoothAdapter bluetoothAdapter = null;
    private BluetoothClient bluetoothClient = null;
    private List<BluetoothDevice> knownDevices = null;

    private TextView lblConnectedDevice;

    private Button btnForward;
    private Button btnStop;
    private Button btnBackward;
    private Button btnLeft;
    private Button btnRight;

    private ListView deviceList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lblConnectedDevice = findViewById( R.id.lblConnectedDevice);

        btnForward = findViewById( R.id.btnForward );
        btnForward.setOnClickListener( buttonsListener );
        btnStop = findViewById( R.id.btnStop );
        btnStop.setOnClickListener( buttonsListener );
        btnBackward = findViewById( R.id.btnBackward );
        btnBackward.setOnClickListener( buttonsListener );
        btnLeft = findViewById( R.id.btnLeft );
        btnLeft.setOnClickListener( buttonsListener );
        btnRight = findViewById( R.id.btnRight );
        btnRight.setOnClickListener( buttonsListener );

        deviceList = findViewById( R.id.deviceList);

        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if ( ! bluetoothAdapter.isEnabled() ) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

        knownDevices = new ArrayList<>( bluetoothAdapter.getBondedDevices() );
        ArrayAdapter<BluetoothDevice> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, knownDevices);
        deviceList.setAdapter(adapter);
        deviceList.setOnItemClickListener( deviceListListener );
    }

    private ListView.OnItemClickListener deviceListListener = new ListView.OnItemClickListener() {
        @Override public void onItemClick(AdapterView<?> adapter, View view, int arg2, long rowId) {
            BluetoothDevice device = knownDevices.get( (int) rowId );
            bluetoothClient = new BluetoothClient( device );
            lblConnectedDevice.setText( "Connected to " + device.getName() );
        }
    };

    private View.OnClickListener buttonsListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if ( bluetoothClient == null ) return;
            char code = 's';
            switch( view.getId() ) {
                case R.id.btnForward: code = 'a'; break;
                case R.id.btnBackward: code = 'r'; break;
                case R.id.btnStop: code = 's'; break;
                case R.id.btnLeft: code = 'g'; break;
                case R.id.btnRight: code = 'd'; break;
            }
            bluetoothClient.writeChar( code );
        }
    };

    private class BluetoothClient extends Thread {

        private BluetoothDevice bluetoothDevice;
        private BluetoothSocket bluetoothSocket;
        private InputStream inputStream;
        private OutputStream outputStream;
        private boolean isAlive = true;

        public BluetoothClient( BluetoothDevice device ) {
            try {
                bluetoothDevice = device;
                bluetoothSocket = device.createRfcommSocketToServiceRecord( device.getUuids()[0].getUuid() );
                bluetoothSocket.connect();

                inputStream = bluetoothSocket.getInputStream();
                outputStream = bluetoothSocket.getOutputStream();

                Toast.makeText( MainActivity.this, device.getName() + " connected", Toast.LENGTH_LONG ).show();
            } catch ( IOException exception ) {
                Log.e( "DEBUG", "Cannot establish connection", exception );
            }
        }


        // Inutile dans le code actuel. Mais cela permettrait de recevoir
        // des informations du véhicule dans une future version. 
        @Override
        public void run() {
            try {
                while (isAlive) {
                    if ( inputStream.available() > 0 ) {
                        Log.i("DEBUG", String.format("%c", inputStream.read()));
                    } else {
                        Thread.sleep( 100 );
                    }
                }
            } catch( Exception exception ) {
                Log.e( "DEBUG", "Cannot read data", exception );
                close();
            }
        }

        public void writeChar(char code) {
            try {
                outputStream.write( code );
                outputStream.flush();
            } catch (IOException e) {
                Log.e( "DEBUG", "Cannot write message", e );
            }
        }

        // Termine la connexion en cours et tue le thread
        public void close() {
            try {
                bluetoothSocket.close();
                isAlive = false;
            } catch (IOException e) {
                Log.e( "DEBUG", "Cannot close socket", e );
            }
        }
    }
}
Le code de votre activité

Fonctionnellement parlant, il convient de vous connecter au périphérique bluetooth de votre véhicule en le sélectionnant dans la liste présente en bas de l'écran. Il doit, bien entendu, être préalablement associé à votre périphérique Android. Une fois la télécommande connectée, appuyez sur les boutons pour envoyer l'ordre au véhicule. Les ordres reconnus par le véhicule sont : 'a' pour avancer, 'r' pour reculer, 'l' pour tourner à gauche, 'd' pour tourner à droite et 's' pour stopper le véhicule.