A Fast(er) interface for Arduino from Matlab
I recently had to setup an experimental rig with an interface between Matlab and an Arduino. The goal of the interface was to read out the position of a treadmill using a rotary encoder. This must be a pretty common situation in neuroscience these days, but my first foray into setting it up (through the official matlab arduino toolbox) was way too slow for any reasonable experiment. I figured I’d write up my notes on the process here. If you know a better way to do this, please email or message me!
Approach 1: Matlab arduino toolbox
The matlab toolbox is pretty easy to use. We need a rotary encoder to read out the turns of the treadmill, and that’s already built in as toolbox (again, figure this is a pretty common task). The code to connect and setup the rotary encoder look like this:
The encoder uses digital inputs 2 and 3.
Great. Simple enough. But is it fast enough? Our experiment code runs visual stimuli at 120 or 240Hz. That means we have to complete all analyses of behavior within ~7 or ~3.5 ms before we absolutely have to draw the stimuli. These numbers shrink if the stimuli are really complicated. All time matters (e.g., communicating with the syringe pump to send reward takes ~0.5 ms).
To compute the arduino toolbox call time, I just ran the main call 1000 times in a for loop and measured the elapsed time. The code looks like this:
Pretty straightforward. And the output?
Median Duration Arduino Toolbox = 8.07 [7.89, 8.23] ms
This just won’t work. Matlab hangs for ~8ms each time the toolbox calls. At that rate, we’d drop every single frame of the experiment. This would be possible at a 60Hz refresh rate, but that’s just too slow for reasonable motion stimuli. I searched the Mathworks forums and Arduino forums and there’s a lot of chatter about how slow this is, but also a lot of insistance that “this is just how fast you can run over USB”. That can’t be right. And it isn’t. There’s a faster way…
Approach 2: IOPort serial connection
Psychtoolbox has a mex function called IOPort that supports connections over a serial port (which can work over USB, of course). This means we’d need to run all rotary encoding on the Arduino as a sketch and only use Matlab to communicate to a serial buffer.
Arduino sketches are easy enough to write and you can find plenty of code snippets online. Here’s the steps of the code I’m using. I based it off a snippet I got from Jack Liska in Huklab.
The code sketch has 4 parts:
main loop, and
rotary encoder function
We use the
digitalWriteFast library which supposedly offers substantial speedup over the default
digitalWrite function in the default Arduino library. All variables are setup here:
Then we setup the serial buffer and initialize the encoder pins. The rotary encoder operates as an interrupt that responds to the rising edge of the digital inputs.
The main loop just dumps the current time and encoder count to the serial buffer. It also listens for a “reset” command to reset the counter, which is necessary so that our count doesn’t exceed the bitdepth of the
long type we’re storing them as.
The loop depends on the encoder to detect digital ins and count them.
It’s pretty straightforward. The output lools something like this. So these are the lines we need to read.
Back in matlab, we can setup our serial connection to the arduino like this:
We need to parse the buffer to make sense of the encoded values. I used regexp to parse the “time” and “count” keywords. There must be a smarter way to read from the buffer, but I wanted to do it as fast as possible.
After all this, we can see how fast the calls are.
Median Duration IOPort = 0.59 [0.58, 0.59] ms
This is a huge speed up! We went from 8ms to 0.5ms and we are getting the correct encoder values. However, useing this approach, we miss a fraction of the samples because the buffer is only partially full and the keywords can be missed. I haven’t fully debugged how to improve this yet, but now we can read from the treadmill online without dropping frames.