Connecting a BMW to the Internet — Part Three

Trent Seed
Design and Tech.Co
Published in
5 min readApr 2, 2019

This blog series details how I connected my BMW E46 to the internet. In part one, we walked through the project motivation, architecture, and component installation process. In part two, we examined the Python code powering the Raspberry Pi which interfaces directly with the vehicle and wireless devices. In part three, we will review the code running on the Android tablet and smart watch, providing an end-to-end experience.

Introducing the Android App

The Android application is responsible for connecting to the Raspberry Pi via Bluetooth, and providing a user interface to issue commands such as rolling down the windows or locking the vehicle.

The project has three primary Java modules:

app – The Android application for mobile/tablet devices

ibuswear – The Android application for wearable devices

common – Common logic shared between the Android apps

For the main application, I designed a simple user interface that allows you to interact with the vehicle. There is also quick-access to applications such as Pandora, Google Play Music and Google Maps by tapping “Radio”, “Media”, and “Maps” respectively.

To interact with the vehicle’s devices, you may select the “Devices” option from the main activity. You will be presented with a horizontal list view of the various integrated devices, along with some quick actions such as locking and unlocking the vehicle, rolling up or down the windows, and more. This was accomplished by creating a RecyclerViewand attaching our AdapterDevices class (which implements RecyclerView.Adapter). There is a single layout XML that represents a device in the list, which is inflated when the adapter’s onCreateViewHoldermethod is called.

I introduced a simple Device class to manage properties for each item in the list, including their callback functions and images.

I proceeded to create a method for each device, which instantiates a Device object and sets the necessary attributes for the view. For example, in getInteriorLights() we assign a name, icon, action buttons, as well as click listeners.

When instantiating the RecyclerView adapter (AdapterDevices), an array list of these Device objects is provided. The adapter and recycler view implementations handle the remaining responsibilities of binding events to the view and displaying resources.

I’ve introduced an IBUSWrapper class which contains methods for interacting with the vehicle, as can be observed in the example device’s onClickListenerAction1 and onClickListenerAction2. For the methods that actuate the windows, you may provide a boolean to indicate if you want a window to roll up or down. The same approach is used for moving the driver seat forward or backward, as well as opening and closing the sunroof.

Each of the IBUSWrapper methods is responsible for writing the appropriate IBUS packet to the Bluetooth output stream. For example:

The Raspberry Pi on the receiving end will parse the data, and write to the IBUS network via a serial connection.

Connecting to the Controller via Bluetooth

The following snippet demonstrates the necessary code to establish a connection with the Raspberry Pi controller. It is important to note that the Bluetooth MAC Address must be specified, and the devices should already be paired via Bluetooth.

The ConnectedThread will continuously read from the Bluetooth input stream, and process information in the background. When data is received, it will relay the information to the current Activity. For example, if we see an IBUS packet that indicates the “next track” button on the steering wheel was pressed, we can instruct the Android device to advance to the next track.

Customizing the Application

Within Android Studio, you may customize the user interface and layouts to your liking. If you would like different default apps for Radio, Music, or Maps, you may easily make those changes.

Introducing Android Wear Support

Now that we have an Android application that communicates with the Raspberry Pi, it’s fairly simple to extend the project to support Android Wear. We can reuse all of our implementation for connectivity (the common module), and will just need to implement the views.

For the Android Wear app interface, I went with a bi-directional navigation pattern. You may scroll vertically on the watch to select a device (such as “Driver Window” or “Locks”) and then can scroll horizontally to select an action (such as “Up” or “Down”). The very first device in the list is called “Voice”, which enables you to invoke commands such as “roll down the driver window” or “pop the trunk”.

This was accomplished using a FragmentGridPageAdapter (now deprecated as of Android Wear 2.0), which contains a getFragment method that returns a Fragment for the appropriate view. The fragment arrangement is represented as a two-dimensional array.

In the adapter, the getFragment method performs a lookup in PAGES using the row and column int indexes.

Adding Voice Recognition

Introducing support for voice commands was easy thanks to Android Speech Recognition. The first step in obtaining a string representation of a voice command is to launch an Intent that starts the Speech Recognizer activity.

We’ll also need to implement an onActivityResult method in our Activity, which will be invoked when the Speech Recognizer activity finishes with a result.

We extract the spoken text string from the intent, and call VoiceCommand.processSpokenText() which exists in the common module. This method is responsible for taking the string and invoking an appropriate method in IBUSWrapper to interact with the vehicle.

This may not be the most elegant solution, but it works well for the targeted use cases. You can vary the command and it will still resolve to a method: “turn off the interior lights” vs “interior lights off”.

Wrapping it Up

I learned a lot throughout the project and hope this blog series serves as a helpful resource for those interested in connecting an E46 BMW to the internet. I plan on continuing to extend the project with new functionality, and add support for additional vehicle types. The controller was recently updated to Python 3.7, and now supports running as a Docker container. Contributions are welcome to the GitHub repository!

Follow Here for More Awesome Content

--

--

Trent Seed
Design and Tech.Co

I’m a startup Co-Founder and Chief Architect of Oomnitza. Areas of interest include Internet of Things, Chatbot Architecture, and Domain-Specific Languages.