- Home /
Arduino with Unity: Bad Framerate!
So im trying to send the data from my Arduino which is using a HC-SR04 Ultrasonic Distance sensor. It is clearly sending it but the problem is that im averaging roughly 2.0 frames per second... I have no idea why. Im at 9600 Baud, on Com 3. Heres the code for Unity:
using UnityEngine;
using System.Collections;
using System.IO.Ports;
public class ArduinoConnect : MonoBehaviour {
SerialPort stream= new SerialPort("COM3", 19200);
string distance;
// Use this for initialization
void Start () {
stream.Open();
}
// Update is called once per frame
void Update () {
distance= stream.ReadLine();
Debug.Log(distance);
}
}
Heres the code I uploaded to my arduino:
#include <NewPing.h>
#define TRIGGER_PIN 12
#define ECHO_PIN 11
#define MAX_DISTANCE 200
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
void setup() {
Serial.begin(19200);
}
void loop() {
delay(50);
int uS = sonar.ping_median(10);
Serial.println(uS / US_ROUNDTRIP_CM); //prints the distance in cm
delay(30);
}
Answer by dubbreak · Feb 16, 2013 at 05:57 AM
The issue is you are trying to read the serial port too often and Readline is a blocking read (synchronous and it doesn't return until it either gets characters or times out in which case it throws a TimeOutException).
There are a few ways to solve this. Personally I'd use the event DataReceived so you don't have to deal with timeout exceptions.
Something like
using UnityEngine;
using System.Collections;
using System.IO.Ports;
public class ArduinoConnect : MonoBehaviour {
SerialPort stream= new SerialPort("COM3", 19200);
string distance;
// Use this for initialization
void Start () {
try{
stream.Open();
stream.DataReceived += DataReceivedHandler;
}
catch(Exception e){
Debug.Log("Could not open serial port: " + e.Message);
}
}
private void DataReceivedHandler(
object sender,
SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string distance = sp.ReadLine();
Debug.Log(distance);
}
}
You are still blocking until you get the newline character, but you aren't doing it once per frame (and you know that the newline char is coming soon since you have data).
Another way is to use ReadExisting and read it into a buffer then parse the buffer (basically look for the newline in your case).
I've also seen a separate thread used that attempts to read a single character in a loop (which handles the timeout exception and uses code in the catch to mark when last data was received etc). That's basically the only way to do it if you have a protocol that has to react to individual characters quickly. Letting it throw exceptions is ugly though (there's a bit of overhead to an exception being thrown).
Anyhow, that should get you going. If you have any questions to clarify things don't hesitate to ask.
Oh, also make sure you have parity, stopbits data bits and handshake set correctly (sounds like the defaults are working for you though).
For completeness you should also handle the ErrorReceived event. Its delegate is a little different from the data one, so you'll need something like this:
stream.ErrorReceived += DataErrorReceivedHandler;
private void DataErrorReceivedHandler(object sender,
SerialErrorReceivedEventArgs e)
{
Debug.Log("Serial port error:" e.EventType.ToString ("G"));
}
@dubbreak, I tried your implementation and for some reason the DataReceivedHandler event is never fired. Is there any other setup details you may not have mentioned?
I checked on the Arduino IDE, and the values are definitely being read and sent properly, and in Unity if I put ReadLine in the Update method it reads fine.
So you are reading from the same stream that you attempted to attach the event from? The most likely issue is the stream isn't opened correctly.
Stream need to be class level, or it ends up out of scope. (e.g. if you are opening the stream every time in your update with the variable declared there it will work.. but if you have the variable for the stream declared in start it won't work).
Post your code that isn't working.
A hack to get you working in the meantime would be to do your read in the update as you are doing, but when you set up your stream set the timeout low:
streamReader.BaseStream.ReadTimeout = 2; //2ms timeout
Not sure what a sane value is (basically want it as high as possible without killing frame rate). BUT, you'd have to catch the timeout exception. It's poor practice and poor for performance to throw exceptions that often, but it beats blocking for the default timeout period. That suggestion is a hack though.
Anyhow post your code and I'll take a quick look to see if there is anything obvious.
So I did some more research, and it looks like in Unity using $$anonymous$$ono there is no proper implementation for SerialPort events, so the implementation won't work. Unless there's another workaround.
What version of Unity are you running? It is definitely available in $$anonymous$$ono 4 (adopted $$anonymous$$S code), I thought it was available in $$anonymous$$ono 3.X.. are you using Unity 3.X therefore stuck on $$anonymous$$ono 2?
A lot of ways to work around it. $$anonymous$$g. derived class
If you are on desktop spawning a thread would be ok. Do the blocking read (with a sane timeout, maybe 50ms or so), making sure to catch the exception. When you have enough data (i.e. parse it in thread into messages) fire an event with the message. The nice thing about doing it this way is you can have an event with a few arguments or an object, so it is something nice to deal with in your visualization code. Also it won't block the main game thread (screwing up your frame rate) and it won't introduce a race condition (like writing/reading from a shared object/string across threads).
I'm not sure if you need to marshal events in Unity to the correct thread. So that may be an issue. Easy to deal with though.
Unity 5.0 is still using $$anonymous$$ono 2. There's some weird licensing conflict between Unity and Xamarin, thus Unity stopped updating the $$anonymous$$ono libraries with Unity.
Answer by Melih Şimşek · Dec 13, 2013 at 10:38 AM
Also,
You can create a thread that deals with TimeException errors with Arduino. This works perfect, and any delay occured in run time.
Thread myThread;
void Start(){
myThread = new Thread(new ThreadStart(GetArduino));
myThread.Start();
}
private void GetArduino(){
while(myThread.IsAlive())
{
string value = stream.ReadLine();
}
}
That will work, however you don't have any code to catch the exception, so as it stands it will throw an uncaught error.
I mentioned this as a possible solution in my solution however as I previously stated, "Letting it throw exceptions is ugly though (there's a bit of overhead to an exception being thrown)." The overhead of throwing and exception is a lot higher than handling events. Generally you want an exception to happen only when something not expected happened.
Also with a thread you have contention issues. Threading is always a little tricky and can introduce some nasty bugs.
I did maintain some software that read this way on an embedded system (.net compact). It can be useful if a timeout is meaningful (no data in X amount of time, therefore message is complete or communication was interrupted).
Answer by nidhalkratos · Aug 09, 2014 at 06:19 PM
Hi, you can chek this code as it worked for me .http://youtu.be/aL4in this tutorial _h-n-rTg Hope this help. Regards nidhalkratos