SparkFun Forums 

Where electronics enthusiasts find answers.

Questions &/or issues related to products in the Movement & IMU category should be posted in this forum.
#207658
Hello, this is going to be a long post, but I hope someone can help as it is quite specific I guess.

I set up an Arduino Uno with the 8-channel Mux Breakout.
I plugged in 3 IMU with the I2C pullup resistorssolders removed (hookup guide says only one should be present - it is on the Mux Breakout).

Tests on the Arduino alone were perfect - the mux returned every rotation vector succesfully in turn.

I then set up a SerialPort stream reading using System.Thread in Unity, as in this guide :
https://www.alanzucconi.com/2016/12/01/ ... ion/#step1
...which uses https://github.com/scogswell/ArduinoSerialCommand

Basically, I send a command "PING" to Arduino which then returns me the IMU's rotation vectors.
I then process the data and send it the PlayerController.

Here it got spicy - the Arduino gets periodically reset, with no apparent reason.
What I know :
- It works for a certain amount of cycles, then gets reset.
- The thread isn't Stopped/Started again (it would print info each time, and it only does once)
- The port is Open in the ThreadLoop, which is called only once. So it isn't the Unity script that re-opens the port, it comes from the Arduino. but the reset might be caused by Unity (as the Arduino worked well alone).

The SerialPort reader class is as follows :
Code: Select all
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using System.IO.Ports;

public class ArduinoThread : MonoBehaviour
{
    public string port = "COM3";
    public int baudrate = 9600;
    public int timeout = 50;
    public bool looping = true;

    private SerialPort stream;
    private Thread thread;
    private Queue outputQueue; // Unity => Arduino
    private Queue inputQueue; // Arduino => Unity

    public string[] imuData; // Used for separating ID, x, y, z, w values from the single string we get from the Arduino
    private int imuID; // The ID of the IMU we get data from
    private Quaternion imuRotation; // The Quaternion of the IMU we get data from

    private PlayerController player;

    private void Start()
    {
        if(player == null) player = FindObjectOfType<PlayerController>();
        StartThread();
    }

    public void StartThread()
    {
        Debug.Log("Started thread !");
        outputQueue = Queue.Synchronized(new Queue());
        inputQueue = Queue.Synchronized(new Queue());

        thread = new Thread(ThreadLoop);
        thread.Start();
    }

    // ThreadLoop() will execute in an endless while() loop IN PARALLEL, so no freezing.
    public void ThreadLoop()
    {
        Debug.Log("Thread loop called initiated !");
        stream = new SerialPort(port, baudrate);
        stream.ReadTimeout = 50;
        stream.Open();

        // Loop
        while (IsLooping())
        {
            // Send command to Arduino
            if(outputQueue.Count != 0)
            {
                string command = (string) outputQueue.Dequeue();
                WriteToArduino(command);
            }

            // Read from Arduino
            string result = ReadFromArduino(timeout);
            if (result != null) inputQueue.Enqueue(result);
        }
        stream.Close();
    }

    // Call this with desired command as parameter, it will add to Queue. Then ThreadLoop() will handle communication.
    public void SendToArduino(string command)
    {
        outputQueue.Enqueue(command);
    }
    public string GetFromArduino()
    {
        //Debug.Log("Queue count = " + inputQueue.Count);
        if (inputQueue.Count == 0) return null;
        return (string)inputQueue.Dequeue();
    }

    public void WriteToArduino(string message)
    {
        // Send the request
        stream.WriteLine(message);
        stream.BaseStream.Flush();
    }
    public string ReadFromArduino(int timeout = 0)
    {
        stream.ReadTimeout = timeout;
        try
        {
            return stream.ReadLine();
        }
        catch (System.TimeoutException)
        {
            return null;
        }
    }

