Using usbMIDI with I2C without saturating I2C bus

Not open for further replies.
Hi there,
First I would like to thank Paul for his work on Teensy and the Arduino environment in general and thank in advance the community for the time it will spend on this thread.

I and 2 other guys are working on a project of MIDI modular controller.
The principle is :
A master unit communicates thru MIDI usb with midi software on computer.
Slave units handles the actual controlling parts (knobs, encoders, faders etc.) and communicates in I2C (half duplex) with master unit.

When master request values from slave, this one send back to him the changing values thru I2C.
Master then passes it to usbMIDI.
When master receives MIDI data from computer, he sends values back to slave thru I2C.

Configuration :
* Master Teensy 4
* Slave Teensy 4 with 6 encoders multiplexed with 74HC165 shift registers using EncoderTool library from luni64 (slightly modified, use the attached one)
* I2C library is Richard-Gemmell Teensy4_i2C
* Teensyduino native usbMIDI library
* Macintosh OS 10.15.7 with Ableton Live as MIDI software

In principle, things are working almost okay.
I mean we manage to control Ableton Live with slave encoders.
When we send MIDI back, the encoders values are well set, i.e. if we move again one of the encoder, it will restart from the updated value.

The issue we're facing is with a lot of MIDI feedback values.
When Ableton Live sends let's say 12 or more continuously changing CC values, the MIDI data entering in Ableton becomes inacurrate, and it goes worth as we increase the amount of MIDI feedback.

Given that usbMIDI is much faster (I read at least 12Mbits minimum) than I2C, we feel like master is certainly saturating I2C bus in the feedback operation.
Yet for the purpose of the project, I2C, or any hot swappable capable protocol, is required to communicate between master and slave units.

So we thought solution could be :

- first, increase the speed of the I2C bus
In this perspective, we tried to set I2C clock to the higher clock frequency available in the library, i.e. 1 Mbits, without any obvious improvement.
We asked Richard Gemmel on GitHub if higher speed modes would be implemented in a near future.

- second, slow usbMIDI in order to not saturate I2C bus
We don't have a clue about how to do this.

So our questions are :
Do you think it looks like the good way to improve I2C flow ?
If yes, do you have any idea to reduce usbMIDI speed ?

Given that our codes are quite long and use miscellaneous libraries, we thought it was better to join all in a .zip folder rather than copy code in this post.

Thank you in advance for your replies.
Best regards,
View attachment
Perhaps filter the incoming midi data, keep the latest master values of controllers and only update changes to the slave every ... say 1 ms or send as often as the i2c can handle dependent on how many channels are being changed
but I recall seeing something where the host wasn't packaging MIDI efficiently and a fix is in the works??? (looking to see if I've imagined this...)

Looks like this project isn't using USBHost at all.

But yeah, improved USB host MIDI transmit was recently put into USBHost_t36 and it's in 1.54-beta7 (published yesterday).

Part of that improvement is a change to how EHCI interrupts are handled, which probably breaks (exposes race conditions) in some of the other USBHost_t36 non-MIDI drivers, so whether we keep that setting or revert it before a 1.54 release is still an open question. But even if we go back to the old interrupt setting, this patch still greatly improves MIDI transmit performance. So we're talking about going from ~3.3 million maximum MIDI messages per second down to about 1 million or maybe as slow as 128000 messages/sec. That's still a lot faster than the ~2000 messages/sec speed USBHost_t36 in Teensyduino 1.53 provides, which was only about twice the 1042 messages/sec speed of 3-byte messages on 31250 baud serial MIDI, just to keep things in perspective.

EDIT : Increase the i2c speed to 1Mbits has improve a little bit the results, but it's not smooth enough.

Maybe you should consider using USB instead of I2C. It offers 480 Mbit/sec speed and the software side has much less overhead than I2C.

You'd need to add a USB hub. If using Teensy 4.0, you'd need to connect the hub's upsteam port to the 2 little pads on the bottom side. USB host is simpler with Teensy 4.1 which has a regular 5 pin header to connect a USB host cable.
All interesting but the OP problem was USBMIDI is to fast for i2c, and yes what I felt was USB MIDI client
Hello guys,
Thank you for all these considerations and sorry for the delay. We moved on other points of the project and don't work full time on it.

