Connecting a BMW to the Internet — Part Two

Trent Seed
Design and Tech.Co
Published in
6 min readNov 10, 2018

This blog series details how I connected my BMW to the internet. In part one, we walked through the project motivation, architecture, and component installation process. In part two, we will examine the Python code powering the Raspberry Pi, which serves as a gateway between the vehicle and our tablet, mobile device, and smart watch.

A Raspberry Pi Gateway

The Raspberry Pi is an essential component in the project, responsible for consuming messages on the vehicle bus and broadcasting them via Bluetooth, as well as receiving commands via Bluetooth and writing to the vehicle bus.

On the Raspberry Pi we will be running the operating system Raspbian, and will utilize Python 2.7 to implement our application. The Python application will establish a serial connection to our IBUS USB adapter, and will additionally act as a Bluetooth server for our wireless devices. We’ll want to setup the Python application as a service, so that if the Raspberry Pi ever restarts, the application will gracefully start back up.

Now that we’ve covered the high-level responsibilities, let’s dive into some code! For the purposes of this blog series, I’ve included snippets to demonstrate specific concepts, rather than detail everything as it currently exists on the GitHub page.

Writing Our First IBUS Packet

To interact with the USB IBUS adapter, we will be utilizing the open-source Python library called pyserial. In very few lines of code, we can establish a serial connection and write a packet that instructs the driver seat to move forward.

If everything is installed correctly, running this code will actuate the driver seat controls and move it forward. In this example, our USB IBUS adapter is registered as /dev/ttyUSB0. To determine which value to use, you may run ls /dev | grep USB or dmesg (here’s a reference).

The baudrate, or the rate at which information is transferred in a communication channel, is set to 9600 bits per second, a common rate for serial communications.

A parity bit, or check bit, is a bit added to a string of binary code to ensure that the total number of 1-bits in the string is even or odd. Parity bits are used as a simple form of error detection. For the IBUS USB adapter, we are utilizing serial.PARITY_EVEN.

The hardcoded packet 3F 06 72 0C 01 01 00 47 can be read as: device 3F is sending a message of length 06 to device 72. The last byte, 47, is the XOR checksum which will be referenced by the receiving device to ensure the integrity of the packet (the calculated value must match). Since the length includes the checksum and the destination id, we can subtract 2 bytes from the length to determine that the data portion of the packet is 4 bytes.

Here are a few additional IBUS commands you may try out:

Now that we’ve covered a simple example of writing a packet, let’s examine how we are able to consume packets.

Reading IBUS Packets

Let’s begin by reading some example data from the bus. We are going to read an arbitrary number of bytes, and break the input stream into separate packets. In our example below, the parse method returns a list of bytearrays.

Running this code will print “parsed N packets” to your console, where N is equal to the number of complete packets we were able to construct from the input.

As it stands, this code runs, establishes a connection, reads up to 1024 bytes, and prints our output. What we really want is for our application to continuously read activity on the bus. One way to accomplish this is to introduce a new daemon thread that will loop and read continuously.

On GitHub, you may reference the IBUSInterface implementation. Now that we’ve covered some examples to read and write to IBUS, let’s understand how we can incorporate Bluetooth for wireless communication with our mobile phone, tablet, and smart watch.

Establishing a Bluetooth Server

We will need to create a Bluetooth server for our wireless devices to communicate to the vehicle. To create our Bluetooth server, we will be utilizing the open-source Python library called pybluez. The following snippet demonstrates how to bind to the local Bluetooth adapter, listen for a connection, accept the connection, receive up to 1024 bytes, and echo back to the sender.

For our Python application, we will be advertising a service via Bluetooth SDP (Service Discovery Protocol), which will be discovered via our wireless devices Bluetooth SDP client. Bluetooth SDP provides a means by which service applications running on different Bluetooth enabled devices may discover each other’s existence and capabilities. We’ll need to define a service name, service UUID, and take advantage of the advertise_service() method of pybluez.

The Bluetooth protocol RFCOMM is a set of transport protocols made on top of the L2CAP protocol, providing emulated RS-232 serial ports. RFCOMM is sometimes called serial port emulation. The Bluetooth serial port profile is based on this protocol, which is leveraged in this example. For more information on the various Bluetooth protocols and profiles, refer to this page.

In the above example, self.server_sock.accept() will block until a connection is established, at which point the self.consume_bus() method will be invoked and the read loop will begin.

Now that we’ve demonstrated how to interact with the USB IBUS adapter and Bluetooth, we are ready to incorporate the components into a single application.

Introducing an Abstract Interface

There are many commonalities when it comes to supporting an interface, for example the ability to connect, send, and receive data. I decided to create a common class, BaseInterface, which will be used by the implementations of IBUSInterface and BluetoothInterface. The complete source code can be found on Github.

Rather than hardcode the various settings, we are utilizing ConfigParser to retrieve information through a base method called get_setting. Here’s an example configuration file:

[interfaces.ibus]
baudrate = 9600
port = /dev/ttyUSB0
timeout = 1
read_buffer_length = 9999
[interfaces.bluetooth]
service_name = ConnectedVehicle
service_uuid = 94f39d31–7d6d-437d-974a-eab49e49d4aa
read_buffer_length = 2048

You may also notice that most of the base methods raise a NotImplementedError, as each particular interface will be responsible for a varying implementation as we saw in the IBUS and Bluetooth example snippets. The bind_send and bind_receive methods are used to connect two interfaces, a sender and a receiver.

Deploying the Code

With an understanding of the various components, we may now deploy the source code on the Raspberry Pi. If you wired an Ethernet cable to the glove box and setup a Static IP, you may SSH to complete these steps. I created a new directory /opt/controller, which is the working directory. A virtualenv can be created to ensure the Python installation and dependencies remain isolated, /opt/controller/venv.

virtualenv /opt/controller/venv

The source code can be deployed to a directory such as /opt/controller/project. More information about deploying the code and dependencies can be found on the project’s readme.

git clone https://github.com/TrentSeed/BMW_E46_Android_RPi_IBUS_Controller /opt/controller/project

Running as a Service using systemd

To ensure the Python application runs on startup, we may create a configuration file /etc/systemd/system/controller.service.

[Unit]
Description=Connected Car Controller
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
User=bmw
ExecStart=/opt/controller/venv/python /opt/controller/project/start_controller.py
[Install]
WantedBy=multi-user.target

Once the configuration file is created, we can get the service to run on boot:

systemctl enable controller

We may also interact with the service with the following examples:

# start the application
systemctl start controller.service
# stop the application
systemctl stop controller.service
# check the application status
systemctl status controller.service

For more information about creating a service using systemd, please refer to this guide.

In part three of the blog series, we will examine the code powering the Android application which will be installed on the tablet and your smart watch.

https://medium.com/design-and-tech-co

--

--

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.