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).
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.
- 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.
- 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!
todo changing device settings. See midi commands, a forum topic and CC below.
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.
The GATT characteristics mention UUIDs, which identifies them (explanation and list):
- 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
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
It looks like we have an nRF52 SoC here, using an ICM20948 motion sensor and LP5562 RGBW LED driver.
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.
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.
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!)
0xf0
0x04
0xf7
- enter bootloader0xf0
0x05
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 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).
- 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
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 ?