Best practices I2C and SPI for MTi 1-series

The MTi 1-series has several hardware interfaces, including I2C and SPI. This page is an introduction on using these interfaces. For programming examples for the MTi 1-series, refer to this article


The MTi at its core has only one command interpreter which is the "Xbus interpreter" as depicted in the figure on page 8 of the MTi 1-series datasheet. No matter what the interface is (UART, SPI or I2C) in the end it is needed to send commands to this interpreter. After sending the command the user waits until the device generates its answer. For the UART interface this is very straightforward, the answer will just arrive on the RX line when ready. For the I2C and SPI the user has to check the output queue of the MTi if it contains anything and if so the user has to read it out, message by message.

As of MT Software Suite version 4.9.1, there are embedded examples available for the MTi 1-series Development Kit that make use of the SPI and I2C interface. The examples and corresponding documentation can be found in your MT Software Suite installation folder at:

C:\Program Files\Xsens\MT Software Suite x.x.x\MT SDK\Examples\embedded_examples

Using the ControlPipe

In order to send commands to the MTi the "ControlPipe" opcode (0x03) is used. The data bytes following are the reduced Xbus message. A reduced Xbus message has the preamble 0xFA and the busId removed. A default busId of 0xFF is assumed for calculating the checksum. For more information on Xbus messages, see the Low-Level Communication Protocol (LLCP) documentation.

Example: if the user wants to request the firmware version of the MTi, the user has to send the ReqFWRev Xbus message. This message has ID 0x12 and a normal full Xbus message would look like this: {0xFA, 0xFF, 0x12, 0x00, 0xEF} and its reduced form is {0x12, 0x00, 0xEF}. So sending this command to the MTi using I2C would entail a single write transfer to slave address 0x6B (default) having payload {0x03, 0x12, 0x00, 0xEF}. For SPI, three additional filler bytes are required: {0x03, 0xFF, 0xFF, 0xFF, 0x12, 0x00, 0xEF}.

Using MeasurementPipe and NotificationPipe

Then the user has to start reading out the output queues of the MTi to retrieve the answer to a request. When using I2C or SPI, the messages in the output queue are accessible through two opcodes. The "MeasurementPipe" (0x06) opcode will only read messages containing measurement data while the "NotificationPipe" (0x05) opcode will read all other messages. In order to know if there is a pending message in either of the pipes the user has to perform a read transfer using the "PipeStatus" (0x04) opcode. The user has to read 4 bytes. These 4 bytes represent two 16bit values which are the sizes of the first pending message in the notification and measurement pipes. If the size is 0 there is no message pending.

Example: if the user reads back {0x7, 0x0, 0x23, 0x01} there is a message of size 0x7 pending in the notification pipe and a message of size 0x123 in the measurement pipe. The next action will be a read transfer from the notification pipe of 7 bytes and after that a read transfer from the measurement pipe of 0x123 (291) bytes. After a message is read from the MTi, the pipestatus will be updated to contain the size of the next queued message or 0 if there are none.

Implementation approach via continuous polling (I2C)

So one approach is to continuously poll the pipe status and if either of the sizes is non-zero read out its corresponding message pipe. In pseudo C:


void i2cReadWithOpcode(uint8_t opcode, uint8_t* destination, uint16_t size)
//Do a I2C write transfer (address 0x6B) for the opcode, do not generate a stop condition
//Generate a repeated start condition
//Do a I2C read transfer for size bytes; store the received bytes to the destination buffer

while (true)
uint8_t buffer[...]
i2cReadWithRegister(OPCODE_PIPESTATUS, buffer, 4);

uint16_t notificationSize = (uint16_t)buffer[0] | ((uint16_t)buffer[1] << 8);
uint16_t measurementSize = (uint16_t)buffer[2] | ((uint16_t)buffer[3] << 8);

if (notificationSize)
i2cReadWithRegister(OPCODE_NOTIFICATIONPIPE, buffer, notificationSize);

if (measurementSize)
i2cReadWithRegister(OPCODE_MEASUREMENTPIPE, buffer, measurementSize);

Implementation approach using the DRDY pin

Instead of continuously polling, it is also possible to connect the DRDY pin which will be high as long as there is a message pending in either of the pipes. Only when the DRDY pin becomes high, the user has to run the code as shown above in the "while (true)" loop. The statement would be "while (DRDY)". In addition the DRDY pin can be used to see that the MTi is ready for communication after a reset (see further in this article). 

Example: How would that look like for the firmware version request? As described earlier the user sends the request to the "ControlPipe" and then waits for the DRDY pin to go high. As soon as it does, the user reads the Status Pipe, followed by either the Notification Pipe or the Measurement Pipe depending on the type of message. The FirmwareRev message (Xbus message ID 0x13) should be available from the notification pipe. A possible FirmwareRev message as read from the notification pipe looks like this:

{0x13, 0x0B, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0xE9, 0xF9, 0xDB}

0x13 = Message ID
0x0B = data length
0x01 = major version
0x01 = minor version
0x01 = patch level
0x00, 0x00, 0x00, 0x23 = build number
0x00, 0x00, 0xe9, 0xf9 = revision number
0xDB = checksum


Additional remarks

The pointers/remarks below are not necessary for the communication but can be helpful to implement good working code. 

a) When the MTi is reset it takes about 2 seconds to boot and be responsive. During this period the I2C interface will not be available. When done booting the MTi will generate a WakeUp (0x3E) Xbus message in the notification pipe. Because of this the DRDY line will go high. This makes the DRDY an indicator to determine if the MTi is ready for communication. This also means that the first message read from the notification pipe after reset will always be the WakeUp message.

b) The default MTi configuration will switch the MTi automatically to measurement mode 500ms after generating the WakeUp message. From this point on the MTi will start generating messages that end up in the measurement pipe. If the user does not read out the measurement pipe in time the MTi will detect a queue overflow and as a consequence will generate data overflow error (0x42) messages on the notification pipe. In this case, the measurement pipe acts as a FIFO buffer: the oldest messages will remain available until they are read out by the user, whereas new incoming messages are dropped. If the user ends up in that situation and sends for example a ReqFWRev message, a couple of these error messages have to be read out from the noticification pipe at first before the answer to the intended request appears. To prevent the MTi from going to Measurement Mode, send the WakeUpAck message (0x3F) within 500ms after the WakeUp has been generated. It is also possible to use a setting that prevents the MTi 1-series from going to Measurement Mode automatically. The message SetOptionFlags (MID 0x48) allows for setting the DisableAutoMeasurement flag. If set the MTi stays in Config Mode until the user sends the command GoToMeasurement (0x10). See the Low-Level Communication Protocol (LLCP) documentation for more details on using this message. 


Was this article helpful?
1 out of 1 found this helpful
Do you have a question? Please post your question in our Community Forum