Beim BeeWi Car handelt es sich um ein ferngesteuertes Auto der Firma BeeWi. Während meines Besuchs des gestrigen Workshops auf dem Developer Open Space in Leipzig zum Thema "Internet of Things", hatte ich den ersten Kontakt mit diesem kleinen Spielzeugauto.

Ich möchte euch mit diesem Artikel zeigen, wie man mit Xamarin.Android eine kleine Bluetooth-Fernsteuerung für das BeeWi Car schreibt und damit eine spaßige Zeit haben kann.

Internet of Things

Ziel des Workshops war es, dass man sich mit dem Auto, einem Arduinos oder anderer Sensor-Hardware, mit der Plattform seiner Wahl verbindet und so erste Gehversuche im Thema IoT unternimmt. In Zeiten von SmartHome und Home-Automation ist an diesem spannenden Thema ohnehin nicht vorbeizukommen.

Das User-Interface

Um die möglichen Befehle Vorwärts, Rückwärts, Links und Rechts an das BeeWi Car senden zu können, braucht die App entsprechende Eingabemöglichkeiten.

Die Layout-Definition ist sehr einfach gehalten. Sie besteht aus vier Buttons, für die Richtungsbefehle, sowie einer Liste an bekannten Geräten. Bevor der Benutzer ein Fahrzeug steuern kann, wählt er eins aus der Liste aus. Und schon kann es los gehen.

An Hand meiner Beschreibung ergibt sich folgendes XML für das Layout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <Button
        android:id="@+id/btnForward"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Vorwärts" />
    <Button
        android:id="@+id/btnLeft"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Links" />
    <Button
        android:id="@+id/btnRight"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Rechts" />
    <Button
        android:id="@+id/btnBackwards"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Rückwärts" />
    <TextView
        android:text="Devices"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView1" />
    <ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/listView1" />
</LinearLayout>

Wenn die App nun gestartet wird, ist es sinnvoll den Benutzer darüber aufzuklären ob sein Gerät überhaupt über Bluetooth verfügt. Ist dem so, dann fragt die App auch gleich ob sie Bluetooth aktivieren kann, falls es das nicht schon ist. Ist das nicht der Fall, tue einfach nichts weiter.

Mit folgenden Code kann genau das erreicht werden.

var bluetoothDefaultAdapter = Android.Bluetooth.BluetoothAdapter.DefaultAdapter;

if (bluetoothDefaultAdapter == null)
{
    Toast.MakeText(this, "Your device does not support Bluetooth", ToastLength.Long).Show();
    return;
}

if (!bluetoothDefaultAdapter.IsEnabled)
    StartActivityForResult(new Intent(Android.Bluetooth.BluetoothAdapter.ActionRequestEnable), 1);

Bluetooth ist verfügbar und aktiviert - Zeit die bereits dem Gerät bekannte Bluetooth-Geräte anzuzeigen. Dazu wird über die Liste an BondedDevices iteriert und der Name sowie die Adresse des Gerätes ausgegeben.

var deviceList = new List<string>();
foreach (var device in bluetoothDefaultAdapter.BondedDevices)
    deviceList.Add(String.Format("{0} ({1})", device.Name, device.Address));

var deviceAdapter = new ArrayAdapter(this, Android.Resource.Layout.SimpleListItemMultipleChoice, deviceList.ToArray());
var listView = FindViewById<ListView>(Resource.Id.listView1);
listView.ChoiceMode = ChoiceMode.Single;
listView.Adapter = deviceAdapter;

Vorwärts, Rückwärts, Links oder Rechts

Der Benutzer wählt das entsprechende Auto aus, in dem er auf eines der in der Liste befindlichen Geräte klickt. In diesem Moment erstellt man die Socket-Verbindung um die Kommunikation mit dem BeeWi Car zu beginnen.

listView.ItemClick += async delegate(object sender, AdapterView.ItemClickEventArgs e)
{
    if (currentDevice != null && currentDevice.Address != bluetoothDefaultAdapter.BondedDevices.ToList()[e.Position].Address)
    socket.Close();

    currentDevice = bluetoothDefaultAdapter.BondedDevices.ToList()[e.Position];
    socket = currentDevice.CreateRfcommSocketToServiceRecord(UUID.FromString("00001101-0000-1000-8000-00805f9b34fb"));

    try
    {
        if (!socket.IsConnected)
            await socket.ConnectAsync();
    }
    catch (Exception ex)
    {
        Toast.MakeText(this, "Could not connect to device", ToastLength.Short).Show();

        currentDevice = null;
        socket = null;
    }
};

"00001101-0000-1000-8000-00805f9b34fb" ist eine Standard-Id für SSP-Bluetooth.

Das Gerät ist nun verbunden, die Kommunikation steht auch. Um die Befehle für entsprechende Richtungsänderungen übermitteln zu können muss noch auf die Eingaben des Benutzers reagiert werden.

Am Beispiel der Vorwärtsbewegung ist ersichtlich wie der Button beim Drücken durch den Benutzer reagiert. Es ist zu sehen, dass wenn der Button gedrückt wird, der Forward_Go-Befehl abgesetzt wird. Sowie der Benutzer den Button nicht mehr drückt, wird der Befehlt aufgehoben.

front.Touch += delegate(object sender, View.TouchEventArgs e)
{
    if (e.Event.Action == MotionEventActions.Down || e.Event.Action == MotionEventActions.Move)
        SendCommand(BeeWiCarCommands.Forward_Go);
    else
        SendCommand(BeeWiCarCommands.Forward_Stop);
};

Alle anderen Buttons unterscheiden sich lediglich in der Art des Befehls, sind an sonsten aber gleich implementiert.

Mit SendCommand werden die ausgewählten Befehle an das BeeWi Car übermittelt. Dazu muss geprüft werden, ob eine Socket-Verbindung besteht und sie mit dem aktuellen Gerät verbunden ist. Ist das der Fall, dann kann der entsprechende Befehl in den Output-Stream geschrieben werden.

void SendCommand(BeeWiCarCommands command)
{
    try
    {
        if (socket == null)
        {
            Toast.MakeText(this, "No device selected", ToastLength.Short).Show();
            return;
        }

        if (!socket.IsConnected)
        {
            Toast.MakeText(this, "Device not connected", ToastLength.Short).Show();
            return;
        }

        if (socket.OutputStream.CanWrite)
        {
            socket.OutputStream.Write(new byte[]{ (byte)command }, 0, 1);
            socket.OutputStream.Flush();
        }
    }
    catch (Exception ex)
    {
        Toast.MakeText(this, ex.Message, ToastLength.Short).Show();
    }
}

Um die Bluetooth-Kommunikation in der App verwenden zu können, fehlt noch die Berechtigung im AndroidManifest:

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

Was fehlt?

Das Verbinden des Gerätes mit dem BeeWi Car, so genanntes Pairing, erfolgt mit den von Android bereitgestellten Möglichkeiten. Wer möchte, kann diese Funktionalität aber gerne in die App einbauen und mir seinen Weg in den Kommentaren beschreiben oder aber ein Pull-Request für das Repository erstellen.

Fazit

Mit Xamarin.Android ist es ein leichtes sich mit Bluetooth-Geräten zu verbinden, spielerisch die Möglichkeiten der bereits verfügbaren Hardware zu entdecken und sich ein wenig die Zeit zu vertreiben trägt ihr übriges bei.

Ich habe mir sagen lassen, dass mit der gleichen Steuerung auch die von BeeWi angebotenen Helikopter fliegen funktionieren. Wer also lieber Pilot anstatt Rennfahrer sein möchte, kann das mit der gleichen App sein.

comments powered by Disqus