Elixir+Nerves for controlling your car (Part 1)

Warning ! All your modifications will cause issues with your car insurance. And may be dangerous ! Please work responsibly and think before doing something.

Additional thank for Nerves team and community. It’s really one of the most friendly communities and developers are never sleep and always ready to help at any time. Your are the best !

I’m an happy owner of BMW 320CI (2003 coupe) and because I love my car and missing set of modern features like playing music from USB, AUX, Phone connection, voice control, mirrors memory and many other small things. I started to implement them by myself.

Elixir really suits perfect for this because of it’s stability (nobody wont’s car to stop or mirrors moved in the middle of your trip). Pattern matching fits perfect for working with binary messages. And lot of other features.

For experiments you will need CAN interface. After a while I bought 2 different devices one for developing and debugging at home (Reslers USB device) and one for working in car using RaspberryPi3 (PiCAN2).

Let’s start with a bit of theory. Most cars using CAN Bus for internal communication. There are lot of information about it online https://en.wikipedia.org/wiki/CAN_bus but for your car it might be really hard to find specifications. I found this folder in Google Drive. Thank you George Umanov for your great work !

CAN Bus has 2 wires (high and low) for working with it but BMW IBus has only one. And you will need separate device. You could buy already assembled Reslers USB device with TH3122 chip. Or assemble your own (it’s really hard to order TH3122 chip). Here are details and shames about assembling: https://drive.google.com/open?id=1w3Y9V8-wWSqb3Vne_CNqRjd2DvdAXuNw

After assembling/receiving assembled device you will be able to use UART module and receive messages. Some info about CAN/IBus message. It just binary message consisting of several parts:

For example if radio try to get info about CD changer it send this message <<0x68, 0x03, 0x18, 0x01, 0x72>> where:

  • Source module: 0x68 Radio address
  • Message length: 0x03–3 bytes
  • Destination module: 0x18 CD changer address
  • Message 0x01 Request status
  • Checksum: 0x72

I spent some time to understand it and wrote a really small library for parsing and working with CAN messages using Elixir. https://github.com/konstantinzolotarev/ex_ibus. Pretty simple lib for parsing and encoding CAN Bus and Ibus messages. And reader GenServer that can handle raw messages or part of raw messages, combine everything together and parse and pick messages from this spam.

Next step is connecting your laptop with car (I spent lot of time working inside car and bought radio, LCM (light control module, required for working with IBus) and IKE (main instrument cluster, not required). And created small dev stand at home.

At home I’m working with Reslers USB device using UART. So for connection you have to know device name and configure it for transmitting data.

To get device name I used Nerves.UART.enumerate() function for this. Just start iex -S mix and run this command:

iex(1)> Nerves.UART.enumerate()
%{
"/dev/cu.Bluetooth-Incoming-Port" => %{},
"/dev/cu.CHX-DevB" => %{},
"/dev/cu.SLAB_USBtoUART" => %{
manufacturer: "Silicon Labs",
product_id: 60000,
serial_number: "R.Resler IBUS Interface V6b - Serial:20170918000441002322",
vendor_id: 4292
}
}
  • Device name (in my case) “/dev/cu.SLAB_USBtoUART”
  • UART options (required): active: true, speed: 9600, parity: :even
{:ok, _pid} = Nerves.UART.start_link(name: Ibus.Serial)    
:ok = Nerves.UART.open(Ibus.Serial, “/dev/cu.SLAB_USBtoUART”, active: true, speed: 9600, parity: :even)

Now after physical connecting device to IBus it will start spamming you with binary data. It could be full messages, part of IBus message or one byte of message. All this binary data flow handled by ExIbus.Reader combining data, fetch IBus messages and sends them to listener PID. I created GenServer for listening all ready messages and starting UART/ExIbus.Reader processes. Here is code samples for this: https://github.com/konstantinzolotarev/bmw_bot/blob/master/apps/ibus/lib/ibus/uart.ex

Now after all this magic we could just listen ExIbus.Message structs and react on them. For example you could send text to Radio display (in BMW Ibus you have to send message from Telephone module to IKE, have no idea why…). I wrote several helper modules for working with car here: https://github.com/konstantinzolotarev/bmw_bot/tree/master/apps/ibus/lib/ibus/device

And now could send commands without need to remember all device addresses. Like: Ibus.Device.Tel.send_text("Hello Medium")

➜  bmw_bot master ✓ iex -S mix
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
12:07:16.780 [debug] Elixir.Ibus.UART: Start serial port
12:07:16.788 [debug] Elixir.Ibus.UART: Open serial connection
12:07:16.790 [debug] Elixir.Ibus.UART: Start serial reader
12:07:16.790 [debug] Elixir.Ibus.UART: Finish configure
12:07:16.790 [debug] Elixir.Ibus.Device.CD: Sending annoounce message
Interactive Elixir (1.6.4) - press Ctrl+C to exit (type h() ENTER for help)
12:08:35.458 [debug] Elixir.Ibus.Device.CD: Handled message 0x68 0x05 0x18 0x38 0x01 0x00 0x4C
iex(1)> Ibus.Device.Tel.send_text("Hello Medium")
:ok

Now you could just play with your devices and dive into IBus specifications =D

In next part we’ll continue our journey into working with car and if something is not clear — just post a comment.