Wixel performance with wireless i2c

Dear All!

I am working with wixel to read values from minimu 9 v5 (x6) and the time spent for reading data is too high :frowning:
I send requests to the wixel 1 using serial port, it sends those messages to wixel 2 which is connected to multiplexer to which 6 imu sensors are connected. So here I have two apps: wireless serial and serial to i2c. The rate of reading data is too low: almost 1 second to read 9 readings from each of 6 sensors. I cannot use threads because it is physically impossible to send the data in such a way. Is there any way to improve performance without editing the apps or using more hardware units (wixels, mux, etc)?

Thank you in advance!

Hello.

The best way to optimize your system would probably be to develop a new app to replace the Wixel’s Serial-to-I²C app. It should know how to read from the six MinIMU-9 v5 boards, so you could just send one byte to trigger all of those readings, and then get all the readings back at once.

However, since you said you do not want to edit the apps, here are some other ideas.

From the latest code you posted in your previous topic, it seems like you are reading one byte at a time from the sensors. With your six MinIMU-9 v5 boards, you need to read 108 bytes to get all the data (2 bytes per reading, 3 readings per sensor, 3 sensors per board, 6 boards). You should be able to speed up the system a lot by reading multiple bytes at a time. The documentation of the Serial-to-I²C app in the Wixel user’s guide explains how to read more than one byte at a time.

The LIS3MDL magnetomer output registers are all consecutive, so you can read all of them by doing a single 6-byte read starting at the lowest output register address. Make sure you add 128 to the register address byte that you transmit to the LIS3MDL (i.e. you need to set its most-significant bit) in order to tell the LIS3MDL that you want to automatically increment the address each time you read a byte. Otherwise, you will end up reading the same byte 6 times, instead of reading 6 consecutive bytes.

Similarly, the LSM6DS33 gyro and accelerometer output registers are at consecutive addresses, so you can just do a single 12-byte read to get all the gyro and accelerometer data from a single board. To get auto-increment working on the LSM6DS33, you should make sure that the IF_INC bit in the CTRL3_C register is 1. That is the default value, so just make sure you are not changing it.

Once you make these changes, you should only be writing to the serial port three times per MinIMU-9 v5 board: once to change the I²C mux, once to read from the LSM6DS33, and once to read from the LIS3MDL. That should make things a lot faster.

At that point, if your system still is not fast enough, I would suggest combining those serial port writes together. In other words, you can try concatenating all the commands for one board together, and then send them all at once, with one call to the SerialPort.Write method. You can also go further and try combining the commands for multiple boards together. At some point, if you combine together too many commands then you might end up filling up all the buffers between your C# program and the Wixel running the Serial-to-I²C app, and the write will never complete, but I suspect that won’t be an issue for you.

Another thing to do is to increase the I2C_freq_kHz parameter on the Wixel running the Serial-to-I²C app, if you have not done so yet. The chips on the MinIMU-9 v5 are capable of handling bit rates up to 400 kHz, while the default value for that parameter is 100 kHz.

Please let me know if you have any further questions.

–David

Thank you very much! I will try the fastest method by trying to write the app. If I fail I will just use the method proposed by you!

Hi David!

I apologize for asking questions again. I wrote a wixel app that reads values from minimu 9 v5 and sends them in array via usb com port. However, I found that reading are quite strange: accelerometer values are way too different from what they should be (1g along z axis and 0 along others). It even seems like somehow magnetometer and accelerometer values took each others place :smiley: . Can you please help me to find what may cause that problem? I am afraid that the way of sending the data is not very good in my code.

Here is the wixel app code:

/** Dependencies **************************************************************/
#include <cc2511_map.h>
#include <board.h>
#include <random.h>
#include <time.h>
#include <usb.h>
#include <usb_com.h>
#include <i2c.h>
#include <stdio.h>

#define LSM6DS33 0xD6
#define CTRL1_XL 0x10
#define CTRL2_G 0x11
#define CTRL3_C 0x12
#define OUTX_L_G 0x22
#define OUTX_H_G 0x23
#define OUTY_L_G 0x24
#define OUTY_H_G 0x25
#define OUTZ_L_G 0x26
#define OUTZ_H_G 0x27
#define OUTX_L_XL 0x28
#define OUTX_H_XL 0x29
#define OUTY_L_XL 0x2A
#define OUTY_H_XL 0x2B
#define OUTZ_L_XL 0x2C
#define OUTZ_H_XL 0x2D