Perhaps filter the incoming midi data, keep the latest master values of controllers and only update changes to the slave every ... say 1 ms or send as often as the i2c can handle dependent on how many channels are being changed
Yes we thought about that but MIDI feedback is sometimes a single message, i.e. a single CC value, along a tempest of other CC values, but this one is still essential. And "polling" usbMIDI every 1 ms would take the risk to miss it.
So we thought we could filter each one of the CC values, i.e Ch1 CC0, Ch2 CC1 etc. but that looks terribly laborious.

@oddson I don't know if I follow you well but I trust Paul if he says it is not the problem.

@MatrixRat We don't use MIDI for master - slave communication because encoder values (or whatever controlling parts) won't be the only data to transit on the bus. There will alse be "configuration packets", such as OLED display data.

@PaulStoffregen We didn't think about using USB, that's interesting, given the native hot-swap ability of the USB and as you said its speed !
So you mean :
Master communicates with computer using its main USB device in Serial + MIDI (we use serial for realtime configuration) and with slaves (up to 4 for the purpose of the project) thru USB Host.
These first slaves communicate with master using their main USB device and with other slaves thru their USB Host
Etc. etc.
Am I right ?
So if I carry on my reflections, this should be designed to avoid slaves to be accidentally connected to each other by their main USB device ports ?
Hi Paul and everyone,
I thought about using USB instead of I2C but I still have some questions.
HID or Raw HID are tempting solutions but they are not fast enough. Maximum speed is 64 kB/s => 512 kbits/s whereas I2C fast mode is 1 Mbits/s, and was already not sufficient.
USB serial on Teensy operates at 12 Mbits/sec, so fits better with the use of usbMIDI on the master<>computer side. But apart that, what are the advantages of USB serial on "regular" serial ports ?
Is it designed to detect and avoid packet collision when using multiple devices on a hub ?
Would you recommend an other solution than HID or USB serial ?
Thank you in advance.
Last edited:
Without seeing the code we are talking about, its hard to give precise help, but filtering CC messages is not hard.

Keep an array "byte CC_value[chn][CC]", then when receiving a message (channel,CC,value) test and filter like the following:

if  (CC_value[channel][CC] != value) {  /* only pass on changed values */
   CC_value[channel][CC] = value;        /* update current value */
Hi mlu,
Codes are attached in a .zip in the first post.

However, I think you pointed me in a good way, thanks.
If we don't achieve to use a faster protocol (USB or something else), filter the incoming Midi data could the trick.

Actually, Ableton Live already avoid CC values repetition but I kept your code in case other software don't. Furthermore, I added a timing condition by declaring an "elapsedMillis CC_time[N_CHANNEL][N_CC]" and including a new statement in the if.

if (CC_value[channel][control] != value && CC_timer[channel][control] > CC_TIMEOUT) {  /* only pass on changed values AND timeout overtaking */
    Serial.print("Control Change, ch=");
    Serial.print(channel, DEC);
    Serial.print(", control=");
    Serial.print(control, DEC);
    Serial.print(", value=");
    Serial.println(value, DEC);
    CC_value[channel][control] = value;        /* update current value */
    CC_timer[channel][control] = 0;              /*reset timer*/

I just tested the filtering tonight by printing CC values in serial monitor cause I don't have the whole hardware right now. I will tomorrow.
And all I can say for the moment is that is seems to work. Logically, the timing condition should make the code miss isolated values but I can't observe this phenomenon with relatively low CC_TIMEOUT setting (less than 10 ms).
I would be glad to hear from you if you see any mistaken or possible improvement.
Last edited:
EDIT : hum, I feel like it won't be enough. Cause let's say I sample CC values every 10 ms, things would certainly work better for a moment but then if I increase again the amount of changing CC in Ableton, the problem will show again at some point.
The idea should be to not only sample CC values, but give the feedback data a limit, whatever the number of automations in use.
So I wonder, is there a way to limit the speed of usbMIDI, like step in the library itself ?

And obviously, I should still try to avoid or reduce the bottleneck action of I2C usage.
If you have a number of automations sending conflicting CC values within 10ms then there is a problem with the Ableton setup, and what value this controller really has, and the rest should be dropped, even if all communications are infinitely fast. With the filtering every slave CC will be updated at most every 10ms.

If the CC messages are not conflicting then given 6 encoders per slave, there should be no more than 6 possible CC values per slave to send. There is no information in the master code what messages are of interest to which slave.

Throttling the MIDI, if possible, will simply push this conflict back into Ableton, making Ableton try to send more messages than MIDI can handle, where you have no control of filtering and CC conflict resolution.
Not open for further replies.