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).
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.
- Obtain the firmware, e.g. the file
res/raw/freedrum_dfu_48.zipfrom 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
0xf7, LED becomes red (not on FD v1 though).
- Else turn device off, release button, press it for 5 seconds until LED becomes yellow.
- Send SysEx message
- 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!
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.
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.
- 00001800-0000-1000-8000-00805f9b34fb - Generic Access
- 00001801-0000-1000-8000-00805f9b34fb - Generic Attribute
- 0000180f-0000-1000-8000-00805f9b34fb - Battery Service
- 03b80e5a-ede8-4b33-a751-6ce34ec4c700 - Bluetooth LE MIDI
- 0e5a1523-ede8-4b33-a751-6ce34ec47c00 - Freedrum
0e5a1523-ede8-4b33-a751-6ce34ec47c00. It looks like it can get/set the following:
- drum conf
- version - string value at 0
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).
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).
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!)
0xf7- enter bootloader
0xf7- turn off
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.
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 188.8.131.52) (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).
- Nordic SDK: Connection Parameters Negotiation
- Bluetooth Core 4.2
- Apple Bluetooth design guidelines
- Android 6.0 Marsmallow BLE: Connection Parameters
- Excessive Bluetooth LE timeouts on Linux?
- Allow setting of connection parameters in noble
- Re: KORG nanoKONTROL Studio MIDI over Bluetooth problem
- [PATCH v4 Bluez 0/4] Connection Update Improvements