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 :
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) :
#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.
}