HID protocol research and a proof-of-concept micropython firmware that lets a HID-capable microcontroller (e.g. Pi Pico 2W) successfully emulate a BMD Speed Editor and interface natively with Davinci Resolve.
This project is designed to be adapted into open source macropad firmwares to allow native communication with Davinci Resolve.
Only USB is currently supported, however Bluetooth is also (potentially) planned.
All findings of this project are derived from public online resources (See References section) or through observing USB HID traffic. I did not decompile or reverse-engineer any Davinci Resolve software or Blackmagic firmware.
THIS PROJECT IS LICENSED UNDER GNU AGPLv3. COMPANIES/INDIVIDUALS ARE NOT ALLOWED TO INTEGRATE THIS PROJECT INTO CLOSED-SOURCE/PROPRIETARY SOFTWARE OR HARDWARE.
This firmware took a lot of research and work to create, please consider donating! Help me fund a speed editor to implement Bluetooth support.
This project's goal is NOT to create counterfeits to the Speed editor.
The speed editor's actually a pretty good deal considering that it's pretty high quality and comes with a studio license, or you're working professionally and need official support.
However, if you already own a studio license, need special accessibility hardware, uses an alternative / better macropad (for example my WIP open-source editing keyboard with a haptic jog wheel) that works with Resolve, or just want your existing macropad to work with resolve (plus the official one is locked to resolve), this project is for you.
With a virtual USB driver, someone theoretically can also adapt this project to allow customization of their official speed editor keyboard (e.g. remap the multicam keys within Davinci Resolve).
Davinci Resolve comes with an undocumented and very limited API, however it's too limited and does not support all basic editing features one would expect from a macropad. Plus, it requires special software setup, and it won't work with the iOS version1. Therefore I decided to create a firmware that can emulate the protocols of the Speed Editor. The best case scenario would be getting bmd to open up an api for accessories, but since that's not happening soon this is the best option.
To prevent this project being misused for counterfeiting, only practical speed editor functionality is implemented. You can check if your hardware is using this project by going to the Davinci Control Panels software or checking its reported serial number. It will show the connected hardware as "blackmagic device" instead of "Speed Editor", plus you'll be unable to update its firmware through the app. These issues are intended and I don't plan on fixing them.
This project is not directly ready for production. However it provides plenty of comments, writeups and easy-to-understand python code that FOSS firmware developers can adapt into their own projects. Since micropython's USB backend is TinyUSB, like many other microcontroller firmwares, it should be trivial to port this code to, for example, an ESP32 using c++.
Please remember to credit me by including Speed editor emulation layer licensed from KIP (kip.gay), AGPLv3 License in
your project.
I provide compiled .uf2 demo firmwares for the pi pico/pico 2 and wireless variants. Please note that running this on a pico 2W is most recommended.
The pi pico firmware is tested on a Pico 2W type-C clone board. If you're using Windows, I highly recommend running this project
on a wireless-capable pi pico board (W-series).
When Windows detects the spoofed speed editor VID, blackmagic's driver takes complete control over pico's usb, which hides the USB serial connection. Therefore, comms is done through WebREPL.
I provide four different demo firmware, for raspberry pi pico, raspberry pi pico W, raspberry pi pico 2, raspberry pi pico 2W respectively. Theoretically any RP2040 or RP2350 dev boards should be supported.
There are significant differences between the W and non-W firmwares.
Since there's no way to communicate with the pico, the pico will randomly emulate some functions of the speed editor, probably moving the jog wheel and pressing some keys. Make sure you're not loaded into an important resolve project!
The pi pico will automatically create an open WiFi network called blackmacro. Connect to the network, then connect
to the pi via WebREPL (micropython's web version doesn't work because of HSTS) using the IP 192.168.4.1:8266 and password madebykip,
you'll then be able to control the firmware through a terminal UI.
press h to show / refresh current menu. Press the keys labeled in [] to select a menu item. A <?> prompt indicates
you have to enter a multi-character value, submitted via the enter key.
Press ~ to toggle visibility of HID packet logs.
The onboard LED will light up once the device is initialized and serial communication is turned off. To interrupt the firmware and reenable the serial connection, press the BOOTSEL button after the LED is lit up. The LED will turn off, and the pico will automatically reconnect as a standard micropython serial device.
If you decide to use a type-C pico clone board like I did, your board's manufacturer probably also forgot to add a resistor on the connector, so you must use an A-to-C cable for data.
If you like this project or it helped you, strongly consider buying me a coffee! Help me fund a speed editor to investigate its bluetooth connection~
This project is licensed under GNU AGPLv3, I reiterate some of its limitations:
- You are not allowed to use/integrate this project in proprietary / closed source hardware and(or) software.
- You must make derivative works free and open source.
- You are not allowed to use any part of this project to train "Generative AI" / "LLM" machine learning models.
Additionally, you are not allowed to sell this project as-is.
This project uses a modifed version of micropython's USB library. As of this repo's release, micropython's USB library is incomplete and does not implement some of the USB HID functions required by this project. Please make sure you're using the library included with the repo and not from
mip.
Please see notebooks/additional_notes.md and notebooks/hid_sequence.md .
The protocols are understood through bmd.py and the wireshark traffic from Poking Technology's video.
Honestly, understanding the algorithm and cracking the keys was very fun and not that challenging. If you're a beginner interested in USB HID, basic boolean algebra, or computer datastructures, I highly recommend trying it yourself through the references listed below (you don't even need to have a speed editor). A fun detour would be investigating the dataset distribution!
Take a look at notebooks/ for my solution.
If you want to have a try cracking it yourself, don't read the contents below! (skip to #References)
The MASK is unchanged from the PC side. The keys and the magic bitmask (the 8bit value in the if statement) are changed. The keys and the magic bitmask are trivial to extract and bruteforce. For a more fun challenge, try reverse engineering the MASK value.
I assumed the encryption algorithm is unchanged between the pc side and the keyboard side, only the variables are changed.
The algorithm is interesting because the keys are only applied at the last step with a XOR, and from the bank
of 16 keys, only one is used in a run (chosen deterministically), so it's trivial to reverse the XOR to get the original
key, given a dataset of correct encryption pairs.
The difficulty comes from determining the variables before the key is applied, which are the MASK(64 bits) and
magic bitmask(8 bits) numbers.
If the MASK is changed, the cracking process would be a little bit more complicated, since it involves an AND nested
within other XORs, so some data is lost in the process. However since the bits are not shifted during the bitwise
operations, through a truth table and processing the bits individually, I can use a big dataset to determine each bit of
the MASK through elimination.
However it turns out the MASK was not changed at all, instead only a 8bit magic bitmask value was changed.
The possible value range (only 0-255) is so small, I can directly bruteforce this number incredibly quickly.
The MASK can also be guessed out if necessary. The MASK is 64bit, so bruteforcing would be a bit unrealistic,
especially with the amount of unknown variables at this point.
The MASK is applied at the last step of the encryption process, via v ^ (rol8(v) & MASK) ^ k. At this point, we don't know
either the key k or the magic bitmask (which affects v and rol8(v)). A few observations allow us to reduce the amount of unknown variables
to figure out what the MASK is, with a large enough dataset.
To figure out what the MASK is, we first need a known key (explained in observation 5). A few interesting observations
make figuring out a key possible.
- The magic bitmask is irrelevant. There is a quirk in the algorithm that ensures the key will be odd when n==0.
There is no data on even == 0 & n == 0 !
handshake dataset distribution histogram bar chart thing (x-axis: key index; y-axis: amount of entries)
This is because evenness is calculated by
v = auth.rol8n(challenge, n); EVENNESS = (v & 1) == ((MAGIC_BITMASK >> n) & 1).
n is calculated bychallenge & 7, when n == 0, the last three bits must be zero.... XXXXX000 <AND> 00000111 -------------- n = (0) 000Since n also determines rotate factor, the bytes are not rotated.
v & 1aka rotated challenge's last bit therefore is 0.Odd happens when v & 1 != x, and x = 0 when n == 0.
so when n == 0, v & 1 always == x, so it's always even. This also gives us twice the amount of data for this situation.
Why was there even a key for odd n == 0? I don't know. This seems like an oversight of how the algorithm is designed.
Through this, we can calculate what v and rol8(v) is when n==0. So our goal is to figure out what the n==0 odd key is.
- Both the
MASKand thekeyare applied at and only at the last step. This means we can theoretically just reverse the boolean operation of the last step to figure out what they are. (Of course it's not that simple sinceANDis destructive and there are too many variables, but this still dramatically reduces the cracking complexity) - There are no bitshifts. If we consider
vandrol8(v)as separate variables, then we can calculate each bit individually. This reduces the amount of guesses we have to make from 2^64 = 1.8446744e+19 to 2*64 = 128 for the two values. - We can cache some of the values. With observation 1, we can pre-calculate the values of
vandrol8(v). Since we know the result (the final encrypted message), the eq becomes boolean algebra with 2 unknowns. (okay this one isn't that important since other techniques have already drastically decreased required guesses so much that the speed increase from caching is insignificant now) - The
MASKstays the same in all situations, plus thekeystays the same for a (n, evenness) configuration. With the even 0 key, we can figure out theMASK, which can be directly applied to figure out rest of the keys. - The
MASKdirectly influence the eq result. If we take a look at the truth table for eqa ^ (b & c) ^ d, we see:
a b c d b∧cb∧c a⊕(b∧c) (a⊕(b∧c))⊕d
1 1 1 1 1 0 1
1 1 1 0 1 0 0
1 1 0 1 0 1 0
1 1 0 0 0 1 1
1 0 1 1 0 1 0
1 0 1 0 0 1 1
1 0 0 1 0 1 0
1 0 0 0 0 1 1
0 1 1 1 1 1 0
0 1 1 0 1 1 1
0 1 0 1 0 0 1
0 1 0 0 0 0 0
0 0 1 1 0 0 1
0 0 1 0 0 0 0
0 0 0 1 0 0 1
0 0 0 0 0 0 0
We see that for some values of (a, b), the result is directly influenced by d (the key).
Reversing the truth table, we find:
# a, b, result: possible combinations of (c, d)
truth_table = {
(0, 0, 0): [(0, 0), (1, 0)], # key can only be 0!
(0, 0, 1): [(0, 1), (1, 1)], # key can only be 1!
(0, 1, 0): [(0, 0), (1, 1)],
(0, 1, 1): [(0, 1), (1, 0)],
(1, 0, 0): [(0, 1), (1, 1)], # key can only be 1!
(1, 0, 1): [(0, 0), (1, 0)], # key can only be 0!
(1, 1, 0): [(0, 1), (1, 0)],
(1, 1, 1): [(0, 0), (1, 1)],
}For each bit on a dataset entry, If result, v, and rol8(v) matches one of the special configurations, we can
positively determine
that bit on the key is NOT 0 or 1. Through enough data, we can determine each bit of the key through elimination.
With the same principle, we can determine what the MASK is through elimination by plugging the key into the eq as well.
Note: Since the
MASKis applied through anAND, we cannot determine what theMASKis as accurately as the 0-odd key. However this still drastically reduces the amount of possibleMASKs we have to bruteforce through.
To run the key reconstruction notebooks, you'll need python, pandas, matplotlib, jupyter, etc. The notebooks themselves are very messy. I left notes as I worked through the project. The notebook should be able to derive the magic numbers through the dataset alone, however some values are hardcoded into the project because I didn't want to spend time writing input handlers or shuffle data around.
Thanks to these very helpful online resources:
- Especially https://github.com/smunaut/blackmagic-misc/blob/master/bmd.py, Copyright (C) 2021 Sylvain Munaut tnt@246tNt.com Apache 2.0 license
- Some hid traffic from screengrabs of wireshark from https://www.youtube.com/watch?v=UoIlwze5xp4
- https://www.reddit.com/r/codes/comments/mq9yot/i_am_cracking_a_usb_handshake_code_for_davinci/
- https://jonnyelwyn.co.uk/film-and-video-editing/using-the-davinci-resolve-speed-editor-in-other-video-editing-apps/
- https://commandpost.fcp.cafe/control-surfaces/resolve/ (specifically discussions)
- https://github.com/orgs/micropython/discussions/17410 for pointers on implementing feature reports in micropython's HID library
- Reddit user u/Tree_Tea for providing their keyboard's report descriptors.
This project is in no way affiliated with nor endorsed by blackmagicdesign, Davinci Resolve, or the references listed above.
(c) 2026 KIP
Footnotes
-
iOS version of Davinci Resolve only works over bluetooth, not USB. Bluetooth is currently not yet implemented in this project, so currently iOS is unsupported. ↩

