It’s a story from 2017 but it’s never too late to share. I worked with a group of human anatomy specialists from medical university. We often need to 3D scan human body parts; now mainly for uses in our VR experiments. Since 2011 they used NextEngine 3D scanner. It had it’s uses but for medical stuff it was really hard to get good results. In particular it often produced horrible textures, was too slow for high quality scans and produced meshes required immense post processing work. Also the software was unnecessarily simplified and was rarely helpful. You know the type. Sometimes it was good for scanning simple bones but not much else. We tried few handheld scanner but we started to get much better results with photogrammetry using RealityCapture. So we moved to new workflows but we still needed a device to hold and manipulate scanned objects. It so happens that for many things NextEngine MultiDrive was quite good.
The problem was that it connected directly to the scanner and was controlled by their software, so for us it was very uncomfortable to use. I decided to do something about it.
The result
To keep the long story short I eventually managed to control the device from Arduino and enclosed it in small package with quite a few options. Below I described steps I took to reach that point.
If you came here for the meat, you can jump straight to the code and the wiring diagram (drawing) of the RJ11 connector. If you want to replicate my result, please be advised that you may potentially brick the device. In particular make sure that the 15V and ground wires are connected correctly.
How I got there
First off I decided to work on AutoDrive since it uses the same physical interface as MultiDrive but it’s cheaper. I figured that if I break something it would be easier to replace. It’s also simpler since it has only one axis. Also, since AutoDrive uses the same physical interface as MultiDrive, the protocol would probably be similar.
I was afraid I was not up to the task, since the whole device and software felt very closed and I never hacked anything like that.
Wiring and voltage levels
AutoDrive and MultiDrive are designed to be connected directly to the scanner using RJ11 cable (6P4C). Since it only has 4 wires I figured that 1 wire must be for ground, 1 would carry the current for driving the motors and 2 other wires would be some kind of data lines, which turned out to be correct.
First off I cut the cable in half and passed it through the breadboard so I could listen to what’s going while the scanning software was running.
Using a multimeter I found a wire is for ground, and that there is a constant voltage of 15V on the other wire. I listened to the other two wire with an oscilloscope. It turned out that the device uses 3.3V singals for other two wires. It was the first time I’ve ever had a chance to use an osciloscope and it was real fun learning the basics.
One of the signals looked like a series of bytes and the other like a clock. Here’s what I assumed about the wires at this point.
These label wires later turned out to be correct.
Low level protocol
After just a few minutes of looking at different time slices of these signals I understood that the protocol they used is not trivial. I just hoped that it wouldn’t be encrypted (it wasn’t). While talking about it with my well informed friend, the phrase logic analyzer appeared few times. So I borrowed one.
It turned out that the device uses I2C to transfer the bits but at very low bitrate (much lower than standard 100kbps).
My intention was to control the motors and not specificaly to crack the details of the protocol. I assumed that I need to know how to initialize the device and how to move it by a bit left and right. Later it turned out that the MultiDrive device uses the same protocol to move both axes but with different I2C address.
So I started to record multiple sequences of bytes running though the data line, while manually controlling the drives with the scanning software. I repeated each action multiple times and hoped that the repeated sequences of bytes would appear in the recordings the same number of times I performed each action. This strategy worked like charm.
At first glance I was put off by how complicated the conversation looked. Scanner and both devices turned out to be much more talkative than I expected. I saved sequences to CSV files and planed to write some script to find the repeating sub-strings… but I was too lazy for that. I figured that repeating sequences are often easy to spot visually, so I just plotted the bytes against time and located many repeating substrings. That helped me to roughly see where to look for interesting pieces. Here’s what I found.
The commands
Each axis uses different I2C address. For single axis of AutoDrive and first axis of MultiDrive (pointing up) uses the address 26. And for the second axis of MultiDrive (tilt axis) the address is 27.
The sequences for moving both axes are the same. Here’s the 13 byte sequance sent from the scanner to move the motor 3 degress in one direction:
byte left[13] = {95, 1, 0, 8, 92, 214, 252, 255, 255, 16, 39, 21, 2};
and to move by 3 degress in the other direction:
byte right[13] = {95, 1, 0, 8, 95, 42, 3, 0, 0, 16, 39, 21, 2};
After that the drive responds to the scanner with sequence of 35 bytes. I didn’t try to crack what’s inside those bytes, since rotation by 3 degress was exactly what I needed. So I stopped there. There’s a little more to moving the motors.
These sequences look very similar to eachother. Number 92 and 95 at position 4 may encode direction bur may also be some error detection code or something completely different. And the following 4 bytes after that, from index 5 up to index 8 looks like 32bit signed little-endian word in one direction, are (214,252,255,255) which translates to -810, and for the other direction (42, 3, 0, 0) which translates to 810. 810 is divisble by 3 degrees, and it comes down to 270 per 1 degree.
I peeked inside the AutoDrive. There’s a servo motor and an optical encoder, so it can precisely control the movement and know how far it managed to move. So the response probably contains some information on that. Numbers 810 and -810 can’t be an accident and probably correspond to number of servomotor ticks the device is order to go. Although the scanning software in manual mode allows only for movement in discrete steps multiply of 3 degrees but it’s probable that by changing these values one can change extent of this movement. I haven’t checked that. It may be that the values 92 and 95 contain some checksum of the whole sequence, but I also didn’t have time to check that.
Controlling from Arduino
I decided to use Arduino Nano as a controller. Only because that’s what I had laying around and it’s quite small but not too small. Since it runs on 5V logic for SDA and SCL lines I had to use 5V-3.3V bi-directional logic level converter (it was the cheap Chinese kind and it worked fine).
From the software side, the only thing one has to do during the initalization is setting the clock speed of I2C line. I already mentioned that it doesn’t seem to work when its too high. I had to go as low as 25000/4 for stable operation. Later I tried talking to the device at higher speeds but it didn’t work. So here’s the initalization code:
#include <Wire.h>
void setup() {
Wire.setClock(25000/4);
}
And here’s the control code:
byte left[13] = {95,1,0,8,92, 214,252,255,255,16, 39,21,2};
byte right[13] = {95,1,0,8,95, 42, 3, 0, 0,16, 39,21,2};
void move(int axis, int dir) {
Wire.beginTransmission(axis == 0 ? 26 : 27);
delayMicroseconds(236);
Wire.write(dir < 0 ? left : right, 13);
Wire.endTransmission();
// read the response (35 bytes)
Wire.requestFrom(axis == 0 ? 26 : 27, 35);
delayMicroseconds(200);
while (Wire.available()) {
Wire.read();
}
}
Argument axis
is used for selecting the axis. Value of 0 selects the first axis MultiDrive and only axis on AutoDrive and any other value selects second axis of MultiDrive. Argument dir
is for setting the direction of movement; any negative value for movement to the left and positive for movement to the right.
The control unit needs quite some time to process and execute these commands. That’s why I had to introduce these delays in the code above. Lower delay values resulted in motor not performing the request.
Example call that orders the first motor to move one step left:
move(0, -1);
The box
The last step was quite straightforward. I decided to put everything in a small box with a joystick for menus and manual positioning. I measured the current draw and bought an appropriate 15V DC power supply. I wanted to power the arduino and LCD from the same power supply so I had to use 5V voltage regulator. Here’s the completed device:
And here are the guts of the box:
And here’s the red board from the other side:
As you can see I didn’t solder the Arduino since I expected to make future changes to the device and maybe fix something if it didn’t work.
As soon as I finished putting it all together, I sent it to my colleagues who were waiting for it.
Epilogue
I planned to add a connector for external trigger to continue the motion sequance but I never had a chance to work on it again. I only got reports that it worked fine and did what they needed. I heard that after about a year, they decided to buy a commercial mount, better suited for photogrammetry workflows.
It was very satisfying to work on that problem and eventually have it moving. It was of low-priority and I treated it as more of a fun side-project/challenge. It gave me a good reason to learn the basics of using logic analyzer and oscilloscope. It also helped me understand how cool these tools are and after completing the box I actually convinced myself to finally purchase my first oscilloscope.