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.
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.
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;
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" />
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.
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.
Als Mobile-Enthusiast und Geschäftsführer der Cayas Software GmbH ist es mir ein großes Anliegen, mein Team und unsere Kunden zu unterstützen, neue potenziale zu entdecken und gemeinsam zu wachsen. Hier schreibe ich vor allem zur Entwicklung von Android und iOS-Apps mit Xamarin und .NET MAUI.
Like what you read? Come work with us.
Learn why your Xamarin Android build fails with "Error executing task Aapt: VersionCode is outside 0, 65535 interval" and how to workaround that issue.
Bei der Entwicklung von Kartenfunktionalitäten ist es hin und wieder sehr hilfreich, wenn man Standort-Koordinaten an die App senden kann ohne umständlich eigene Feldtests durchführen zu müssen.
Hin und wieder fragen mich Kunden, wie ich eigentlich die Apps testen kann. Manche sind sogar der Meinung das ich ein riesiges Arsenal an Geräten vorrätig haben müsste um besonders im Android-Umfeld alle möglichen Konfigurationen testen zu können.