Skip to content

Instantly share code, notes, and snippets.

@wvengen
Last active March 13, 2021 17:20
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wvengen/7ebd29da38c08540832fb228c4628171 to your computer and use it in GitHub Desktop.
Save wvengen/7ebd29da38c08540832fb228c4628171 to your computer and use it in GitHub Desktop.
Freedrum on Linux

Freedrum on Linux

The recently released Freedrum has no official Linux support. But, fortunately, it uses standard Bluetooth LE MIDI. It doesn't seem to work out of the box, yet (on Ubuntu 17.04, most probably Ubuntu 18.04 will support it directly).

Bluez 5.46

One really needs bluez 5.46 or higher. On Ubuntu, these are packaged in artful proposed (amd64). Unfortunately, MIDI support is not enabled in this build (Ubuntu bug #1713017).

So you'll need to download the sources, install dependencies plus libasound2-dev, build and install resulting debs. You may need to add --enable-midi to debian/rules. Like this:

$ sudo apt install build-essential debhelper fakeroot wget \
                   dh-autoreconf flex bison libdbus-glib-1-dev libglib2.0-dev \
                   libcap-ng-dev udev libudev-dev libreadline-dev libical-dev \
                   check dh-systemd libebook1.2-dev libasound2-dev
$ wget https://launchpad.net/ubuntu/+archive/primary/+files/bluez_5.46.orig.tar.xz
$ wget https://launchpad.net/ubuntu/+archive/primary/+files/bluez_5.46-0ubuntu2.debian.tar.xz
$ tar xJf bluez_5.46.orig.tar.xz
$ cd bluez-5.46
$ tar xJf ../bluez_5.46-0ubuntu3.debian.tar.xz
$ sed -i 's/\(--enable-usb\)/\1 --enable-midi/' debian/rules
$ dpkg-buildpackage -rfakeroot -b
$ cd ..
$ sudo dpkg -i bluez*.deb libbluetooth*.deb
$ sudo service bluetooth restart

After this you should see the Freedrum devices show up in seqdump -l. To connect this to a MIDI drum synth like Hydrogen, open it and connect the ALSA MIDI port from FD v1 to it. Note that I haven't been able to record reliably with Hydrogen, with Ardour that was no problem.

Upgrading firmware

  • Obtain the firmware, e.g. the file res/raw/freedrum_dfu_48.zip from the Android APK. Let's hope Freedrum will provide these as a direct download in the future.
  • Put the device in bootloader mode
    • Send SysEx message 0xf0 0x04 0xf7, LED becomes red (not on FD v1 though).
    • Else turn device off, release button, press it for 5 seconds until LED becomes yellow.
  • You'll see there a new bluetooth device with the address + 1, named DfuTarg.
  • Perform the firmware upgrade (no bonding), can be done using ota-dfu-python
    $ git clone https://github.com/dingari/ota-dfu-python.git
    $ cd ota-dfu-python
    $ pip install --user pexpect intelhex
    $ bluetoothctl
    [NEW] Device 01:02:03:04:05:07 DfuTarg
    # quit
    $ sudo python dfu.py -a 01:02:03:04:05:07 -z ../freedrum_dfu_48.zip
    
  • After it is finished, the Freedrum device will reboot and be upgraded!

Configuration

todo changing device settings. See midi commands, a forum topic and CC below.

Reducing latency

For drumming, low latency is really important. On the audio side, you can use JACK with low buffer sizes. On the Bluetooth side, connection parameters need to be set. This would normally happen by default, but somehow it doesn't happen. To do it manually, see this blogpost and perhaps this linux-bluetooth post. According to the Freedrum developer, the device should (be able to) communicate this.

See below for technical notes on finding out why this isn't set by default.


Manual inspection

If you haven't paired yet, you can interact with the device manually. This also works for lower versions of Bluez without MIDI support.

# hcitool lescan
01:02:03:04:05:06 FD1 v1
# hcitool leinfo --random 01:02:03:04:05:06
Handle: 64 (0x0040)
LMP Version: 4.2 (0x8) LMP Subversion: 0x91
Manufacturer: Nordic Semiconductor ASA (89)
Features: 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00
# hcitool lewladd --random 01:02:03:04:05:06
# gatttool -b 01:02:03:04:05:06 -t random --characteristics
attr handle = 0x0001, end grp handle = 0x0009 uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle = 0x000a, end grp handle = 0x000a uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle = 0x000b, end grp handle = 0x000e uuid: 0000180f-0000-1000-8000-00805f9b34fb
attr handle = 0x000f, end grp handle = 0x0012 uuid: 03b80e5a-ede8-4b33-a751-6ce34ec4c700
attr handle = 0x0013, end grp handle = 0xffff uuid: 0e5a1523-ede8-4b33-a751-6ce34ec47c00
# gatttool -b 01:02:03:04:05:06 -t random --char-read --handle 0x11
# gatttool -b 01:02:03:04:05:06 -t random --char-write-req --handle 0x12 --value 01:00 --listen
Notification handle = 0x0011 value: 80 80 89 26 00 
Notification handle = 0x0011 value: 80 80 b0 10 04 

The last two notifications appeared after using the Freedrum stick.

Services

The GATT characteristics mention UUIDs, which identifies them (explanation and list):

Freedrum service

Has UUID 0e5a1523-ede8-4b33-a751-6ce34ec47c00. It looks like it can get/set the following:

  • orientation
  • drum conf
  • status
  • version - string value at 0

Technical notes

It looks like we have an nRF52 SoC here, using an ICM20948 motion sensor and LP5562 RGBW LED driver.

More about firmware

The firmware for FD v3 is 186312 bytes in size. Descriptor:

$ nrfutil pkg display freedrum_dfu_48.zip 

DFU Package: <freedrum_dfu_48.zip>:
|
|- Image count: 1
|
|- Image #0:
   |- Type: application
   |- Image file: freedrum_nrf52.elf.bin
   |- Init packet file: freedrum_nrf52.elf.dat
      |
      |- op_code: INIT
      |- signature_type: ECDSA_P256_SHA256
      |- signature (little-endian): 65829efea197514fba0c311d97baa4a050823e4888674ff94c851ff703a8f7a1c2d0167320010d19ce81b378709f3d816d7b49dbc0510792d9834c4437984a53
      |
      |- fw_version: 0x00000004 (4)
      |- hw_version 0x00000034 (52)
      |- sd_req: 0x91
      |- type: APPLICATION
      |- sd_size: 0
      |- bl_size: 0
      |- app_size: 186312
      |
      |- hash_type: SHA256
      |- hash (little-endian): 6f369b7cb1f22c75b2f2e99e1e535cf8117975d14fa6a1b9811158f05add4eea
      |
      |- is_debug: False

So we have an nRFC52xxx device with SoftDevice version s132_nrf52_3.1.0. Firmware contains only the application, which makes sense (no SoftDevice or Bootloader).

Instruction set is ARM (Cortex-M4). Useful tools may be radare, plasma, onlinedisassembler and many more.

More about bootloader mode

In bootloader mode, there is a service UUID 0000fe59-0000-1000-8000-00805f9b34fb. It has endpoint UUID 8ec90001-f315-4f60-9fb8-838830daea50 for the DFU Control Point and 8ec90002-f315-4f60-9fb8-838830daea50 for DFU Packet. This corresponds to secure DFU for the nRF5 SDK v12 (perhaps other versions too).

Too bad Nordic's official tool nrfutil can't be used can't do the upgrade. ble didn't work either. Gladly the above fork of ota-dfu-python works.

CC

List of CCs (submit on channel 0):

  • 20 - command
    • 0 - save configuration
    • (no other commands)
  • 21 - status (compass valid, is still)
  • 22 - read value (any of the following CCs)
  • 23 - Y position
  • 103 - Z position
  • 104 - threshold
  • 105 - sensitivity
  • 106 - midi note for pad
  • 107 - X axis CC
  • 108 - Y axis CC
  • 109 - Z axis CC
  • 110 - drum window size
  • 111 - drum strength

This has now also been partly documented on the Freedrum website (thanks!)

SysEx

  • 0xf0 0x04 0xf7 - enter bootloader
  • 0xf0 0x05 0xf7 - turn off

Snooping bluetooth

on Android, there is the possibility to capture Bluetooth packets. Remount /system read-write, edit bt_stack.conf to set TRC_GATT=5. I needed to run btsnoop as root on the adb shell to get proper dumps. This helped to find a known-good initialization sequence.

Improving latency

It would be great if the Linux bluetooth system (bluez) would set the device's connection parameters by default. If you have an idea where this may be going wrong, please let me know!

The device is expected to send a "connection parameter update request" (BL Core 4.2 section 7.7.65.6) (event code 0x3e).

I see the following initial create connection packet (using wireshark):

Android: host -> controller                        Linux: host -> controller
Bluetooth HCI Command - LE Create Connection       Bluetooth HCI Command - LE Create Connection
MinInterval: 24 (30ms)                             MinInterval: 40 (50ms)
MaxInterval: 40 (50ms)                             MaxInterval: 56 (70ms)
Latency: 0                                         Latency: 0

Note that a lot of fields were stripped, only the description and connection parameters are shown. Then on Android, there are some connection updates that are not happening on Linux:

Android: controller -> host
Bluetooth HCI Event - LE Meta
Sub Event: LE Connection Update Complete
Interval: 39 (48.75ms)
Latency: 0

Android: host -> controller
Bluetooth HCI Command - LE Connection Update
MinInterval: 6 (7.5ms)
MaxInterval: 6 (7.5ms)
Latency: 0

Android: controller -> host
Bluetooth HCI Event - LE Meta
Sub Event: LE Connection Update Complete
Interval: 6 (7.5ms)
Latency: 0

Android: host -> controller
Bluetooth HCI Command - LE Connection Update
MinInterval: 39 (48.75ms)
MaxInterval: 39 (48.75ms)
Latency: 0

Android: controller -> host
Bluetooth HCI Event - LE Meta
Sub Event: LE Connection Update Complete
Interval: 39 (48.75ms)
Latency: 0

In between these events, there are many other packets (like version request, which doesn't happen on Linux), setting up encryption, etc. Then something interesting appears on Android (and not on Linux):

Android: [device_address] -> localhost
L2CAP Command: Connection Parameter Update Request
MinInterval: 6 (7.5ms)
MaxInterval: 12 (15ms)
Latency: 0

Android: localhost -> [device_address]
L2CAP Command: Connection Parameter Update Response
Move Result: Accepted

This is something the device requests, and allows the host to choose proper connection parameters. Why is this not happening on Linux? Does the device wait for something, or is it sent but not picked up by the controller?

For comparison, please see below a list of packets after turning on the device (from connection packet) for Linux (left) and Android (right).

@jibweb
Copy link

jibweb commented Sep 30, 2017

Hey,
Great job, thanks for this super useful guide. I can't wait to try out.
However, I need to get a bluetooth adapter for my computer. I was wondering if you might have an idea of one that would work both with freedrum and linux ? Which one do you use ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment