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é
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.
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> |
screenOrientation="portrait"
en ligne 15.
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.
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> |
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 ); } } } } |
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.
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 :