#define LIS3MDL 0x3C
#define CTRL1 0x20
#define CTRL2 0x21
#define CTRL3 0x22
#define CTRL4 0x23
#define CTRL5 0x24
#define OUTX_L 0x28
#define OUTX_H 0x29
#define OUTY_L 0x2A
#define OUTY_H 0x2B
#define OUTZ_L 0x2C
#define OUTZ_H 0x2D

uint8 XDATA response[18];
uint8 startReading=0;
uint8 returnResponse=0;

void updateLeds(void)
{
    usbShowStatusWithGreenLed();

    //LED_YELLOW(vinPowerPresent());
    //LED_RED(errors);
}

void i2cMux(uint8 channel)
{
	uint8 address=0xE0;
	i2cStart();
	i2cWriteByte(address);
	i2cWriteByte(1<<channel);
	i2cStop();
}

void i2cWrite(uint8 deviceAddress, uint8 regAddress, uint8 controlWord)
{
	i2cStart();
	i2cWriteByte(deviceAddress);
	i2cWriteByte(regAddress);
	i2cWriteByte(controlWord);
	i2cStop();
}

uint8 i2cRead(uint8 deviceAddress, uint8 regAddress)
{
	i2cStart();
	i2cWriteByte(deviceAddress);
	i2cWriteByte(regAddress);
	i2cStart();
	i2cWriteByte(deviceAddress | 1);
	i2cStop();
	return i2cReadByte(1);
}

void i2cInit(uint8 channel)
{
	i2cMux(channel);
	i2cWrite(LSM6DS33, CTRL1_XL, 0x80);
	i2cWrite(LSM6DS33, CTRL2_G, 0x80);
	i2cWrite(LSM6DS33, CTRL3_C, 0x04);
	i2cWrite(LIS3MDL, CTRL1, 0x70);
	i2cWrite(LIS3MDL, CTRL2, 0x00);
	i2cWrite(LIS3MDL, CTRL3, 0x00);
	i2cWrite(LIS3MDL, CTRL4, 0x0C);
}

void i2cService(uint8 channel)
{	
	i2cMux(channel);
	response[0] = i2cRead(LSM6DS33,OUTX_L_G);
	response[1] = i2cRead(LSM6DS33,OUTX_H_G);
	response[2] = i2cRead(LSM6DS33,OUTY_L_G);
	response[3] = i2cRead(LSM6DS33,OUTY_H_G);	
	response[4] = i2cRead(LSM6DS33,OUTZ_L_G);
	response[5] = i2cRead(LSM6DS33,OUTZ_H_G);	
	
	response[6] = i2cRead(LSM6DS33,OUTX_L_XL);
	response[7] = i2cRead(LSM6DS33,OUTX_H_XL);
	response[8] = i2cRead(LSM6DS33,OUTY_L_XL);
	response[9] = i2cRead(LSM6DS33,OUTY_H_XL);	
	response[10] = i2cRead(LSM6DS33,OUTZ_L_XL);
	response[11] = i2cRead(LSM6DS33,OUTZ_H_XL);	
	
	response[12] = i2cRead(LIS3MDL,OUTX_L);
	response[13] = i2cRead(LIS3MDL,OUTX_H);
	response[14] = i2cRead(LIS3MDL,OUTY_L);
	response[15] = i2cRead(LIS3MDL,OUTY_H);
	response[16] = i2cRead(LIS3MDL,OUTZ_L);
	response[17] = i2cRead(LIS3MDL,OUTZ_H);
}

void main(void)
{
	systemInit();
	usbInit();
	
	i2cPinScl = 10;
	i2cPinSda = 11;
	
	i2cSetFrequency(300);
	i2cSetTimeout(10);
	i2cInit(0);
	while (1)
	{
		boardService();
		updateLeds();
		usbComService();
		if(usbComRxAvailable() && returnResponse == 0){
			startReading = usbComRxReceiveByte();
		}
		if(startReading == 1 && returnResponse == 0)
		{
			i2cService(0);
			returnResponse = 1;
			startReading = 0;
		}
		if(usbComTxAvailable() && returnResponse == 1){
			int i;
			for(i=0;i<18;i++){
				usbComTxSendByte(response[i]);
			}
			returnResponse=0;
		}
	}
}

Here is my c# code on unity:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System;