    private void Update()
    {
        SendToArduino("PING");
        string rawInput;
        rawInput = GetFromArduino();
        if (rawInput != null)
        {
            imuData = new string[] { };
            imuData = rawInput.Split(' ');
            ExtractRotation(imuData); // This simply sends each IMU's ID and Quaternion to PlayerController class.
        }
    }
    private void ExtractRotation(string[] words)
    {
        for (int i = 0; i < words.Length; i++)
        {
            Debug.Log("words is : " + words[i]);
        }
        float id = float.Parse(words[0]);
        imuID = (int)System.Math.Round(id);

        float[] quat = new float[4];
        int x = 0;
        for (int i = 1; i < words.Length; i++)
        {
            if (words[i] != ' '.ToString())
            {
                quat[x] = float.Parse(words[i]);
                x++;
            }
            if (x == 4) break;
        }
        imuRotation = new Quaternion(quat[0], quat[1], quat[2], quat[3]);
        player.imuInputs[imuID] = imuRotation;
    }

    // Loop stop handling
    public bool IsLooping()
    {
        lock (this)
        {
            return looping;
        }
    }
    public void StopThread()
    {
        Debug.Log("Stopped thread !");
        lock (this)
        {
            looping = false;
        }
    }
    private void OnDestroy()
    {
        StopThread();
    }
}
The Arduino code (works without the SerialCommands part for Unity) :
Code: Select all
#include <SerialCommand.h>
#include <Wire.h>
#include "SparkFun_BNO080_Arduino_Library.h"

SerialCommand sCmd;
BNO080 imu;
int imuCount;

void setup()
{  
  imuCount = 0;
  Serial.begin(9600);
  while (!Serial);

  // SerialCommand setup
  sCmd.addCommand("PING", pingHandler);
  sCmd.addCommand("ECHO", echoHandler);
  
  // I2C setup
  Wire.begin();
  Wire.setClock(400000); //Increase I2C data rate to 400kHz

  //Initialize all the sensors
  for (byte x = 0 ; x < 8 ; x++)
  {
    enableMuxPort(x); //Tell mux to connect to port X
    if (imu.begin() == false)
    {
      Serial.println("BNO080 not detected at default I2C address. Skipping this port.");
    }
    else
    {
      imuCount++;
      Serial.println(String("An IMU was detected at port : ") + x + ". IMU count is : " + imuCount);
    }

    imu.enableRotationVector(50); //Send data update every 50ms
    disableMuxPort(x);
  }

  Serial.println("Mux Shield online");
}

void loop()
{
  if (Serial.available() > 0) sCmd.readSerial();

  delay(50);
}

void pingHandler ()
{  
Serial.println();
    for (byte x = 0 ; x < imuCount ; x++)
  {
    enableMuxPort(x); //Tell mux to connect to this port, and this port only
    //Look for reports from the IMU
    if (imu.dataAvailable() == true)
    {
      float quatI = imu.getQuatI();
      float quatJ = imu.getQuatJ();
      float quatK = imu.getQuatK();
      float quatReal = imu.getQuatReal();
      float quatRadianAccuracy = imu.getQuatRadianAccuracy();
      Serial.print(x);
      Serial.print(" ");
      Serial.print(quatI, 2);
      Serial.print(" ");
      Serial.print(quatJ, 2);
      Serial.print(" ");
      Serial.print(quatK, 2);
      Serial.print(" ");
      Serial.print(quatReal, 2);
      Serial.print(" ");
      Serial.print(quatRadianAccuracy, 2);
      Serial.println(); 
    }
    else {
      Serial.println("IMU_NOT_AVAILABLE");
}
    disableMuxPort(x); //Tell mux to disconnect from this port
  }
 
}

void echoHandler ()
{
    // Not used.
}
#207664
As it turns out, the problem was caused on Arduino's script in the loop in pinghandler, where I get through all the ports of the Mux breakout.

When I query all the ports in separate PINGs, I get the data without problem. It is less efficient because every request carries only one IMU's data, but at least it works...

Is there an issue with sending more than one Serial.println() per request?

Hoping to get a more efficient solution sometime (I'll end up with 10 IMU's).
Thank you