public class reader : MonoBehaviour {
	private SerialPort port;
	private string port_name;
	private float GyroSens, AcceSens, MagnSens;
	public MadgwickAHRS filt;
	public float q0, q1, q2, q3;
	public reader(){
		string header_to_file = @"\\.\";
		port_name=header_to_file+"COM20";
		port = new SerialPort(port_name, 9600, Parity.None, 8, StopBits.One); // port settings
		port.ReadTimeout = 100;
		port.Handshake = Handshake.None;
		port.Open();
		port.DiscardOutBuffer();
		port.DiscardInBuffer();
		GyroSens = 8.75f / 1000f;
		GyroSens = GyroSens * 3.14159265359f / 1000f;
		AcceSens = 0.061f / 1000f;
		MagnSens = 0.00014615609f;
		filt = new MadgwickAHRS (0.01f,0.03f);
	}
	public string readSerial(){
		float[] data = new float[9];
		int value;
		byte[] temp=new byte[18];
		port.Write (new byte[]{1},0,1);
		for (int i = 0; i < 18; i++) {
			temp [i] = (byte)port.ReadByte ();
		}
		for (int i = 0; i < 9; i++) {
			if (i >= 0 && i <= 2) {
				data [i] = ((float)((short)(temp [i * 2] | temp [i * 2 + 1] << 8)));
				//Debug.Log ("GYRO RAW: " + i + ", " + data [i]);
				//data [i] = GyroSens*data [i];
				if (data [i] < 0.01f && data [i] > -0.01f) {
					data [i] = 0;
				}
			} else if (i >= 3 && i <= 5) {
				data [i] = ((float)((short)(temp [i * 2] | temp [i * 2 + 1] << 8)));
				//Debug.Log ("ACCE RAW: " + i + ", " + data [i]);
				//data [i] = AcceSens * data [i];
			} else {
				data [i] = ((float)((short)(temp [i * 2] | temp [i * 2 + 1] << 8)));
				//Debug.Log ("MAGN RAW: " + i + ", " + data [i]);
				//data [i] = MagnSens * data [i];
			}
		}
		string str = "GYRO:" + data [0] + ", " + data [1] + ", " + data [2];
		str = str + "\nACCE: " + data [3] + ", " + data [4] + ", " + data [5];
		str = str + "\nMAGN: " + data [6] + ", " + data [7] + ", " + data [8];
		/*filt.Update (data [0], data [1], data [2], data [3], data [4], data [5], data [6], data [7], data [8]);
		q0 = filt.Quaternion [0];
		q1 = filt.Quaternion [1];
		q2 = filt.Quaternion [2];
		q3 = filt.Quaternion [3];
		str = str + "\nQUAT: " + q0 + "," + q1 + ", " + q2 + ", " + q3;*/
		Debug.Log (str);
		System.Threading.Thread.Sleep (10);
		return "";
	}
}

Here is screenshot of output of unity script:

If you want to call usbComTxSendByte 18 times, you need to first ensure that usbComTxAvailable() returns 18 or more. Right now, all your code does is check to make sure that it returned a non-zero value. I recommend changing the first line of your last if statement to:

if (usbComTxAvailable() >= 18 && returnResponse == 1) {

If you are still having problems after that, I recommend making two changes to test that the USB communication itself is working:

  • On the Wixel, after calling i2cService, write some known, constant data into your response array. I suggest writing something like 128 and 129 to the first two bytes, and 254 and 255 to the last two bytes.
  • On the C# side, in readSerial, change your call to Debug.Log so that it simply prints out all 18 bytes from the temp array.

Now you can look at that debug console to see what bytes the C# side is receiving, and what bytes they correspond to in the response buffer. That might reveal communication issues. If not, then you would need to look more carefully at the I2C code and the processing code on the C# side.

Please let me know if you need further help troubleshooting or have other questions.

–David

Thank you very much for your response!

I tried your method and now I know that data is sent well but the readings themselves are strange. When i flip IMU upside down accelerometer starts showing correct value (about 1g I suppose) in z axis. The same with x and y axes. One side shows values while another output is wrong. Seems like the reason should be in fusion of received bytes or in IMU itself, but earlier it was working well on with previous method (using serial to i2c app). In my previous code I used the same method to convert (binary or with cast to short type). If you have any idea why this method does not work, please help :sob:
Meanwhile I will try to find the problem myself
Here is reading when IMU is upside down:

Now codes are like these:
Wixel:

/** Dependencies **************************************************************/
#include <cc2511_map.h>
#include <board.h>
#include <random.h>
#include <time.h>
#include <usb.h>
#include <usb_com.h>
#include <i2c.h>
#include <stdio.h>

#define LSM6DS33 0xD6
#define CTRL1_XL 0x10
#define CTRL2_G 0x11
#define CTRL3_C 0x12
#define OUTX_L_G 0x22
#define OUTX_H_G 0x23
#define OUTY_L_G 0x24
#define OUTY_H_G 0x25
#define OUTZ_L_G 0x26
#define OUTZ_H_G 0x27
#define OUTX_L_XL 0x28
#define OUTX_H_XL 0x29
#define OUTY_L_XL 0x2A
#define OUTY_H_XL 0x2B
#define OUTZ_L_XL 0x2C
#define OUTZ_H_XL 0x2D

#define LIS3MDL 0x3C
#define CTRL1 0x20
#define CTRL2 0x21
#define CTRL3 0x22
#define CTRL4 0x23
#define CTRL5 0x24
#define OUTX_L 0x28
#define OUTX_H 0x29
#define OUTY_L 0x2A
#define OUTY_H 0x2B
#define OUTZ_L 0x2C
#define OUTZ_H 0x2D

uint8 XDATA response[18];
uint8 startReading=0;
uint8 returnResponse=0;

void updateLeds(void)
{
    usbShowStatusWithGreenLed();

    //LED_YELLOW(vinPowerPresent());
    //LED_RED(errors);
}

void i2cMux(uint8 channel)
{
	uint8 address=0xE0;
	i2cStart();
	i2cWriteByte(address);
	i2cWriteByte(1<<channel);
	i2cStop();
}

void i2cWrite(uint8 deviceAddress, uint8 regAddress, uint8 controlWord)
{
	i2cStart();
	i2cWriteByte(deviceAddress);
	i2cWriteByte(regAddress);
	i2cWriteByte(controlWord);
	i2cStop();
}

uint8 i2cRead(uint8 deviceAddress, uint8 regAddress)
{
	i2cStart();
	i2cWriteByte(deviceAddress);
	i2cWriteByte(regAddress);
	i2cStart();
	i2cWriteByte(deviceAddress | 1);
	i2cStop();
	return i2cReadByte(1);
}

void i2cInit(uint8 channel)
{
	i2cMux(channel);
	i2cWrite(LSM6DS33, CTRL1_XL, 0x80);
	i2cWrite(LSM6DS33, CTRL2_G, 0x80);
	i2cWrite(LSM6DS33, CTRL3_C, 0x04);
	i2cWrite(LIS3MDL, CTRL1, 0x70);
	i2cWrite(LIS3MDL, CTRL2, 0x00);
	i2cWrite(LIS3MDL, CTRL3, 0x00);
	i2cWrite(LIS3MDL, CTRL4, 0x0C);
}

void i2cService(uint8 channel)
{	
	i2cMux(channel);
	response[0] = i2cRead(LSM6DS33,OUTX_L_G);
	response[1] = i2cRead(LSM6DS33,OUTX_H_G);
	response[2] = i2cRead(LSM6DS33,OUTY_L_G);
	response[3] = i2cRead(LSM6DS33,OUTY_H_G);	
	response[4] = i2cRead(LSM6DS33,OUTZ_L_G);
	response[5] = i2cRead(LSM6DS33,OUTZ_H_G);	
	
	response[6] = i2cRead(LSM6DS33,OUTX_L_XL);
	response[7] = i2cRead(LSM6DS33,OUTX_H_XL);
	response[8] = i2cRead(LSM6DS33,OUTY_L_XL);
	response[9] = i2cRead(LSM6DS33,OUTY_H_XL);	
	response[10] = i2cRead(LSM6DS33,OUTZ_L_XL);
	response[11] = i2cRead(LSM6DS33,OUTZ_H_XL);	
	
	response[12] = i2cRead(LIS3MDL,OUTX_L);
	response[13] = i2cRead(LIS3MDL,OUTX_H);
	response[14] = i2cRead(LIS3MDL,OUTY_L);
	response[15] = i2cRead(LIS3MDL,OUTY_H);
	response[16] = i2cRead(LIS3MDL,OUTZ_L);
	response[17] = i2cRead(LIS3MDL,OUTZ_H);
}

void main(void)
{
	systemInit();
	usbInit();
	
	i2cPinScl = 10;
	i2cPinSda = 11;
	
	i2cSetFrequency(300);
	i2cSetTimeout(10);
	i2cInit(0);
	while (1)
	{
		boardService();
		updateLeds();
		usbComService();
		if(usbComRxAvailable() && returnResponse == 0){
			startReading = usbComRxReceiveByte();
		}
		if(startReading == 1 && returnResponse == 0)
		{
			i2cService(0);
			response[0]=128;
			response[1]=129;
			response[16]=254;
			response[17]=255;
			returnResponse = 1;
			startReading = 0;
		}
		if(usbComTxAvailable()>=18 && returnResponse == 1){
			int i;
			for(i=0;i<18;i++){
				usbComTxSendByte(response[i]);
			}
			returnResponse=0;
		}
	}
}

Unity:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System;

public class reader : MonoBehaviour {
	private SerialPort port;
	private string port_name;
	private float GyroSens, AcceSens, MagnSens;
	public MadgwickAHRS filt;
	public float q0, q1, q2, q3;
	public reader(){
		string header_to_file = @"\\.\";
		port_name=header_to_file+"COM20";
		port = new SerialPort(port_name, 9600, Parity.None, 8, StopBits.One); // port settings
		port.ReadTimeout = 100;
		port.Handshake = Handshake.None;
		port.Open();
		port.DiscardOutBuffer();
		port.DiscardInBuffer();
		GyroSens = 8.75f / 1000f;
		GyroSens = GyroSens * 3.14159265359f / 1000f;
		AcceSens = 0.061f / 1000f;
		MagnSens = 0.00014615609f;
		filt = new MadgwickAHRS (0.01f,0.03f);
	}
	public string readSerial(){
		float[] data = new float[9];
		int value;
		byte[] temp=new byte[18];
		port.Write (new byte[]{1},0,1);
		for (int i = 0; i < 18; i++) {
			temp [i] = (byte)port.ReadByte ();
			Debug.Log ("Bytes Received - " + i + ": " + temp [i]);
		}
		for (int i = 0; i < 9; i++) {
			if (i >= 0 && i <= 2) {
				data [i] = ((float)((short)(temp [i * 2] | temp [i * 2 + 1] << 8)));
				//Debug.Log ("GYRO RAW: " + i + ", " + data [i]);
				//data [i] = GyroSens*data [i];
				if (data [i] < 0.01f && data [i] > -0.01f) {
					data [i] = 0;
				}
			} else if (i >= 3 && i <= 5) {
				data [i] = ((float)((short)(temp [i * 2] | temp [i * 2 + 1] << 8)));
				//Debug.Log ("ACCE RAW: " + i + ", " + data [i]);
				//data [i] = AcceSens * data [i];
			} else {
				data [i] = ((float)((short)(temp [i * 2] | temp [i * 2 + 1] << 8)));
				//Debug.Log ("MAGN RAW: " + i + ", " + data [i]);
				//data [i] = MagnSens * data [i];
			}
		}
		string str = "GYRO:" + data [0] + ", " + data [1] + ", " + data [2];
		str = str + "\nACCE: " + data [3] + ", " + data [4] + ", " + data [5];
		str = str + "\nMAGN: " + data [6] + ", " + data [7] + ", " + data [8];
		/*filt.Update (data [0], data [1], data [2], data [3], data [4], data [5], data [6], data [7], data [8]);
		q0 = filt.Quaternion [0];
		q1 = filt.Quaternion [1];
		q2 = filt.Quaternion [2];
		q3 = filt.Quaternion [3];
		str = str + "\nQUAT: " + q0 + "," + q1 + ", " + q2 + ", " + q3;*/
		Debug.Log (str);
		System.Threading.Thread.Sleep (10);
		return "";
	}
}

An update. I checked received bytes 10 and 11 (L and H bits) with online 2’s complement to decimal converters. They showed the same value as my C# script. So if the method of bytes fusion is correct, I have no any idea about what is wrong here :frowning:
Also, i tried another minimu 9 v5 and the result is the same.

An update again :slight_smile:
Now everything works well. The problem was in those lines:

uint8 i2cRead(uint8 deviceAddress, uint8 regAddress)
{
	i2cStart();
	i2cWriteByte(deviceAddress);
	i2cWriteByte(regAddress);
	i2cStart();
	i2cWriteByte(deviceAddress | 1);
	temp = i2cReadByte(1);
	i2cStop();
	return temp;
}

My mistake was that previously I called i2cStop() before i2cReadByte() byte :slight_smile:
Sorry for inconvenience! Thanks for your help!

Hi again, David!

Is it possible to use multiple threads in wixel? Did not find such topics here. I wanted to use multiple threads for multiple sensors.

Thanks!

We do not have any experience with using threads on the Wixel, but you might be able to find a real-time operating system (RTOS) that works on it by searching for “rtos 8051” or “rtos mcs51”. I suspect the hassle and added complexity of integrating a real-time operating system will not be worth it. The CC2511F32 microcontroller on the Wixel can only execute one instruction at a time (it’s single-core). Switching between threads (context switching) takes some time.

If you are still reading one byte at a time as shown in the i2cRead function you posted, then reading 6 bytes or 12 bytes at a time as I suggested in my earlier post would be a good way to speed up the readings.

–David